summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrej Shadura <andrewsh@debian.org>2019-03-09 22:30:34 +0000
committerAndrej Shadura <andrewsh@debian.org>2019-03-09 22:30:34 +0000
commitb7b404fd7a726774d442562d11659d7b5368cdb9 (patch)
tree6f510ddbf80c1a51e333f80411541565ac71c9e9
parentace5fa7f57d49756c0e1b111a30f3b6a9436c1cb (diff)
Import Upstream version 0.5.5
-rw-r--r--src/META-INF/elki/de.lmu.ifi.dbs.elki.algorithm.AbstractDistanceBasedAlgorithm8
-rw-r--r--src/META-INF/elki/de.lmu.ifi.dbs.elki.algorithm.AbstractPrimitiveDistanceBasedAlgorithm3
-rw-r--r--src/META-INF/elki/de.lmu.ifi.dbs.elki.algorithm.Algorithm12
-rw-r--r--src/META-INF/elki/de.lmu.ifi.dbs.elki.algorithm.clustering.ClusteringAlgorithm4
-rw-r--r--src/META-INF/elki/de.lmu.ifi.dbs.elki.algorithm.outlier.OutlierAlgorithm6
-rw-r--r--src/META-INF/elki/de.lmu.ifi.dbs.elki.application.AbstractApplication12
-rw-r--r--src/META-INF/elki/de.lmu.ifi.dbs.elki.data.NumberVector11
-rw-r--r--src/META-INF/elki/de.lmu.ifi.dbs.elki.data.NumberVector$Factory7
-rw-r--r--src/META-INF/elki/de.lmu.ifi.dbs.elki.data.SparseNumberVector2
-rw-r--r--src/META-INF/elki/de.lmu.ifi.dbs.elki.data.SparseNumberVector$Factory2
-rw-r--r--src/META-INF/elki/de.lmu.ifi.dbs.elki.datasource.DatabaseConnection7
-rw-r--r--src/META-INF/elki/de.lmu.ifi.dbs.elki.datasource.filter.ObjectFilter19
-rw-r--r--src/META-INF/elki/de.lmu.ifi.dbs.elki.datasource.filter.StreamFilter13
-rw-r--r--src/META-INF/elki/de.lmu.ifi.dbs.elki.distance.distancefunction.NumberVectorDistanceFunction2
-rw-r--r--src/META-INF/elki/de.lmu.ifi.dbs.elki.distance.distancefunction.SpatialPrimitiveDistanceFunction3
-rw-r--r--src/META-INF/elki/de.lmu.ifi.dbs.elki.distance.distancefunction.SpatialPrimitiveDoubleDistanceFunction3
-rw-r--r--src/META-INF/elki/de.lmu.ifi.dbs.elki.math.dimensionsimilarity.DimensionSimilarity7
-rw-r--r--src/META-INF/elki/de.lmu.ifi.dbs.elki.math.linearalgebra.pca.CovarianceMatrixBuilder1
-rw-r--r--src/META-INF/elki/de.lmu.ifi.dbs.elki.math.statistics.KernelDensityFunction4
-rw-r--r--src/META-INF/elki/de.lmu.ifi.dbs.elki.math.statistics.distribution.Distribution11
-rw-r--r--src/META-INF/elki/de.lmu.ifi.dbs.elki.math.statistics.distribution.DistributionWithRandom9
-rw-r--r--src/META-INF/elki/de.lmu.ifi.dbs.elki.result.ResultHandler1
-rw-r--r--src/META-INF/elki/de.lmu.ifi.dbs.elki.utilities.ensemble.EnsembleVoting6
-rw-r--r--src/META-INF/elki/de.lmu.ifi.dbs.elki.visualization.visualizers.VisFactory96
-rw-r--r--src/de/lmu/ifi/dbs/elki/KDDTask.java6
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/APRIORI.java41
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/AbstractAlgorithm.java12
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/AbstractDistanceBasedAlgorithm.java7
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/AbstractPrimitiveDistanceBasedAlgorithm.java5
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/Algorithm.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/DependencyDerivator.java137
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/DummyAlgorithm.java9
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/KNNDistanceOrder.java24
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/KNNJoin.java201
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/MaterializeDistances.java16
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/NullAlgorithm.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/benchmark/KNNBenchmarkAlgorithm.java303
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/benchmark/RangeQueryBenchmarkAlgorithm.java357
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/benchmark/package-info.java30
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/clustering/AbstractProjectedClustering.java87
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/clustering/AbstractProjectedDBSCAN.java45
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/clustering/DBSCAN.java71
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/clustering/DeLiClu.java43
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/clustering/EM.java150
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/clustering/NaiveMeanShiftClustering.java279
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/clustering/OPTICS.java60
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/clustering/OPTICSXi.java38
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/clustering/SLINK.java851
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/clustering/SNNClustering.java52
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/CASH.java367
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/COPAC.java37
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/ERiC.java85
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/FourC.java8
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/HiCO.java99
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/LMCLUS.java200
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/ORCLUS.java212
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/cash/CASHInterval.java47
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/cash/CASHIntervalSplit.java19
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/cash/ParameterizationFunction.java (renamed from src/de/lmu/ifi/dbs/elki/data/ParameterizationFunction.java)171
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/clustering/gdbscan/CorePredicate.java5
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/clustering/gdbscan/EpsilonNeighborPredicate.java92
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/clustering/gdbscan/GeneralizedDBSCAN.java59
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/clustering/gdbscan/MinPtsCorePredicate.java45
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/clustering/gdbscan/NeighborPredicate.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/AbstractKMeans.java36
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/AbstractKMeansInitialization.java24
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/FirstKInitialMeans.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/KMeans.java8
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/KMeansInitialization.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/KMeansLloyd.java87
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/KMeansMacQueen.java85
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/KMeansPlusPlusInitialMeans.java19
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/KMediansLloyd.java67
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/KMedoidsEM.java71
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/KMedoidsPAM.java95
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/PAMInitialMeans.java14
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/RandomlyChosenInitialMeans.java13
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/RandomlyGeneratedInitialMeans.java23
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/clustering/package-info.java6
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/clustering/subspace/CLIQUE.java156
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/clustering/subspace/DiSH.java259
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/clustering/subspace/HiSC.java17
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/clustering/subspace/PROCLUS.java310
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/clustering/subspace/PreDeCon.java8
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/clustering/subspace/SUBCLU.java132
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/clustering/subspace/clique/CLIQUESubspace.java18
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/clustering/subspace/clique/CLIQUEUnit.java22
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/clustering/trivial/ByLabelClustering.java13
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/clustering/trivial/ByLabelHierarchicalClustering.java6
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/clustering/trivial/ByModelClustering.java9
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/clustering/trivial/TrivialAllInOne.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/clustering/trivial/TrivialAllNoise.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/ABOD.java296
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/ALOCI.java112
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/AbstractAggarwalYuOutlier.java93
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/AbstractDBOutlier.java10
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/AggarwalYuEvolutionary.java166
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/AggarwalYuNaive.java33
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/COP.java385
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/DBOutlierDetection.java28
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/DBOutlierScore.java8
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/EMOutlier.java8
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/GaussianModel.java18
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/GaussianUniformMixture.java26
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/HilOut.java117
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/INFLO.java113
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/KNNOutlier.java86
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/KNNWeightOutlier.java37
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/LDF.java342
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/LDOF.java33
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/LOCI.java49
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/LOF.java165
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/LoOP.java193
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/OPTICSOF.java22
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/OnlineLOF.java57
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/OutlierAlgorithm.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/ReferenceBasedOutlierDetection.java40
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/SimpleCOP.java236
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/SimpleKernelDensityLOF.java284
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/SimpleLOF.java249
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/meta/ExternalDoubleOutlierScore.java16
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/meta/FeatureBagging.java172
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/meta/HiCS.java307
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/meta/RescaleMetaOutlierAlgorithm.java6
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/meta/SimpleOutlierEnsemble.java222
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/meta/package-info.java5
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/package-info.java7
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/AbstractDistanceBasedSpatialOutlier.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/AbstractNeighborhoodOutlier.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/CTLuGLSBackwardSearchAlgorithm.java115
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/CTLuMeanMultipleAttributes.java31
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/CTLuMedianAlgorithm.java96
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/CTLuMedianMultipleAttributes.java33
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/CTLuMoranScatterplotOutlier.java31
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/CTLuRandomWalkEC.java182
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/CTLuScatterplotOutlier.java40
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/CTLuZTestOutlier.java35
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/SLOM.java39
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/SOF.java23
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/TrimmedMeanApproach.java66
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/neighborhood/AbstractPrecomputedNeighborhood.java9
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/neighborhood/ExtendedNeighborhood.java37
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/neighborhood/ExternalNeighborhood.java37
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/neighborhood/NeighborSetPredicate.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/neighborhood/PrecomputedKNearestNeighborNeighborhood.java21
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/neighborhood/weighted/LinearWeightedExtendedNeighborhood.java29
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/neighborhood/weighted/UnweightedNeighborhoodAdapter.java14
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/neighborhood/weighted/WeightedNeighborSetPredicate.java6
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/subspace/OUTRES.java94
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/subspace/OutRankS1.java27
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/subspace/SOD.java113
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/trivial/ByLabelOutlier.java6
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/trivial/TrivialAllOutlier.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/trivial/TrivialGeneratedOutlier.java12
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/trivial/TrivialNoOutlier.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/package-info.java5
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/statistics/AddSingleScale.java92
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/statistics/AveragePrecisionAtK.java70
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/statistics/DistanceStatisticsWithClasses.java302
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/statistics/EvaluateRankingQuality.java89
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/statistics/RankingQualityHistogram.java64
-rw-r--r--src/de/lmu/ifi/dbs/elki/application/AbstractApplication.java163
-rw-r--r--src/de/lmu/ifi/dbs/elki/application/ComputeSingleColorHistogram.java13
-rw-r--r--src/de/lmu/ifi/dbs/elki/application/ConvertToBundleApplication.java139
-rw-r--r--src/de/lmu/ifi/dbs/elki/application/ELKILauncher.java76
-rw-r--r--src/de/lmu/ifi/dbs/elki/application/GeneratorByModel.xsd8
-rw-r--r--src/de/lmu/ifi/dbs/elki/application/GeneratorXMLSpec.java23
-rw-r--r--src/de/lmu/ifi/dbs/elki/application/KDDCLIApplication.java7
-rw-r--r--src/de/lmu/ifi/dbs/elki/application/cache/CacheDoubleDistanceInOnDiskMatrix.java36
-rw-r--r--src/de/lmu/ifi/dbs/elki/application/cache/CacheFloatDistanceInOnDiskMatrix.java33
-rw-r--r--src/de/lmu/ifi/dbs/elki/application/geo/VisualizeGeodesicDistances.java268
-rw-r--r--src/de/lmu/ifi/dbs/elki/application/geo/package-info.java (renamed from src/de/lmu/ifi/dbs/elki/math/histograms/package-info.java)4
-rw-r--r--src/de/lmu/ifi/dbs/elki/application/greedyensemble/ComputeKNNOutlierScores.java47
-rw-r--r--src/de/lmu/ifi/dbs/elki/application/greedyensemble/GreedyEnsembleExperiment.java161
-rw-r--r--src/de/lmu/ifi/dbs/elki/application/greedyensemble/VisualizePairwiseGainMatrix.java103
-rw-r--r--src/de/lmu/ifi/dbs/elki/application/internal/CheckELKIServices.java83
-rw-r--r--src/de/lmu/ifi/dbs/elki/application/internal/CheckParameterizables.java22
-rw-r--r--src/de/lmu/ifi/dbs/elki/application/internal/DocumentParameters.java127
-rw-r--r--src/de/lmu/ifi/dbs/elki/application/internal/DocumentReferences.java6
-rw-r--r--src/de/lmu/ifi/dbs/elki/application/jsmap/JSONBuffer.java6
-rw-r--r--src/de/lmu/ifi/dbs/elki/application/jsmap/JSONResultHandler.java9
-rw-r--r--src/de/lmu/ifi/dbs/elki/application/jsmap/JSONWebServer.java40
-rw-r--r--src/de/lmu/ifi/dbs/elki/application/visualization/KNNExplorer.java42
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/AbstractNumberVector.java67
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/Arithmetic.java10
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/BitVector.java259
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/ClassLabel.java22
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/Cluster.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/DoubleVector.java286
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/FeatureVector.java45
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/FloatVector.java251
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/HierarchicalClassLabel.java48
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/HyperBoundingBox.java30
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/IntegerVector.java253
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/LabelList.java53
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/ModifiableHyperBoundingBox.java10
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/NumberVector.java60
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/OneDimensionalDoubleVector.java72
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/RationalNumber.java122
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/SimpleClassLabel.java59
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/SparseDoubleVector.java167
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/SparseFeatureVector.java7
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/SparseFloatVector.java189
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/SparseNumberVector.java35
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/Subspace.java26
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/VectorUtil.java172
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/images/ComputeHSBColorHistogram.java11
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/images/ComputeNaiveHSBColorHistogram.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/images/ComputeNaiveRGBColorHistogram.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/images/package-info.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/model/Bicluster.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/model/BiclusterWithInverted.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/model/ClusterModel.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/model/CorrelationAnalysisSolution.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/model/CorrelationModel.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/model/DendrogramModel.java9
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/model/EMModel.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/model/KMeansModel.java44
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/model/MeanModel.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/model/Model.java3
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/model/SubspaceModel.java11
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/package-info.java8
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/projection/FeatureSelection.java37
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/projection/NumericalFeatureSelection.java29
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/spatial/Polygon.java8
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/spatial/PolygonsObject.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/spatial/SpatialComparable.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/spatial/SpatialUtil.java103
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/synthetic/bymodel/GeneratorMain.java13
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/type/AlternativeTypeInformation.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/type/CombinedTypeInformation.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/type/NoSupportedDataTypeException.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/type/SimpleTypeInformation.java26
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/type/TypeInformationSerializer.java430
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/type/TypeUtil.java51
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/type/VectorFieldTypeInformation.java150
-rw-r--r--src/de/lmu/ifi/dbs/elki/data/type/VectorTypeInformation.java60
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/AbstractDatabase.java12
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/Database.java8
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/DatabaseEventManager.java39
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/HashmapDatabase.java99
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/ProxyDatabase.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/StaticArrayDatabase.java17
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/UpdatableDatabase.java12
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/datastore/DBIDDataStore.java (renamed from src/de/lmu/ifi/dbs/elki/database/ids/integer/TroveIteratorAdapter.java)46
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/datastore/DataStoreFactory.java20
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/datastore/DataStoreIDMap.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/datastore/DataStoreUtil.java47
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/datastore/DoubleDistanceDataStore.java (renamed from src/de/lmu/ifi/dbs/elki/database/datastore/RangeIDMap.java)32
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/datastore/WritableDBIDDataStore.java (renamed from src/de/lmu/ifi/dbs/elki/database/query/DistanceResultPair.java)45
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/datastore/WritableDoubleDistanceDataStore.java65
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/datastore/memory/ArrayDBIDStore.java125
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/datastore/memory/ArrayDoubleDistanceStore.java138
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/datastore/memory/ArrayDoubleStore.java14
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/datastore/memory/ArrayIntegerStore.java14
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/datastore/memory/ArrayRecordStore.java6
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/datastore/memory/ArrayStore.java11
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/datastore/memory/MapIntegerDBIDDBIDStore.java104
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/datastore/memory/MapIntegerDBIDDoubleDistanceStore.java111
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/datastore/memory/MapIntegerDBIDDoubleStore.java15
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/datastore/memory/MapIntegerDBIDIntegerStore.java15
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/datastore/memory/MapIntegerDBIDRecordStore.java19
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/datastore/memory/MapIntegerDBIDStore.java11
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/datastore/memory/MapRecordStore.java17
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/datastore/memory/MapStore.java10
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/datastore/memory/MemoryDataStoreFactory.java47
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/datastore/package-info.java3
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/ids/ArrayDBIDs.java14
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/ids/ArrayModifiableDBIDs.java22
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/ids/DBID.java43
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/ids/DBIDArrayIter.java34
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/ids/DBIDArrayMIter.java33
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/ids/DBIDFactory.java148
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/ids/DBIDIter.java16
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/ids/DBIDMIter.java11
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/ids/DBIDRange.java6
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/ids/DBIDRef.java45
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/ids/DBIDUtil.java355
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/ids/DBIDVar.java42
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/ids/DBIDs.java14
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/ids/DistanceDBIDPair.java52
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/ids/DoubleDBIDPair.java64
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/ids/DoubleDistanceDBIDPair.java53
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/ids/EmptyDBIDs.java52
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/ids/HashSetModifiableDBIDs.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/ids/ModifiableDBIDs.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/ids/generic/DBIDIterAdapter.java26
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/ids/generic/GenericArrayModifiableDBIDs.java139
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/ids/generic/GenericHashSetModifiableDBIDs.java130
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/ids/generic/MaskedDBIDs.java158
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/ids/generic/MergedDBIDs.java8
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/ids/generic/UnmodifiableArrayDBIDs.java91
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/ids/generic/UnmodifiableDBIDs.java64
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/ids/integer/DistanceIntegerDBIDPair.java95
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/ids/integer/DoubleDistanceIntegerDBIDPair.java103
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/ids/integer/IntArrayStaticDBIDs.java165
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/ids/integer/IntegerArrayStaticDBIDs.java161
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/ids/integer/IntegerDBID.java126
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/ids/integer/IntegerDBIDArrayIter.java34
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/ids/integer/IntegerDBIDArrayMIter.java34
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/ids/integer/IntegerDBIDArrayQuickSort.java250
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/ids/integer/IntegerDBIDIter.java34
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/ids/integer/IntegerDBIDMIter.java34
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/ids/integer/IntegerDBIDRange.java146
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/ids/integer/IntegerDBIDRef.java41
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/ids/integer/IntegerDBIDVar.java198
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/ids/integer/IntegerDBIDs.java35
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/ids/integer/IntegerDoubleDBIDPair.java83
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/ids/integer/ReusingDBIDFactory.java23
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/ids/integer/SimpleDBIDFactory.java82
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/ids/integer/TrivialDBIDFactory.java96
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/ids/integer/TroveArrayDBIDs.java100
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/ids/integer/TroveArrayModifiableDBIDs.java50
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/ids/integer/TroveArrayStaticDBIDs.java5
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/ids/integer/TroveHashSetModifiableDBIDs.java125
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/ids/integer/UnmodifiableIntegerArrayDBIDs.java160
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/ids/integer/UnmodifiableIntegerDBIDs.java119
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/ids/package-info.java17
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/package-info.java6
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/query/DoubleDistanceResultPair.java165
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/query/GenericDistanceResultPair.java131
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/query/distance/package-info.java1
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/query/knn/AbstractDistanceKNNQuery.java1
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/query/knn/KNNQuery.java16
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/query/knn/KNNUtil.java429
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/query/knn/LinearScanKNNQuery.java49
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/query/knn/LinearScanPrimitiveDistanceKNNQuery.java26
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/query/knn/LinearScanRawDoubleDistanceKNNQuery.java116
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/query/knn/PreprocessorKNNQuery.java18
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/query/knn/package-info.java3
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/query/package-info.java5
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/query/range/AbstractDistanceRangeQuery.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/query/range/LinearScanPrimitiveDistanceRangeQuery.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/query/range/LinearScanRangeQuery.java15
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/query/range/LinearScanRawDoubleDistanceRangeQuery.java19
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/query/range/RangeQuery.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/query/rknn/AbstractRKNNQuery.java6
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/query/rknn/LinearScanRKNNQuery.java47
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/query/rknn/PreprocessorRKNNQuery.java12
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/query/rknn/RKNNQuery.java10
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/relation/DBIDView.java5
-rw-r--r--src/de/lmu/ifi/dbs/elki/database/relation/RelationUtil.java105
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/AbstractDatabaseConnection.java30
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/ArrayAdapterDatabaseConnection.java6
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/BundleDatabaseConnection.java127
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/ConcatenateFilesDatabaseConnection.java54
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/DBIDRangeDatabaseConnection.java10
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/EmptyDatabaseConnection.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/ExternalIDJoinDatabaseConnection.java94
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/FileBasedDatabaseConnection.java6
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/GeneratorXMLDatabaseConnection.java397
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/InputStreamDatabaseConnection.java16
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/LabelJoinDatabaseConnection.java120
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/PresortedBlindJoinDatabaseConnection.java6
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/RandomDoubleVectorDatabaseConnection.java88
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/bundle/BundleReader.java168
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/bundle/BundleWriter.java169
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/bundle/MultipleObjectsBundle.java7
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/bundle/package-info.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/filter/AbstractConversionFilter.java13
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/filter/AbstractRandomFeatureSelectionFilter.java158
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/filter/AbstractStreamConversionFilter.java12
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/filter/AbstractVectorConversionFilter.java51
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/filter/AbstractVectorStreamConversionFilter.java51
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/filter/ByLabelFilter.java12
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/filter/ClassLabelFilter.java38
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/filter/DoubleVectorProjectionFilter.java85
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/filter/DoubleVectorRandomProjectionFilter.java87
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/filter/ExternalIDFilter.java30
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/filter/FilterUtil.java22
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/filter/FixedDBIDsFilter.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/filter/HistogramJitterFilter.java164
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/filter/NoMissingValuesFilter.java6
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/filter/RandomSamplingStreamFilter.java43
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/filter/ShuffleObjectsFilter.java47
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/filter/SortByLabelFilter.java28
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/filter/SparseNumberVectorProjectionFilter.java86
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/filter/SparseNumberVectorRandomProjectionFilter.java83
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/filter/SparseVectorFieldFilter.java10
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/filter/SplitNumberVectorFilter.java62
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/filter/StreamFilter.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/filter/normalization/AbstractNormalization.java12
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/filter/normalization/AbstractStreamNormalization.java10
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/filter/normalization/AttributeWiseErfNormalization.java8
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/filter/normalization/AttributeWiseMinMaxNormalization.java45
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/filter/normalization/AttributeWiseVarianceNormalization.java51
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/filter/normalization/InverseDocumentFrequencyNormalization.java20
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/filter/normalization/LengthNormalization.java18
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/filter/normalization/RankTieNormalization.java12
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/filter/normalization/TFIDFNormalization.java6
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/filter/package-info.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/filter/transform/GlobalPrincipalComponentAnalysisTransform.java116
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/filter/transform/NumberVectorFeatureSelectionFilter.java (renamed from src/de/lmu/ifi/dbs/elki/datasource/filter/AbstractFeatureSelectionFilter.java)296
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/filter/transform/NumberVectorRandomFeatureSelectionFilter.java174
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/parser/AbstractParser.java9
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/parser/ArffParser.java76
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/parser/BitVectorLabelParser.java7
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/parser/DoubleVectorLabelParser.java6
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/parser/FloatVectorLabelParser.java6
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/parser/NumberVectorLabelParser.java161
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/parser/Parser.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/parser/SimplePolygonParser.java21
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/parser/SparseBitVectorLabelParser.java14
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/parser/SparseFloatVectorLabelParser.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/parser/SparseNumberVectorLabelParser.java63
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/parser/TermFrequencyParser.java102
-rw-r--r--src/de/lmu/ifi/dbs/elki/datasource/parser/package-info.java5
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/DistanceUtil.java25
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/AbstractIndexBasedDistanceFunction.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/AbstractVectorDoubleDistanceFunction.java6
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/AbstractVectorDoubleDistanceNorm.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/ArcCosineDistanceFunction.java8
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/CanberraDistanceFunction.java10
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/CosineDistanceFunction.java8
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/EuclideanDistanceFunction.java28
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/FilteredLocalPCABasedDistanceFunction.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/IndexBasedDistanceFunction.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/JeffreyDivergenceDistanceFunction.java10
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/LPNormDistanceFunction.java21
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/LocallyWeightedDistanceFunction.java25
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/ManhattanDistanceFunction.java68
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/MaximumDistanceFunction.java12
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/MinKDistance.java14
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/MinimumDistanceFunction.java14
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/NumberVectorDistanceFunction.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/RandomStableDistanceFunction.java5
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/SharedNearestNeighborJaccardDistanceFunction.java3
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/SparseEuclideanDistanceFunction.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/SparseLPNormDistanceFunction.java15
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/SparseManhattanDistanceFunction.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/SparseMaximumDistanceFunction.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/SquaredEuclideanDistanceFunction.java24
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/WeightedDistanceFunction.java6
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/WeightedLPNormDistanceFunction.java8
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/WeightedSquaredEuclideanDistanceFunction.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/adapter/AbstractSimilarityAdapter.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/adapter/SimilarityAdapterArccos.java6
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/adapter/SimilarityAdapterLinear.java6
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/adapter/SimilarityAdapterLn.java6
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/colorhistogram/HSBHistogramQuadraticDistanceFunction.java14
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/colorhistogram/HistogramIntersectionDistanceFunction.java10
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/colorhistogram/RGBHistogramQuadraticDistanceFunction.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/correlation/ERiCDistanceFunction.java62
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/correlation/PCABasedCorrelationDistanceFunction.java47
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/correlation/PearsonCorrelationDistanceFunction.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/correlation/SquaredPearsonCorrelationDistanceFunction.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/correlation/WeightedPearsonCorrelationDistanceFunction.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/correlation/WeightedSquaredPearsonCorrelationDistanceFunction.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/external/DiskCacheBasedDoubleDistanceFunction.java11
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/external/DiskCacheBasedFloatDistanceFunction.java11
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/external/DistanceParser.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/external/DistanceParsingResult.java25
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/external/FileBasedDoubleDistanceFunction.java33
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/external/FileBasedFloatDistanceFunction.java18
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/external/NumberDistanceParser.java69
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/geo/DimensionSelectingLatLngDistanceFunction.java70
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/geo/LatLngDistanceFunction.java56
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/geo/LngLatDistanceFunction.java56
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/package-info.java7
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/subspace/AbstractDimensionsSelectingDoubleDistanceFunction.java25
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/subspace/AbstractPreferenceVectorBasedCorrelationDistanceFunction.java28
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/subspace/DiSHDistanceFunction.java32
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/subspace/DimensionSelectingDistanceFunction.java22
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/subspace/HiSCDistanceFunction.java34
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/subspace/LocalSubspaceDistanceFunction.java12
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/subspace/SubspaceEuclideanDistanceFunction.java24
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/subspace/SubspaceLPNormDistanceFunction.java37
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/subspace/SubspaceManhattanDistanceFunction.java25
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/timeseries/AbstractEditDistanceFunction.java24
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/timeseries/DTWDistanceFunction.java6
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/timeseries/EDRDistanceFunction.java45
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/timeseries/ERPDistanceFunction.java51
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancefunction/timeseries/LCSSDistanceFunction.java78
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/AbstractKNNHeap.java113
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/DistanceDBIDResult.java88
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/DistanceDBIDResultIter.java (renamed from src/de/lmu/ifi/dbs/elki/database/query/GenericDistanceDBIDList.java)47
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/DistanceDBIDResultUtil.java86
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/DoubleDistanceDBIDList.java190
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/DoubleDistanceDBIDResultIter.java61
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/DoubleDistanceKNNHeap.java191
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/DoubleDistanceKNNList.java238
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/GenericDistanceDBIDList.java163
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/GenericKNNHeap.java106
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/GenericKNNList.java (renamed from src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/KNNList.java)104
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/KNNHeap.java113
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/KNNResult.java (renamed from src/de/lmu/ifi/dbs/elki/database/query/knn/KNNResult.java)55
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/KNNUtil.java372
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/ModifiableDistanceDBIDResult.java48
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/package-info.java53
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancevalue/BitDistance.java50
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancevalue/CorrelationDistance.java25
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancevalue/DoubleDistance.java85
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancevalue/FloatDistance.java102
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancevalue/IntegerDistance.java55
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancevalue/NumberDistance.java67
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancevalue/PCACorrelationDistance.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancevalue/PreferenceVectorBasedCorrelationDistance.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancevalue/SubspaceDistance.java50
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/distancevalue/package-info.java5
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/similarityfunction/AbstractIndexBasedSimilarityFunction.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/similarityfunction/FractionalSharedNearestNeighborSimilarityFunction.java3
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/similarityfunction/InvertedDistanceSimilarityFunction.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/similarityfunction/SharedNearestNeighborSimilarityFunction.java3
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/similarityfunction/kernel/FooKernelFunction.java14
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/similarityfunction/kernel/KernelMatrix.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/similarityfunction/kernel/LinearKernelFunction.java6
-rw-r--r--src/de/lmu/ifi/dbs/elki/distance/similarityfunction/kernel/PolynomialKernelFunction.java21
-rw-r--r--src/de/lmu/ifi/dbs/elki/evaluation/AutomaticEvaluation.java24
-rw-r--r--src/de/lmu/ifi/dbs/elki/evaluation/NoAutomaticEvaluation.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/evaluation/clustering/ClusterContingencyTable.java8
-rw-r--r--src/de/lmu/ifi/dbs/elki/evaluation/clustering/EvaluateClustering.java18
-rw-r--r--src/de/lmu/ifi/dbs/elki/evaluation/clustering/PairCounting.java15
-rw-r--r--src/de/lmu/ifi/dbs/elki/evaluation/clustering/SetMatchingPurity.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/evaluation/clustering/pairsegments/Segment.java29
-rw-r--r--src/de/lmu/ifi/dbs/elki/evaluation/clustering/pairsegments/Segments.java26
-rw-r--r--src/de/lmu/ifi/dbs/elki/evaluation/histogram/ComputeOutlierHistogram.java128
-rw-r--r--src/de/lmu/ifi/dbs/elki/evaluation/index/IndexPurity.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/evaluation/index/IndexStatistics.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/evaluation/outlier/JudgeOutlierScores.java89
-rw-r--r--src/de/lmu/ifi/dbs/elki/evaluation/outlier/OutlierPrecisionAtKCurve.java17
-rw-r--r--src/de/lmu/ifi/dbs/elki/evaluation/outlier/OutlierPrecisionRecallCurve.java46
-rw-r--r--src/de/lmu/ifi/dbs/elki/evaluation/outlier/OutlierROCCurve.java14
-rw-r--r--src/de/lmu/ifi/dbs/elki/evaluation/outlier/OutlierSmROCCurve.java19
-rw-r--r--src/de/lmu/ifi/dbs/elki/evaluation/outlier/OutlierThresholdClustering.java10
-rw-r--r--src/de/lmu/ifi/dbs/elki/evaluation/roc/ROC.java45
-rw-r--r--src/de/lmu/ifi/dbs/elki/evaluation/similaritymatrix/ComputeSimilarityMatrixImage.java82
-rw-r--r--src/de/lmu/ifi/dbs/elki/gui/GUIUtil.java114
-rw-r--r--src/de/lmu/ifi/dbs/elki/gui/configurator/AbstractParameterConfigurator.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/gui/configurator/AbstractSingleParameterConfigurator.java18
-rw-r--r--src/de/lmu/ifi/dbs/elki/gui/configurator/ClassListParameterConfigurator.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/gui/configurator/ClassParameterConfigurator.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/gui/configurator/ConfiguratorPanel.java11
-rw-r--r--src/de/lmu/ifi/dbs/elki/gui/configurator/EnumParameterConfigurator.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/gui/configurator/FileParameterConfigurator.java39
-rw-r--r--src/de/lmu/ifi/dbs/elki/gui/configurator/FlagParameterConfigurator.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/gui/configurator/ParameterConfigurator.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/gui/configurator/TextParameterConfigurator.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/gui/configurator/package-info.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/gui/icons/StockIcon.java50
-rw-r--r--src/de/lmu/ifi/dbs/elki/gui/minigui/MiniGUI.java182
-rw-r--r--src/de/lmu/ifi/dbs/elki/gui/multistep/MultiStepGUI.java7
-rw-r--r--src/de/lmu/ifi/dbs/elki/gui/multistep/panels/AlgorithmTabPanel.java12
-rw-r--r--src/de/lmu/ifi/dbs/elki/gui/multistep/panels/EvaluationTabPanel.java44
-rw-r--r--src/de/lmu/ifi/dbs/elki/gui/multistep/panels/InputTabPanel.java10
-rw-r--r--src/de/lmu/ifi/dbs/elki/gui/multistep/panels/LoggingTabPanel.java20
-rw-r--r--src/de/lmu/ifi/dbs/elki/gui/multistep/panels/OutputTabPanel.java30
-rw-r--r--src/de/lmu/ifi/dbs/elki/gui/multistep/panels/ParameterTabPanel.java123
-rw-r--r--src/de/lmu/ifi/dbs/elki/gui/multistep/panels/SavedSettingsTabPanel.java6
-rw-r--r--src/de/lmu/ifi/dbs/elki/gui/util/DynamicParameters.java68
-rw-r--r--src/de/lmu/ifi/dbs/elki/gui/util/LogPane.java5
-rw-r--r--src/de/lmu/ifi/dbs/elki/gui/util/ParameterTable.java26
-rw-r--r--src/de/lmu/ifi/dbs/elki/gui/util/ParametersModel.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/gui/util/SavedSettingsFile.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/AbstractIndex.java6
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/AbstractRefiningIndex.java19
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/Index.java6
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/package-info.java5
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/preprocessed/AbstractPreprocessorIndex.java6
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/preprocessed/LocalProjectionIndex.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/preprocessed/knn/AbstractMaterializeKNNPreprocessor.java22
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/preprocessed/knn/KNNJoinMaterializeKNNPreprocessor.java12
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/preprocessed/knn/MaterializeKNNAndRKNNPreprocessor.java215
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/preprocessed/knn/MaterializeKNNPreprocessor.java90
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/preprocessed/knn/MetricalIndexApproximationMaterializeKNNPreprocessor.java21
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/preprocessed/knn/PartitionApproximationMaterializeKNNPreprocessor.java133
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/preprocessed/knn/RandomSampleKNNPreprocessor.java75
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/preprocessed/knn/SpatialApproximationMaterializeKNNPreprocessor.java25
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/preprocessed/localpca/AbstractFilteredPCAIndex.java24
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/preprocessed/localpca/FilteredLocalPCAIndex.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/preprocessed/localpca/KNNQueryFilteredPCAIndex.java32
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/preprocessed/localpca/RangeQueryFilteredPCAIndex.java28
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/preprocessed/preference/AbstractPreferenceVectorIndex.java8
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/preprocessed/preference/DiSHPreferenceVectorIndex.java196
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/preprocessed/preference/HiSCPreferenceVectorIndex.java101
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/preprocessed/preference/PreferenceVectorIndex.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/preprocessed/snn/SharedNearestNeighborPreprocessor.java18
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/preprocessed/subspaceproj/AbstractSubspaceProjectionIndex.java25
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/preprocessed/subspaceproj/FourCSubspaceIndex.java49
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/preprocessed/subspaceproj/PreDeConSubspaceIndex.java85
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/preprocessed/subspaceproj/SubspaceProjectionIndex.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/AbstractLeafEntry.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/AbstractNode.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/IndexTree.java11
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/IndexTreePath.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/TreeIndexFactory.java41
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/AbstractMTree.java339
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/AbstractMTreeFactory.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/MTreeDirectoryEntry.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/AbstractMkTree.java44
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/AbstractMkTreeUnified.java25
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/AbstractMkTreeUnifiedFactory.java7
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/MkAppTree.java181
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/MkAppTreeFactory.java26
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/MkAppTreeIndex.java15
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/MkAppTreeNode.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/ConvexHull.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/MkCoPTree.java216
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/MkCoPTreeIndex.java15
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/MkCoPTreeNode.java12
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/MkCopTreeFactory.java7
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkmax/MkMaxTree.java80
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkmax/MkMaxTreeIndex.java37
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mktab/MkTabTree.java33
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mktab/MkTabTreeIndex.java19
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mtree/MTree.java14
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mtree/MTreeFactory.java6
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mtree/MTreeIndex.java17
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/DoubleDistanceMetricalIndexKNNQuery.java155
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/DoubleDistanceMetricalIndexRangeQuery.java135
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/MTreeQueryUtil.java90
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/MetricalIndexKNNQuery.java90
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/MetricalIndexRangeQuery.java105
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/MkTreeRKNNQuery.java8
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/query/DoubleDistanceSearchCandidate.java5
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/query/DoubleMTreeDistanceSearchCandidate.java62
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/query/GenericMTreeDistanceSearchCandidate.java9
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/spatial/SpatialPointLeafEntry.java19
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/AbstractRStarTree.java46
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/AbstractRStarTreeFactory.java32
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/AbstractRStarTreeNode.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/NonFlatRStarTree.java10
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/deliclu/DeLiCluLeafEntry.java24
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/deliclu/DeLiCluTree.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/deliclu/DeLiCluTreeFactory.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/deliclu/DeLiCluTreeIndex.java51
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/query/DoubleDistanceRStarTreeKNNQuery.java59
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/query/DoubleDistanceRStarTreeRangeQuery.java17
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/query/GenericRStarTreeKNNQuery.java23
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/query/GenericRStarTreeRangeQuery.java15
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/rstar/RStarTree.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/rstar/RStarTreeFactory.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/rstar/RStarTreeIndex.java23
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/bulk/MaxExtensionBulkSplit.java30
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/bulk/OneDimSortBulkSplit.java6
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/bulk/SortTileRecursiveBulkSplit.java9
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/bulk/SpatialSortBulkSplit.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/insert/ApproximativeLeastOverlapInsertionStrategy.java5
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/insert/CombinedInsertionStrategy.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/overflow/LimitedReinsertOverflowTreatment.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/reinsert/AbstractPartialReinsert.java17
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/split/AngTanLinearSplit.java18
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/split/GreeneSplit.java67
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/split/RTreeLinearSplit.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/split/TopologicalSplitter.java29
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/vafile/DAFile.java23
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/vafile/PartialVAFile.java153
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/vafile/VAFile.java95
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/vafile/VALPNormDistance.java24
-rw-r--r--src/de/lmu/ifi/dbs/elki/index/vafile/VectorApproximation.java12
-rw-r--r--src/de/lmu/ifi/dbs/elki/logging/CLISmartHandler.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/logging/ELKILogRecord.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/logging/ErrorFormatter.java58
-rw-r--r--src/de/lmu/ifi/dbs/elki/logging/Logging.java41
-rw-r--r--src/de/lmu/ifi/dbs/elki/logging/LoggingConfiguration.java6
-rw-r--r--src/de/lmu/ifi/dbs/elki/logging/LoggingUtil.java18
-rw-r--r--src/de/lmu/ifi/dbs/elki/logging/progress/AbstractProgress.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/logging/progress/FiniteProgress.java18
-rw-r--r--src/de/lmu/ifi/dbs/elki/logging/progress/IndefiniteProgress.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/logging/progress/Progress.java7
-rw-r--r--src/de/lmu/ifi/dbs/elki/logging/progress/ProgressTracker.java3
-rw-r--r--src/de/lmu/ifi/dbs/elki/logging/progress/StepProgress.java6
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/DoubleMinMax.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/GeoUtil.java670
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/IntegerMinMax.java176
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/MathUtil.java285
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/MeanVarianceMinMax.java9
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/Primes.java148
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/SinCosTable.java308
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/dimensionsimilarity/CovarianceDimensionSimilarity.java82
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/dimensionsimilarity/DimensionSimilarity.java46
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/dimensionsimilarity/DimensionSimilarityMatrix.java247
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/dimensionsimilarity/HSMDimensionSimilarity.java251
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/dimensionsimilarity/HiCSDimensionSimilarity.java269
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/dimensionsimilarity/MCEDimensionSimilarity.java288
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/dimensionsimilarity/SURFINGDimensionSimilarity.java133
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/dimensionsimilarity/SlopeDimensionSimilarity.java142
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/dimensionsimilarity/SlopeInversionDimensionSimilarity.java158
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/dimensionsimilarity/package-info.java26
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/geometry/AlphaShape.java10
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/geometry/PrimsMinimumSpanningTree.java177
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/geometry/SweepHullDelaunay2D.java18
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/geometry/XYCurve.java14
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/histograms/AggregatingHistogram.java254
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/histograms/FlexiHistogram.java487
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/histograms/ReplacingHistogram.java452
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/linearalgebra/AffineTransformation.java120
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/linearalgebra/Centroid.java60
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/linearalgebra/CholeskyDecomposition.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/linearalgebra/CovarianceMatrix.java67
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/linearalgebra/EigenvalueDecomposition.java362
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/linearalgebra/LUDecomposition.java84
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/linearalgebra/LinearEquationSystem.java205
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/linearalgebra/Matrix.java506
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/linearalgebra/ProjectedCentroid.java40
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/linearalgebra/ProjectionResult.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/linearalgebra/SingularValueDecomposition.java10
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/linearalgebra/SortedEigenPairs.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/linearalgebra/VMath.java473
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/linearalgebra/Vector.java238
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/linearalgebra/fitting/FittingFunction.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/linearalgebra/fitting/LevenbergMarquardtMethod.java14
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/AbstractCovarianceMatrixBuilder.java18
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/CompositeEigenPairFilter.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/CovarianceMatrixBuilder.java24
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/DropEigenPairFilter.java19
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/EigenPairFilter.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/FilteredEigenPairs.java6
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/FirstNEigenPairFilter.java17
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/LimitEigenPairFilter.java59
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/NormalizingEigenPairFilter.java8
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/PCAFilteredAutotuningRunner.java155
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/PCAFilteredRunner.java63
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/PCARunner.java24
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/PercentageEigenPairFilter.java41
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/ProgressiveEigenPairFilter.java32
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/RANSACCovarianceMatrixBuilder.java190
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/RelativeEigenPairFilter.java17
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/SignificantEigenPairFilter.java19
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/StandardCovarianceMatrixBuilder.java6
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/WeakEigenPairFilter.java20
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/WeightedCovarianceMatrixBuilder.java61
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/weightfunctions/GaussStddevWeight.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/scales/Scales.java43
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/spacefillingcurves/AbstractSpatialSorter.java44
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/spacefillingcurves/BinarySplitSpatialSorter.java6
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/spacefillingcurves/HilbertSpatialSorter.java63
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/spacefillingcurves/PeanoSpatialSorter.java11
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/spacefillingcurves/SpatialSorter.java6
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/spacefillingcurves/ZCurveSpatialSorter.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/spacefillingcurves/ZCurveTransformer.java23
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/statistics/EpanechnikovKernelDensityFunction.java15
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/statistics/GaussianKernelDensityFunction.java15
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/statistics/KernelDensityEstimator.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/statistics/MultipleLinearRegression.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/statistics/StudentDistribution.java189
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/statistics/TriangularKernelDensityFunction.java15
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/statistics/UniformKernelDensityFunction.java15
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/statistics/distribution/ChiDistribution.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/statistics/distribution/ChiSquaredDistribution.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/statistics/distribution/Distribution.java10
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/statistics/distribution/DistributionWithRandom.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/statistics/distribution/ExponentialDistribution.java126
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/statistics/distribution/HaltonUniformDistribution.java313
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/statistics/distribution/NormalDistribution.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/statistics/distribution/PoissonDistribution.java14
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/statistics/distribution/StudentsTDistribution.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/math/statistics/tests/GoodnessOfFitTest.java6
-rw-r--r--src/de/lmu/ifi/dbs/elki/persistent/ByteArrayUtil.java233
-rw-r--r--src/de/lmu/ifi/dbs/elki/persistent/ByteBufferSerializer.java19
-rw-r--r--src/de/lmu/ifi/dbs/elki/persistent/LRUCache.java22
-rw-r--r--src/de/lmu/ifi/dbs/elki/persistent/OnDiskArray.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/persistent/OnDiskArrayPageFile.java6
-rw-r--r--src/de/lmu/ifi/dbs/elki/persistent/OnDiskUpperTriangleMatrix.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/persistent/PageFileUtil.java9
-rw-r--r--src/de/lmu/ifi/dbs/elki/persistent/PersistentPageFile.java6
-rw-r--r--src/de/lmu/ifi/dbs/elki/result/KMLOutputHandler.java33
-rw-r--r--src/de/lmu/ifi/dbs/elki/result/LogResultStructureResultHandler.java94
-rw-r--r--src/de/lmu/ifi/dbs/elki/result/OrderingFromDataStore.java10
-rw-r--r--src/de/lmu/ifi/dbs/elki/result/ResultHierarchy.java14
-rw-r--r--src/de/lmu/ifi/dbs/elki/result/ResultUtil.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/result/ResultWriter.java10
-rw-r--r--src/de/lmu/ifi/dbs/elki/result/ScalesResult.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/result/SettingsResult.java15
-rw-r--r--src/de/lmu/ifi/dbs/elki/result/optics/DoubleDistanceClusterOrderEntry.java5
-rw-r--r--src/de/lmu/ifi/dbs/elki/result/optics/GenericClusterOrderEntry.java5
-rw-r--r--src/de/lmu/ifi/dbs/elki/result/outlier/OrderingFromRelation.java6
-rw-r--r--src/de/lmu/ifi/dbs/elki/result/textwriter/MultipleFilesOutput.java14
-rw-r--r--src/de/lmu/ifi/dbs/elki/result/textwriter/TextWriter.java15
-rw-r--r--src/de/lmu/ifi/dbs/elki/result/textwriter/TextWriterStream.java12
-rw-r--r--src/de/lmu/ifi/dbs/elki/result/textwriter/naming/SimpleEnumeratingScheme.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/result/textwriter/writers/TextWriterObjectArray.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/Base64.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/BitsUtil.java330
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/ClassGenericsUtil.java111
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/ConstantObject.java164
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/DatabaseUtil.java297
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/ELKIServiceLoader.java9
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/FileUtil.java15
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/FormatUtil.java299
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/InspectionUtil.java8
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/RandomFactory.java89
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/Util.java140
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/QuickSelect.java526
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/arraylike/ArrayLikeUtil.java33
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/arraylike/DoubleArrayAdapter.java3
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/arraylike/ExtendedArray.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/arraylike/FeatureVectorAdapter.java8
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/arraylike/FloatArrayAdapter.java3
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/arraylike/ListArrayAdapter.java22
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/arraylike/NumberVectorAdapter.java37
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/arraylike/SubsetArrayAdapter.java6
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/arraylike/TDoubleListAdapter.java3
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/arrays/IntegerArrayQuickSort.java225
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/arrays/IntegerComparator.java40
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/arrays/package-info.java26
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/AbstractHeap.java103
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/ComparableMaxHeap.java63
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/ComparableMinHeap.java63
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/DoubleHeap.java264
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/DoubleMaxHeap.java62
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/DoubleMinHeap.java62
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/DoubleObjMaxHeap.java328
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/DoubleObjMinHeap.java328
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/DoublePriorityObject.java7
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/Heap.java264
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/IntegerHeap.java264
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/IntegerMaxHeap.java62
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/IntegerMinHeap.java62
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/IntegerPriorityObject.java7
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/KNNHeap.java153
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/ObjectHeap.java267
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/TiedTopBoundedHeap.java47
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/TiedTopBoundedUpdatableHeap.java19
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/TopBoundedHeap.java58
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/TopBoundedUpdatableHeap.java39
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/UpdatableHeap.java51
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/hierarchy/HierarchyHashmapList.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/AbstractObjDynamicHistogram.java271
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/AbstractObjStaticHistogram.java129
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/AbstractStaticHistogram.java220
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/DoubleArrayStaticHistogram.java85
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/DoubleDynamicHistogram.java248
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/DoubleHistogram.java58
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/DoubleStaticHistogram.java132
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/FloatDynamicHistogram.java248
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/FloatHistogram.java58
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/FloatStaticHistogram.java132
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/Histogram.java105
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/IntArrayStaticHistogram.java85
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/IntDynamicHistogram.java248
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/IntHistogram.java58
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/IntStaticHistogram.java132
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/LongArrayStaticHistogram.java85
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/LongDynamicHistogram.java248
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/LongHistogram.java58
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/LongStaticHistogram.java132
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/MeanVarianceStaticHistogram.java80
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/ObjHistogram.java69
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/ShortDynamicHistogram.java248
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/ShortHistogram.java58
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/ShortStaticHistogram.java132
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/package-info.java34
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/documentation/DocumentationUtil.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/ensemble/EnsembleVoting.java42
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/ensemble/EnsembleVotingBayes.java106
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/ensemble/EnsembleVotingMax.java48
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/ensemble/EnsembleVotingMean.java48
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/ensemble/EnsembleVotingMedian.java95
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/ensemble/EnsembleVotingMin.java49
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/ensemble/EnsembleVotingRestrictedBayes.java127
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/ensemble/package-info.java26
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/iterator/AbstractFilteredIterator.java6
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/iterator/ArrayIter.java59
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/iterator/EmptyIterator.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/iterator/Iter.java5
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/iterator/MIter.java (renamed from src/de/lmu/ifi/dbs/elki/database/query/DistanceDBIDResult.java)20
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/AbstractParameterizer.java10
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/InternalParameterizationErrors.java3
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/NoParameterValueException.java1
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/OptionID.java50
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/OptionUtil.java51
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/Parameterizable.java9
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/UnspecifiedParameterException.java28
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/WrongParameterValueException.java22
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/AllOrNoneMustBeSetGlobalConstraint.java16
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/EqualStringConstraint.java8
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/GlobalListSizeConstraint.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/GlobalVectorListElementSizeConstraint.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/GreaterConstraint.java85
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/GreaterEqualConstraint.java87
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/IntervalConstraint.java53
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/LessConstraint.java83
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/LessEqualConstraint.java81
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/ListEachConstraint.java90
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/ListGreaterEqualConstraint.java74
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/ListParameterNoDuplicateValueConstraint.java78
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/ListSizeConstraint.java6
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/NoDuplicateValueGlobalConstraint.java9
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/OneMustBeSetGlobalConstraint.java10
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/OnlyOneIsAllowedToBeSetGlobalConstraint.java13
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/ParameterFlagGlobalConstraint.java31
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/package-info.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameterization/AbstractParameterization.java36
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameterization/ChainedParameterization.java10
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameterization/EmptyParameterization.java3
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameterization/ListParameterization.java12
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameterization/MergedParameterization.java15
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameterization/Parameterization.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameterization/SerializedParameterization.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameterization/TrackParameters.java33
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameterization/UnParameterization.java8
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/AbstractParameter.java353
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/ClassListParameter.java12
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/ClassParameter.java76
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/DistanceParameter.java171
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/DoubleListParameter.java71
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/DoubleParameter.java123
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/EnumParameter.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/FileListParameter.java5
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/FileParameter.java9
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/Flag.java24
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/IntListParameter.java96
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/IntParameter.java83
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/ListParameter.java96
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/LongParameter.java91
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/NumberParameter.java75
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/ObjectListParameter.java5
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/ObjectParameter.java5
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/Parameter.java378
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/PatternParameter.java110
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/RandomParameter.java147
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/StringParameter.java79
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/VectorListParameter.java55
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/package-info.java6
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/pairs/CTriple.java14
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/pairs/DoubleDoublePair.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/pairs/DoubleIntPair.java6
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/pairs/DoubleObjPair.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/pairs/IntDoublePair.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/pairs/IntIntPair.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/pairs/PairUtil.java8
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/referencepoints/AxisBasedReferencePoints.java39
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/referencepoints/FullDatabaseReferencePoints.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/referencepoints/GridBasedReferencePoints.java50
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/referencepoints/RandomGeneratedReferencePoints.java37
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/referencepoints/RandomSampleReferencePoints.java23
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/referencepoints/StarBasedReferencePoints.java46
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/scaling/ClipScaling.java10
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/scaling/GammaScaling.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/HeDESNormalizationOutlierScaling.java8
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/MinusLogGammaScaling.java13
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/MinusLogStandardDeviationScaling.java23
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/MixtureModelOutlierScalingFunction.java20
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/MultiplicativeInverseScaling.java8
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/OutlierGammaScaling.java10
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/OutlierLinearScaling.java30
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/OutlierMinusLogScaling.java8
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/OutlierSqrtScaling.java18
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/RankingPseudoOutlierScaling.java12
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/SigmoidOutlierScalingFunction.java17
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/SqrtStandardDeviationScaling.java32
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/StandardDeviationScaling.java30
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/TopKOutlierScaling.java13
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/xml/HTMLUtil.java3
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/ExportVisualizations.java88
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/VisualizationTask.java107
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/VisualizerContext.java101
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/VisualizerParameterizer.java110
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/batikutil/CSSHoverClass.java14
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/batikutil/CloneInlineImages.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/batikutil/JSVGUpdateSynchronizer.java3
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/batikutil/ThumbnailRegistryEntry.java22
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/colors/ColorLibrary.java19
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/css/CSSClass.java12
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/css/CSSClassManager.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/gui/ResultVisualizer.java17
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/gui/ResultWindow.java11
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/gui/SelectionTableWindow.java12
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/gui/detail/DetailView.java15
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/gui/overview/OverviewPlot.java163
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/gui/overview/RectangleArranger.java99
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/gui/overview/package-info.java3
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/opticsplot/OPTICSPlot.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/package-info.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/projections/AbstractFullProjection.java20
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/projections/AffineProjection.java32
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/projections/FullProjection.java33
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/projections/OPTICSProjection.java94
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/projections/Projection1D.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/projections/Projection2D.java12
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/projections/ProjectionParallel.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/projections/Simple1D.java10
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/projections/Simple2D.java20
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/projections/SimpleParallel.java15
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/projector/HistogramFactory.java15
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/projector/HistogramProjector.java18
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/projector/OPTICSProjector.java3
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/projector/ParallelPlotFactory.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/projector/ParallelPlotProjector.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/projector/ScatterPlotFactory.java17
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/projector/ScatterPlotProjector.java48
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/savedialog/SVGSaveDialog.java79
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/savedialog/SaveOptionsPanel.java7
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/style/ClusterStylingPolicy.java9
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/style/PropertiesBasedStyleLibrary.java104
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/style/StyleResult.java26
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/style/lines/DashedLineStyleLibrary.java14
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/style/lines/LineStyleLibrary.java6
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/style/lines/SolidLineStyleLibrary.java8
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/style/marker/CircleMarkers.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/style/marker/MinimalMarkers.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/style/marker/PrettyMarkers.java8
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/style/presentation.properties4
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/svg/SVGArrow.java114
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/svg/SVGHyperCube.java20
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/svg/SVGHyperSphere.java10
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/svg/SVGPath.java22
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/svg/SVGPlot.java5
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/svg/SVGScoreBar.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/svg/SVGSimpleLinearAxis.java6
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/svg/SVGUtil.java84
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/svg/VoronoiDraw.java3
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/AbstractVisFactory.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/AbstractVisualization.java1
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/StaticVisualizationInstance.java (renamed from src/de/lmu/ifi/dbs/elki/visualization/visualizers/StaticVisualization.java)4
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/VisualizerUtil.java133
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/histogram/ColoredHistogramVisualizer.java557
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/AbstractOPTICSVisualization.java12
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSClusterVisualization.java229
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSPlotCutVisualization.java395
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSPlotSelectionVisualization.java489
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSPlotVisualizer.java114
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSSteepAreaVisualization.java233
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/package-info.java5
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/pairsegments/CircleSegmentsVisualizer.java1062
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/AbstractParallelVisualization.java7
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/AxisReorderVisualization.java296
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/AxisVisibilityVisualization.java293
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/LineVisualization.java245
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/ParallelAxisVisualization.java214
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/cluster/ClusterOutlineVisualization.java297
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/cluster/ClusterParallelMeanVisualization.java226
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/index/RTreeParallelVisualization.java278
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionAxisRangeVisualization.java180
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionLineVisualization.java148
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionToolAxisRangeVisualization.java387
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionToolLineVisualization.java423
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/AbstractScatterplotVisualization.java6
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/AbstractTooltipVisualization.java30
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/AxisVisualization.java175
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/MarkerVisualization.java167
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/PolygonVisualization.java170
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/ReferencePointsVisualization.java127
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/ToolBox2DVisualization.java393
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/TooltipScoreVisualization.java281
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/TooltipStringVisualization.java243
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/ClusterHullVisualization.java306
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/ClusterMeanVisualization.java345
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/ClusterOrderVisualization.java126
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/EMClusterVisualization.java659
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/VoronoiVisualization.java358
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/density/DensityEstimationOverlay.java298
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/index/TreeMBRVisualization.java258
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/index/TreeSphereVisualization.java333
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/outlier/BubbleVisualization.java374
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/outlier/COPVectorVisualization.java190
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/DistanceFunctionVisualization.java370
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/MoveObjectsToolVisualization.java270
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionConvexHullVisualization.java161
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionCubeVisualization.java293
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionDotVisualization.java143
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionToolCubeVisualization.java373
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionToolDotVisualization.java330
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/thumbs/ThumbnailVisualization.java42
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/ClusterEvaluationVisualization.java (renamed from src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/ClusterEvaluationVisFactory.java)15
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/HistogramVisualization.java (renamed from src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/HistogramVisFactory.java)78
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/KeyVisualization.java249
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/LabelVisualization.java (renamed from src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/LabelVisFactory.java)20
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/PixmapVisualizer.java136
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/SettingsVisualization.java (renamed from src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/SettingsVisFactory.java)18
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/SimilarityMatrixVisualizer.java184
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/XYCurveVisualization.java (renamed from src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/XYCurveVisFactory.java)28
-rw-r--r--src/de/lmu/ifi/dbs/elki/workflow/AlgorithmStep.java14
-rw-r--r--src/de/lmu/ifi/dbs/elki/workflow/LoggingStep.java7
-rw-r--r--src/tutorial/clustering/SameSizeKMeansAlgorithm.java519
-rw-r--r--src/tutorial/clustering/package-info.java26
-rw-r--r--src/tutorial/distancefunction/MultiLPNorm.java14
-rw-r--r--src/tutorial/distancefunction/TutorialDistanceFunction.java10
-rw-r--r--src/tutorial/outlier/DistanceStddevOutlier.java24
-rw-r--r--test/de/lmu/ifi/dbs/elki/algorithm/AbstractSimpleAlgorithmTest.java2
-rw-r--r--test/de/lmu/ifi/dbs/elki/algorithm/TestKNNJoin.java28
-rw-r--r--test/de/lmu/ifi/dbs/elki/algorithm/clustering/TestSLINKResults.java2
-rw-r--r--test/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/TestCASHResults.java21
-rw-r--r--test/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/TestORCLUSResults.java12
-rw-r--r--test/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/TestKMeansResults.java12
-rw-r--r--test/de/lmu/ifi/dbs/elki/algorithm/clustering/subspace/TestPROCLUSResults.java20
-rw-r--r--test/de/lmu/ifi/dbs/elki/algorithm/clustering/subspace/TestPreDeConResults.java4
-rw-r--r--test/de/lmu/ifi/dbs/elki/algorithm/outlier/TestAggarwalYuEvolutionary.java8
-rw-r--r--test/de/lmu/ifi/dbs/elki/algorithm/outlier/TestAggarwalYuNaive.java4
-rw-r--r--test/de/lmu/ifi/dbs/elki/algorithm/outlier/TestOnlineLOF.java11
-rw-r--r--test/de/lmu/ifi/dbs/elki/database/TestRelationSorting.java91
-rw-r--r--test/de/lmu/ifi/dbs/elki/distance/distancefunction/SpatialPrimitiveDistanceFunctionTest.java10
-rw-r--r--test/de/lmu/ifi/dbs/elki/index/TestIndexStructures.java44
-rw-r--r--test/de/lmu/ifi/dbs/elki/index/preprocessed/TestMaterializedKNNAndRKNNPreprocessor.java55
-rw-r--r--test/de/lmu/ifi/dbs/elki/math/TestKernelDensityFitting.java4
-rw-r--r--test/de/lmu/ifi/dbs/elki/math/TestMathUtil.java6
-rw-r--r--test/de/lmu/ifi/dbs/elki/math/TestSinCosTable.java50
-rw-r--r--test/de/lmu/ifi/dbs/elki/math/geometry/TestPrimsMinimumSpanningTree.java76
-rw-r--r--test/de/lmu/ifi/dbs/elki/math/histograms/TestFlexiHistogram.java90
-rw-r--r--test/de/lmu/ifi/dbs/elki/math/statistics/distribution/TestExponentialDistribution.java826
-rw-r--r--test/de/lmu/ifi/dbs/elki/utilities/TestBitsUtil.java62
-rw-r--r--test/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/TestDoubleHeap.java91
-rw-r--r--test/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/TestDoubleObjHeaps.java91
-rw-r--r--test/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/TestHeapPerformance.java18
-rw-r--r--test/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/TestTiedTopBoundedUpdatableHeap.java2
-rw-r--r--test/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/TestDoubleHistogram.java (renamed from test/de/lmu/ifi/dbs/elki/math/histograms/TestReplacingHistogram.java)51
-rw-r--r--test/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/TestFlexiHistogram.java159
1097 files changed, 47973 insertions, 27217 deletions
diff --git a/src/META-INF/elki/de.lmu.ifi.dbs.elki.algorithm.AbstractDistanceBasedAlgorithm b/src/META-INF/elki/de.lmu.ifi.dbs.elki.algorithm.AbstractDistanceBasedAlgorithm
index 69a3aaed..217954ca 100644
--- a/src/META-INF/elki/de.lmu.ifi.dbs.elki.algorithm.AbstractDistanceBasedAlgorithm
+++ b/src/META-INF/elki/de.lmu.ifi.dbs.elki.algorithm.AbstractDistanceBasedAlgorithm
@@ -2,6 +2,7 @@ de.lmu.ifi.dbs.elki.algorithm.KNNDistanceOrder
de.lmu.ifi.dbs.elki.algorithm.KNNJoin
de.lmu.ifi.dbs.elki.algorithm.MaterializeDistances
de.lmu.ifi.dbs.elki.algorithm.clustering.DBSCAN
+de.lmu.ifi.dbs.elki.algorithm.clustering.NaiveMeanShiftClustering
de.lmu.ifi.dbs.elki.algorithm.clustering.OPTICS
de.lmu.ifi.dbs.elki.algorithm.clustering.DeLiClu
de.lmu.ifi.dbs.elki.algorithm.clustering.SLINK
@@ -10,19 +11,26 @@ de.lmu.ifi.dbs.elki.algorithm.clustering.kmeans.KMedoidsEM
de.lmu.ifi.dbs.elki.algorithm.clustering.correlation.HiCO
de.lmu.ifi.dbs.elki.algorithm.clustering.subspace.HiSC
de.lmu.ifi.dbs.elki.algorithm.outlier.ABOD
+de.lmu.ifi.dbs.elki.algorithm.outlier.COP
de.lmu.ifi.dbs.elki.algorithm.outlier.DBOutlierDetection
de.lmu.ifi.dbs.elki.algorithm.outlier.DBOutlierScore
de.lmu.ifi.dbs.elki.algorithm.outlier.HilOut
de.lmu.ifi.dbs.elki.algorithm.outlier.INFLO
de.lmu.ifi.dbs.elki.algorithm.outlier.KNNOutlier
de.lmu.ifi.dbs.elki.algorithm.outlier.KNNWeightOutlier
+de.lmu.ifi.dbs.elki.algorithm.outlier.LDF
de.lmu.ifi.dbs.elki.algorithm.outlier.LDOF
de.lmu.ifi.dbs.elki.algorithm.outlier.LOCI
de.lmu.ifi.dbs.elki.algorithm.outlier.OPTICSOF
+de.lmu.ifi.dbs.elki.algorithm.outlier.SimpleLOF
+de.lmu.ifi.dbs.elki.algorithm.outlier.SimpleKernelDensityLOF
+de.lmu.ifi.dbs.elki.algorithm.outlier.SimpleCOP
de.lmu.ifi.dbs.elki.algorithm.outlier.spatial.CTLuGLSBackwardSearchAlgorithm
de.lmu.ifi.dbs.elki.algorithm.outlier.spatial.CTLuRandomWalkEC
de.lmu.ifi.dbs.elki.algorithm.statistics.AveragePrecisionAtK
de.lmu.ifi.dbs.elki.algorithm.statistics.RankingQualityHistogram
de.lmu.ifi.dbs.elki.algorithm.statistics.DistanceStatisticsWithClasses
de.lmu.ifi.dbs.elki.algorithm.statistics.EvaluateRankingQuality
+de.lmu.ifi.dbs.elki.algorithm.benchmark.KNNBenchmarkAlgorithm
+de.lmu.ifi.dbs.elki.algorithm.benchmark.RangeQueryBenchmarkAlgorithm
tutorial.outlier.DistanceStddevOutlier \ No newline at end of file
diff --git a/src/META-INF/elki/de.lmu.ifi.dbs.elki.algorithm.AbstractPrimitiveDistanceBasedAlgorithm b/src/META-INF/elki/de.lmu.ifi.dbs.elki.algorithm.AbstractPrimitiveDistanceBasedAlgorithm
index 7ee58e22..32a6b535 100644
--- a/src/META-INF/elki/de.lmu.ifi.dbs.elki.algorithm.AbstractPrimitiveDistanceBasedAlgorithm
+++ b/src/META-INF/elki/de.lmu.ifi.dbs.elki.algorithm.AbstractPrimitiveDistanceBasedAlgorithm
@@ -1,4 +1,5 @@
de.lmu.ifi.dbs.elki.algorithm.DependencyDerivator
de.lmu.ifi.dbs.elki.algorithm.clustering.kmeans.KMeansLloyd
de.lmu.ifi.dbs.elki.algorithm.clustering.kmeans.KMeansMacQueen
-de.lmu.ifi.dbs.elki.algorithm.clustering.kmeans.KMediansLloyd \ No newline at end of file
+de.lmu.ifi.dbs.elki.algorithm.clustering.kmeans.KMediansLloyd
+tutorial.clustering.SameSizeKMeansAlgorithm
diff --git a/src/META-INF/elki/de.lmu.ifi.dbs.elki.algorithm.Algorithm b/src/META-INF/elki/de.lmu.ifi.dbs.elki.algorithm.Algorithm
index 37621d8b..25c3f6e9 100644
--- a/src/META-INF/elki/de.lmu.ifi.dbs.elki.algorithm.Algorithm
+++ b/src/META-INF/elki/de.lmu.ifi.dbs.elki.algorithm.Algorithm
@@ -8,6 +8,7 @@ de.lmu.ifi.dbs.elki.algorithm.clustering.kmeans.KMeansMacQueen
de.lmu.ifi.dbs.elki.algorithm.clustering.kmeans.KMediansLloyd
de.lmu.ifi.dbs.elki.algorithm.clustering.kmeans.KMedoidsPAM
de.lmu.ifi.dbs.elki.algorithm.clustering.kmeans.KMedoidsEM
+de.lmu.ifi.dbs.elki.algorithm.clustering.NaiveMeanShiftClustering
de.lmu.ifi.dbs.elki.algorithm.clustering.OPTICSXi
de.lmu.ifi.dbs.elki.algorithm.clustering.OPTICS
de.lmu.ifi.dbs.elki.algorithm.clustering.SLINK
@@ -35,6 +36,7 @@ de.lmu.ifi.dbs.elki.algorithm.outlier.ABOD
de.lmu.ifi.dbs.elki.algorithm.outlier.AggarwalYuEvolutionary
de.lmu.ifi.dbs.elki.algorithm.outlier.AggarwalYuNaive
de.lmu.ifi.dbs.elki.algorithm.outlier.ALOCI
+de.lmu.ifi.dbs.elki.algorithm.outlier.COP
de.lmu.ifi.dbs.elki.algorithm.outlier.DBOutlierDetection
de.lmu.ifi.dbs.elki.algorithm.outlier.DBOutlierScore
de.lmu.ifi.dbs.elki.algorithm.outlier.EMOutlier
@@ -44,6 +46,7 @@ de.lmu.ifi.dbs.elki.algorithm.outlier.HilOut
de.lmu.ifi.dbs.elki.algorithm.outlier.INFLO
de.lmu.ifi.dbs.elki.algorithm.outlier.KNNOutlier
de.lmu.ifi.dbs.elki.algorithm.outlier.KNNWeightOutlier
+de.lmu.ifi.dbs.elki.algorithm.outlier.LDF
de.lmu.ifi.dbs.elki.algorithm.outlier.LDOF
de.lmu.ifi.dbs.elki.algorithm.outlier.LOCI
de.lmu.ifi.dbs.elki.algorithm.outlier.LOF
@@ -51,6 +54,9 @@ de.lmu.ifi.dbs.elki.algorithm.outlier.LoOP
de.lmu.ifi.dbs.elki.algorithm.outlier.OPTICSOF
de.lmu.ifi.dbs.elki.algorithm.outlier.ReferenceBasedOutlierDetection
de.lmu.ifi.dbs.elki.algorithm.outlier.OnlineLOF
+de.lmu.ifi.dbs.elki.algorithm.outlier.SimpleLOF
+de.lmu.ifi.dbs.elki.algorithm.outlier.SimpleKernelDensityLOF
+de.lmu.ifi.dbs.elki.algorithm.outlier.SimpleCOP
de.lmu.ifi.dbs.elki.algorithm.outlier.subspace.OUTRES
de.lmu.ifi.dbs.elki.algorithm.outlier.subspace.OutRankS1
de.lmu.ifi.dbs.elki.algorithm.outlier.subspace.SOD
@@ -69,6 +75,7 @@ de.lmu.ifi.dbs.elki.algorithm.outlier.meta.ExternalDoubleOutlierScore
de.lmu.ifi.dbs.elki.algorithm.outlier.meta.FeatureBagging
de.lmu.ifi.dbs.elki.algorithm.outlier.meta.HiCS
de.lmu.ifi.dbs.elki.algorithm.outlier.meta.RescaleMetaOutlierAlgorithm
+de.lmu.ifi.dbs.elki.algorithm.outlier.meta.SimpleOutlierEnsemble
de.lmu.ifi.dbs.elki.algorithm.outlier.trivial.ByLabelOutlier
de.lmu.ifi.dbs.elki.algorithm.outlier.trivial.TrivialAllOutlier
de.lmu.ifi.dbs.elki.algorithm.outlier.trivial.TrivialNoOutlier
@@ -84,4 +91,7 @@ de.lmu.ifi.dbs.elki.algorithm.DependencyDerivator
de.lmu.ifi.dbs.elki.algorithm.KNNDistanceOrder
de.lmu.ifi.dbs.elki.algorithm.KNNJoin
de.lmu.ifi.dbs.elki.algorithm.MaterializeDistances
-tutorial.outlier.DistanceStddevOutlier \ No newline at end of file
+de.lmu.ifi.dbs.elki.algorithm.benchmark.KNNBenchmarkAlgorithm
+de.lmu.ifi.dbs.elki.algorithm.benchmark.RangeQueryBenchmarkAlgorithm
+tutorial.clustering.SameSizeKMeansAlgorithm
+tutorial.outlier.DistanceStddevOutlier
diff --git a/src/META-INF/elki/de.lmu.ifi.dbs.elki.algorithm.clustering.ClusteringAlgorithm b/src/META-INF/elki/de.lmu.ifi.dbs.elki.algorithm.clustering.ClusteringAlgorithm
index 2ae85aa5..678db72f 100644
--- a/src/META-INF/elki/de.lmu.ifi.dbs.elki.algorithm.clustering.ClusteringAlgorithm
+++ b/src/META-INF/elki/de.lmu.ifi.dbs.elki.algorithm.clustering.ClusteringAlgorithm
@@ -6,6 +6,7 @@ de.lmu.ifi.dbs.elki.algorithm.clustering.kmeans.KMeansMacQueen
de.lmu.ifi.dbs.elki.algorithm.clustering.kmeans.KMediansLloyd
de.lmu.ifi.dbs.elki.algorithm.clustering.kmeans.KMedoidsPAM
de.lmu.ifi.dbs.elki.algorithm.clustering.kmeans.KMedoidsEM
+de.lmu.ifi.dbs.elki.algorithm.clustering.NaiveMeanShiftClustering
de.lmu.ifi.dbs.elki.algorithm.clustering.OPTICSXi
de.lmu.ifi.dbs.elki.algorithm.clustering.SNNClustering
de.lmu.ifi.dbs.elki.algorithm.clustering.correlation.CASH
@@ -23,4 +24,5 @@ de.lmu.ifi.dbs.elki.algorithm.clustering.trivial.ByLabelHierarchicalClustering
de.lmu.ifi.dbs.elki.algorithm.clustering.trivial.ByModelClustering
de.lmu.ifi.dbs.elki.algorithm.clustering.trivial.TrivialAllInOne
de.lmu.ifi.dbs.elki.algorithm.clustering.trivial.TrivialAllNoise
-de.lmu.ifi.dbs.elki.algorithm.clustering.trivial.ByLabelOrAllInOneClustering \ No newline at end of file
+de.lmu.ifi.dbs.elki.algorithm.clustering.trivial.ByLabelOrAllInOneClustering
+tutorial.clustering.SameSizeKMeansAlgorithm \ No newline at end of file
diff --git a/src/META-INF/elki/de.lmu.ifi.dbs.elki.algorithm.outlier.OutlierAlgorithm b/src/META-INF/elki/de.lmu.ifi.dbs.elki.algorithm.outlier.OutlierAlgorithm
index 18ac09f8..b8ebe564 100644
--- a/src/META-INF/elki/de.lmu.ifi.dbs.elki.algorithm.outlier.OutlierAlgorithm
+++ b/src/META-INF/elki/de.lmu.ifi.dbs.elki.algorithm.outlier.OutlierAlgorithm
@@ -2,6 +2,7 @@ de.lmu.ifi.dbs.elki.algorithm.outlier.ABOD
de.lmu.ifi.dbs.elki.algorithm.outlier.AggarwalYuEvolutionary
de.lmu.ifi.dbs.elki.algorithm.outlier.AggarwalYuNaive
de.lmu.ifi.dbs.elki.algorithm.outlier.ALOCI
+de.lmu.ifi.dbs.elki.algorithm.outlier.COP
de.lmu.ifi.dbs.elki.algorithm.outlier.DBOutlierDetection
de.lmu.ifi.dbs.elki.algorithm.outlier.DBOutlierScore
de.lmu.ifi.dbs.elki.algorithm.outlier.EMOutlier
@@ -11,6 +12,7 @@ de.lmu.ifi.dbs.elki.algorithm.outlier.HilOut
de.lmu.ifi.dbs.elki.algorithm.outlier.INFLO
de.lmu.ifi.dbs.elki.algorithm.outlier.KNNOutlier
de.lmu.ifi.dbs.elki.algorithm.outlier.KNNWeightOutlier
+de.lmu.ifi.dbs.elki.algorithm.outlier.LDF
de.lmu.ifi.dbs.elki.algorithm.outlier.LDOF
de.lmu.ifi.dbs.elki.algorithm.outlier.LOCI
de.lmu.ifi.dbs.elki.algorithm.outlier.LOF
@@ -18,6 +20,9 @@ de.lmu.ifi.dbs.elki.algorithm.outlier.LoOP
de.lmu.ifi.dbs.elki.algorithm.outlier.OPTICSOF
de.lmu.ifi.dbs.elki.algorithm.outlier.ReferenceBasedOutlierDetection
de.lmu.ifi.dbs.elki.algorithm.outlier.OnlineLOF
+de.lmu.ifi.dbs.elki.algorithm.outlier.SimpleLOF
+de.lmu.ifi.dbs.elki.algorithm.outlier.SimpleKernelDensityLOF
+de.lmu.ifi.dbs.elki.algorithm.outlier.SimpleCOP
de.lmu.ifi.dbs.elki.algorithm.outlier.subspace.OUTRES
de.lmu.ifi.dbs.elki.algorithm.outlier.subspace.OutRankS1
de.lmu.ifi.dbs.elki.algorithm.outlier.subspace.SOD
@@ -36,6 +41,7 @@ de.lmu.ifi.dbs.elki.algorithm.outlier.meta.ExternalDoubleOutlierScore
de.lmu.ifi.dbs.elki.algorithm.outlier.meta.FeatureBagging
de.lmu.ifi.dbs.elki.algorithm.outlier.meta.HiCS
de.lmu.ifi.dbs.elki.algorithm.outlier.meta.RescaleMetaOutlierAlgorithm
+de.lmu.ifi.dbs.elki.algorithm.outlier.meta.SimpleOutlierEnsemble
de.lmu.ifi.dbs.elki.algorithm.outlier.trivial.ByLabelOutlier
de.lmu.ifi.dbs.elki.algorithm.outlier.trivial.TrivialAllOutlier
de.lmu.ifi.dbs.elki.algorithm.outlier.trivial.TrivialNoOutlier
diff --git a/src/META-INF/elki/de.lmu.ifi.dbs.elki.application.AbstractApplication b/src/META-INF/elki/de.lmu.ifi.dbs.elki.application.AbstractApplication
new file mode 100644
index 00000000..b2eb6445
--- /dev/null
+++ b/src/META-INF/elki/de.lmu.ifi.dbs.elki.application.AbstractApplication
@@ -0,0 +1,12 @@
+de.lmu.ifi.dbs.elki.gui.minigui.MiniGUI
+de.lmu.ifi.dbs.elki.application.KDDCLIApplication
+de.lmu.ifi.dbs.elki.application.GeneratorXMLSpec
+de.lmu.ifi.dbs.elki.application.ConvertToBundleApplication
+de.lmu.ifi.dbs.elki.application.cache.CacheDoubleDistanceInOnDiskMatrix
+de.lmu.ifi.dbs.elki.application.cache.CacheFloatDistanceInOnDiskMatrix
+de.lmu.ifi.dbs.elki.application.geo.VisualizeGeodesicDistances
+de.lmu.ifi.dbs.elki.application.greedyensemble.ComputeKNNOutlierScores
+de.lmu.ifi.dbs.elki.application.greedyensemble.GreedyEnsembleExperiment
+de.lmu.ifi.dbs.elki.application.greedyensemble.VisualizePairwiseGainMatrix
+de.lmu.ifi.dbs.elki.application.visualization.KNNExplorer
+de.lmu.ifi.dbs.elki.application.ComputeSingleColorHistogram
diff --git a/src/META-INF/elki/de.lmu.ifi.dbs.elki.data.NumberVector b/src/META-INF/elki/de.lmu.ifi.dbs.elki.data.NumberVector
deleted file mode 100644
index fd4cab91..00000000
--- a/src/META-INF/elki/de.lmu.ifi.dbs.elki.data.NumberVector
+++ /dev/null
@@ -1,11 +0,0 @@
-de.lmu.ifi.dbs.elki.data.DoubleVector
-de.lmu.ifi.dbs.elki.data.BitVector
-de.lmu.ifi.dbs.elki.data.FloatVector
-de.lmu.ifi.dbs.elki.data.IntegerVector
-de.lmu.ifi.dbs.elki.data.OneDimensionalDoubleVector
-de.lmu.ifi.dbs.elki.data.ParameterizationFunction
-de.lmu.ifi.dbs.elki.data.SparseFloatVector
-de.lmu.ifi.dbs.elki.data.SparseDoubleVector
-# de.lmu.ifi.dbs.elki.math.linearalgebra.Vector
-# de.lmu.ifi.dbs.elki.math.linearalgebra.Centroid
-# de.lmu.ifi.dbs.elki.math.linearalgebra.ProjectedCentroid \ No newline at end of file
diff --git a/src/META-INF/elki/de.lmu.ifi.dbs.elki.data.NumberVector$Factory b/src/META-INF/elki/de.lmu.ifi.dbs.elki.data.NumberVector$Factory
new file mode 100644
index 00000000..c5e5f3d8
--- /dev/null
+++ b/src/META-INF/elki/de.lmu.ifi.dbs.elki.data.NumberVector$Factory
@@ -0,0 +1,7 @@
+de.lmu.ifi.dbs.elki.data.DoubleVector$Factory
+de.lmu.ifi.dbs.elki.data.BitVector$Factory
+de.lmu.ifi.dbs.elki.data.FloatVector$Factory
+de.lmu.ifi.dbs.elki.data.IntegerVector$Factory
+de.lmu.ifi.dbs.elki.data.OneDimensionalDoubleVector$Factory
+de.lmu.ifi.dbs.elki.data.SparseFloatVector$Factory
+de.lmu.ifi.dbs.elki.data.SparseDoubleVector$Factory \ No newline at end of file
diff --git a/src/META-INF/elki/de.lmu.ifi.dbs.elki.data.SparseNumberVector b/src/META-INF/elki/de.lmu.ifi.dbs.elki.data.SparseNumberVector
deleted file mode 100644
index ca27b7b8..00000000
--- a/src/META-INF/elki/de.lmu.ifi.dbs.elki.data.SparseNumberVector
+++ /dev/null
@@ -1,2 +0,0 @@
-de.lmu.ifi.dbs.elki.data.SparseFloatVector
-de.lmu.ifi.dbs.elki.data.SparseDoubleVector \ No newline at end of file
diff --git a/src/META-INF/elki/de.lmu.ifi.dbs.elki.data.SparseNumberVector$Factory b/src/META-INF/elki/de.lmu.ifi.dbs.elki.data.SparseNumberVector$Factory
new file mode 100644
index 00000000..959360e1
--- /dev/null
+++ b/src/META-INF/elki/de.lmu.ifi.dbs.elki.data.SparseNumberVector$Factory
@@ -0,0 +1,2 @@
+de.lmu.ifi.dbs.elki.data.SparseFloatVector$Factory
+de.lmu.ifi.dbs.elki.data.SparseDoubleVector$Factory \ No newline at end of file
diff --git a/src/META-INF/elki/de.lmu.ifi.dbs.elki.datasource.DatabaseConnection b/src/META-INF/elki/de.lmu.ifi.dbs.elki.datasource.DatabaseConnection
index 942dc71f..1ec23984 100644
--- a/src/META-INF/elki/de.lmu.ifi.dbs.elki.datasource.DatabaseConnection
+++ b/src/META-INF/elki/de.lmu.ifi.dbs.elki.datasource.DatabaseConnection
@@ -1,11 +1,12 @@
de.lmu.ifi.dbs.elki.datasource.FileBasedDatabaseConnection
-de.lmu.ifi.dbs.elki.datasource.ExternalIDJoinDatabaseConnection
-de.lmu.ifi.dbs.elki.datasource.LabelJoinDatabaseConnection
+de.lmu.ifi.dbs.elki.datasource.BundleDatabaseConnection
de.lmu.ifi.dbs.elki.datasource.GeneratorXMLDatabaseConnection
de.lmu.ifi.dbs.elki.datasource.RandomDoubleVectorDatabaseConnection
+de.lmu.ifi.dbs.elki.datasource.DBIDRangeDatabaseConnection
+de.lmu.ifi.dbs.elki.datasource.ExternalIDJoinDatabaseConnection
+de.lmu.ifi.dbs.elki.datasource.LabelJoinDatabaseConnection
de.lmu.ifi.dbs.elki.datasource.ConcatenateFilesDatabaseConnection
de.lmu.ifi.dbs.elki.datasource.EmptyDatabaseConnection
-de.lmu.ifi.dbs.elki.datasource.DBIDRangeDatabaseConnection
de.lmu.ifi.dbs.elki.datasource.PresortedBlindJoinDatabaseConnection
# de.lmu.ifi.dbs.elki.datasource.InputStreamDatabaseConnection
# de.lmu.ifi.dbs.elki.datasource.ArrayAdapterDatabaseConnection \ No newline at end of file
diff --git a/src/META-INF/elki/de.lmu.ifi.dbs.elki.datasource.filter.ObjectFilter b/src/META-INF/elki/de.lmu.ifi.dbs.elki.datasource.filter.ObjectFilter
index fa40e217..4ae2cece 100644
--- a/src/META-INF/elki/de.lmu.ifi.dbs.elki.datasource.filter.ObjectFilter
+++ b/src/META-INF/elki/de.lmu.ifi.dbs.elki.datasource.filter.ObjectFilter
@@ -1,17 +1,14 @@
-de.lmu.ifi.dbs.elki.datasource.filter.ByLabelFilter
-de.lmu.ifi.dbs.elki.datasource.filter.ClassLabelFilter
-de.lmu.ifi.dbs.elki.datasource.filter.DoubleVectorProjectionFilter
-de.lmu.ifi.dbs.elki.datasource.filter.DoubleVectorRandomProjectionFilter
-de.lmu.ifi.dbs.elki.datasource.filter.ExternalIDFilter
de.lmu.ifi.dbs.elki.datasource.filter.FixedDBIDsFilter
de.lmu.ifi.dbs.elki.datasource.filter.NoOpFilter
-de.lmu.ifi.dbs.elki.datasource.filter.NoMissingValuesFilter
+de.lmu.ifi.dbs.elki.datasource.filter.ClassLabelFilter
+de.lmu.ifi.dbs.elki.datasource.filter.ExternalIDFilter
+de.lmu.ifi.dbs.elki.datasource.filter.SparseVectorFieldFilter
+de.lmu.ifi.dbs.elki.datasource.filter.ByLabelFilter
de.lmu.ifi.dbs.elki.datasource.filter.RandomSamplingStreamFilter
de.lmu.ifi.dbs.elki.datasource.filter.ShuffleObjectsFilter
de.lmu.ifi.dbs.elki.datasource.filter.SortByLabelFilter
-de.lmu.ifi.dbs.elki.datasource.filter.SparseNumberVectorProjectionFilter
-de.lmu.ifi.dbs.elki.datasource.filter.SparseNumberVectorRandomProjectionFilter
-de.lmu.ifi.dbs.elki.datasource.filter.SparseVectorFieldFilter
+de.lmu.ifi.dbs.elki.datasource.filter.NoMissingValuesFilter
+de.lmu.ifi.dbs.elki.datasource.filter.HistogramJitterFilter
de.lmu.ifi.dbs.elki.datasource.filter.SplitNumberVectorFilter
de.lmu.ifi.dbs.elki.datasource.filter.normalization.AttributeWiseErfNormalization
de.lmu.ifi.dbs.elki.datasource.filter.normalization.AttributeWiseMinMaxNormalization
@@ -20,4 +17,6 @@ de.lmu.ifi.dbs.elki.datasource.filter.normalization.LengthNormalization
de.lmu.ifi.dbs.elki.datasource.filter.normalization.InverseDocumentFrequencyNormalization
de.lmu.ifi.dbs.elki.datasource.filter.normalization.RankTieNormalization
de.lmu.ifi.dbs.elki.datasource.filter.normalization.TFIDFNormalization
-de.lmu.ifi.dbs.elki.datasource.filter.transform.GlobalPrincipalComponentAnalysisTransform \ No newline at end of file
+de.lmu.ifi.dbs.elki.datasource.filter.transform.GlobalPrincipalComponentAnalysisTransform
+de.lmu.ifi.dbs.elki.datasource.filter.transform.NumberVectorFeatureSelectionFilter
+de.lmu.ifi.dbs.elki.datasource.filter.transform.NumberVectorRandomFeatureSelectionFilter
diff --git a/src/META-INF/elki/de.lmu.ifi.dbs.elki.datasource.filter.StreamFilter b/src/META-INF/elki/de.lmu.ifi.dbs.elki.datasource.filter.StreamFilter
index 178916cd..4c4a71cc 100644
--- a/src/META-INF/elki/de.lmu.ifi.dbs.elki.datasource.filter.StreamFilter
+++ b/src/META-INF/elki/de.lmu.ifi.dbs.elki.datasource.filter.StreamFilter
@@ -1,10 +1,9 @@
-de.lmu.ifi.dbs.elki.datasource.filter.ByLabelFilter
-de.lmu.ifi.dbs.elki.datasource.filter.DoubleVectorProjectionFilter
-de.lmu.ifi.dbs.elki.datasource.filter.DoubleVectorRandomProjectionFilter
+de.lmu.ifi.dbs.elki.datasource.filter.NoOpFilter
de.lmu.ifi.dbs.elki.datasource.filter.FixedDBIDsFilter
+de.lmu.ifi.dbs.elki.datasource.filter.ByLabelFilter
de.lmu.ifi.dbs.elki.datasource.filter.NoMissingValuesFilter
-de.lmu.ifi.dbs.elki.datasource.filter.NoOpFilter
de.lmu.ifi.dbs.elki.datasource.filter.RandomSamplingStreamFilter
-de.lmu.ifi.dbs.elki.datasource.filter.SparseNumberVectorProjectionFilter
-de.lmu.ifi.dbs.elki.datasource.filter.SparseNumberVectorRandomProjectionFilter
-de.lmu.ifi.dbs.elki.datasource.filter.normalization.LengthNormalization \ No newline at end of file
+de.lmu.ifi.dbs.elki.datasource.filter.HistogramJitterFilter
+de.lmu.ifi.dbs.elki.datasource.filter.normalization.LengthNormalization
+de.lmu.ifi.dbs.elki.datasource.filter.transform.NumberVectorFeatureSelectionFilter
+de.lmu.ifi.dbs.elki.datasource.filter.transform.NumberVectorRandomFeatureSelectionFilter \ No newline at end of file
diff --git a/src/META-INF/elki/de.lmu.ifi.dbs.elki.distance.distancefunction.NumberVectorDistanceFunction b/src/META-INF/elki/de.lmu.ifi.dbs.elki.distance.distancefunction.NumberVectorDistanceFunction
index ab889ff6..a9c0f46d 100644
--- a/src/META-INF/elki/de.lmu.ifi.dbs.elki.distance.distancefunction.NumberVectorDistanceFunction
+++ b/src/META-INF/elki/de.lmu.ifi.dbs.elki.distance.distancefunction.NumberVectorDistanceFunction
@@ -26,4 +26,4 @@ de.lmu.ifi.dbs.elki.distance.distancefunction.timeseries.EDRDistanceFunction
de.lmu.ifi.dbs.elki.distance.distancefunction.timeseries.ERPDistanceFunction
de.lmu.ifi.dbs.elki.distance.distancefunction.timeseries.LCSSDistanceFunction
# tutorial.distancefunction.MultiLPNorm
-# tutorial.distancefunction.TutorialDistanceFunction \ No newline at end of file
+# tutorial.distancefunction.TutorialDistanceFunction
diff --git a/src/META-INF/elki/de.lmu.ifi.dbs.elki.distance.distancefunction.SpatialPrimitiveDistanceFunction b/src/META-INF/elki/de.lmu.ifi.dbs.elki.distance.distancefunction.SpatialPrimitiveDistanceFunction
index 01d9789a..bf2810a7 100644
--- a/src/META-INF/elki/de.lmu.ifi.dbs.elki.distance.distancefunction.SpatialPrimitiveDistanceFunction
+++ b/src/META-INF/elki/de.lmu.ifi.dbs.elki.distance.distancefunction.SpatialPrimitiveDistanceFunction
@@ -9,6 +9,9 @@ de.lmu.ifi.dbs.elki.distance.distancefunction.CanberraDistanceFunction
de.lmu.ifi.dbs.elki.distance.distancefunction.SquaredEuclideanDistanceFunction
# de.lmu.ifi.dbs.elki.distance.distancefunction.WeightedLPNormDistanceFunction
de.lmu.ifi.dbs.elki.distance.distancefunction.colorhistogram.HistogramIntersectionDistanceFunction
+de.lmu.ifi.dbs.elki.distance.distancefunction.geo.LatLngDistanceFunction
+de.lmu.ifi.dbs.elki.distance.distancefunction.geo.LngLatDistanceFunction
+de.lmu.ifi.dbs.elki.distance.distancefunction.geo.DimensionSelectingLatLngDistanceFunction
de.lmu.ifi.dbs.elki.distance.distancefunction.subspace.DimensionSelectingDistanceFunction
de.lmu.ifi.dbs.elki.distance.distancefunction.subspace.SubspaceEuclideanDistanceFunction
de.lmu.ifi.dbs.elki.distance.distancefunction.subspace.SubspaceLPNormDistanceFunction
diff --git a/src/META-INF/elki/de.lmu.ifi.dbs.elki.distance.distancefunction.SpatialPrimitiveDoubleDistanceFunction b/src/META-INF/elki/de.lmu.ifi.dbs.elki.distance.distancefunction.SpatialPrimitiveDoubleDistanceFunction
index a5e3364e..1ff7d635 100644
--- a/src/META-INF/elki/de.lmu.ifi.dbs.elki.distance.distancefunction.SpatialPrimitiveDoubleDistanceFunction
+++ b/src/META-INF/elki/de.lmu.ifi.dbs.elki.distance.distancefunction.SpatialPrimitiveDoubleDistanceFunction
@@ -9,6 +9,9 @@ de.lmu.ifi.dbs.elki.distance.distancefunction.CanberraDistanceFunction
de.lmu.ifi.dbs.elki.distance.distancefunction.WeightedLPNormDistanceFunction
de.lmu.ifi.dbs.elki.distance.distancefunction.SquaredEuclideanDistanceFunction
de.lmu.ifi.dbs.elki.distance.distancefunction.colorhistogram.HistogramIntersectionDistanceFunction
+de.lmu.ifi.dbs.elki.distance.distancefunction.geo.LatLngDistanceFunction
+de.lmu.ifi.dbs.elki.distance.distancefunction.geo.LngLatDistanceFunction
+de.lmu.ifi.dbs.elki.distance.distancefunction.geo.DimensionSelectingLatLngDistanceFunction
de.lmu.ifi.dbs.elki.distance.distancefunction.subspace.DimensionSelectingDistanceFunction
de.lmu.ifi.dbs.elki.distance.distancefunction.subspace.SubspaceEuclideanDistanceFunction
de.lmu.ifi.dbs.elki.distance.distancefunction.subspace.SubspaceLPNormDistanceFunction
diff --git a/src/META-INF/elki/de.lmu.ifi.dbs.elki.math.dimensionsimilarity.DimensionSimilarity b/src/META-INF/elki/de.lmu.ifi.dbs.elki.math.dimensionsimilarity.DimensionSimilarity
new file mode 100644
index 00000000..0c49e67c
--- /dev/null
+++ b/src/META-INF/elki/de.lmu.ifi.dbs.elki.math.dimensionsimilarity.DimensionSimilarity
@@ -0,0 +1,7 @@
+de.lmu.ifi.dbs.elki.math.dimensionsimilarity.CovarianceDimensionSimilarity
+de.lmu.ifi.dbs.elki.math.dimensionsimilarity.HSMDimensionSimilarity
+de.lmu.ifi.dbs.elki.math.dimensionsimilarity.HiCSDimensionSimilarity
+de.lmu.ifi.dbs.elki.math.dimensionsimilarity.MCEDimensionSimilarity
+de.lmu.ifi.dbs.elki.math.dimensionsimilarity.SURFINGDimensionSimilarity
+de.lmu.ifi.dbs.elki.math.dimensionsimilarity.SlopeDimensionSimilarity
+de.lmu.ifi.dbs.elki.math.dimensionsimilarity.SlopeInversionDimensionSimilarity
diff --git a/src/META-INF/elki/de.lmu.ifi.dbs.elki.math.linearalgebra.pca.CovarianceMatrixBuilder b/src/META-INF/elki/de.lmu.ifi.dbs.elki.math.linearalgebra.pca.CovarianceMatrixBuilder
index 95801f2c..7dabf57a 100644
--- a/src/META-INF/elki/de.lmu.ifi.dbs.elki.math.linearalgebra.pca.CovarianceMatrixBuilder
+++ b/src/META-INF/elki/de.lmu.ifi.dbs.elki.math.linearalgebra.pca.CovarianceMatrixBuilder
@@ -1,2 +1,3 @@
de.lmu.ifi.dbs.elki.math.linearalgebra.pca.StandardCovarianceMatrixBuilder
de.lmu.ifi.dbs.elki.math.linearalgebra.pca.WeightedCovarianceMatrixBuilder
+de.lmu.ifi.dbs.elki.math.linearalgebra.pca.RANSACCovarianceMatrixBuilder \ No newline at end of file
diff --git a/src/META-INF/elki/de.lmu.ifi.dbs.elki.math.statistics.KernelDensityFunction b/src/META-INF/elki/de.lmu.ifi.dbs.elki.math.statistics.KernelDensityFunction
new file mode 100644
index 00000000..84f29b30
--- /dev/null
+++ b/src/META-INF/elki/de.lmu.ifi.dbs.elki.math.statistics.KernelDensityFunction
@@ -0,0 +1,4 @@
+de.lmu.ifi.dbs.elki.math.statistics.EpanechnikovKernelDensityFunction
+de.lmu.ifi.dbs.elki.math.statistics.GaussianKernelDensityFunction
+de.lmu.ifi.dbs.elki.math.statistics.TriangularKernelDensityFunction
+de.lmu.ifi.dbs.elki.math.statistics.UniformKernelDensityFunction
diff --git a/src/META-INF/elki/de.lmu.ifi.dbs.elki.math.statistics.distribution.Distribution b/src/META-INF/elki/de.lmu.ifi.dbs.elki.math.statistics.distribution.Distribution
new file mode 100644
index 00000000..cb0541ea
--- /dev/null
+++ b/src/META-INF/elki/de.lmu.ifi.dbs.elki.math.statistics.distribution.Distribution
@@ -0,0 +1,11 @@
+de.lmu.ifi.dbs.elki.math.statistics.distribution.UniformDistribution
+de.lmu.ifi.dbs.elki.math.statistics.distribution.NormalDistribution
+de.lmu.ifi.dbs.elki.math.statistics.distribution.ExponentialDistribution
+de.lmu.ifi.dbs.elki.math.statistics.distribution.GammaDistribution
+de.lmu.ifi.dbs.elki.math.statistics.distribution.BetaDistribution
+de.lmu.ifi.dbs.elki.math.statistics.distribution.ChiDistribution
+de.lmu.ifi.dbs.elki.math.statistics.distribution.ChiSquaredDistribution
+de.lmu.ifi.dbs.elki.math.statistics.distribution.PoissonDistribution
+de.lmu.ifi.dbs.elki.math.statistics.distribution.StudentsTDistribution
+de.lmu.ifi.dbs.elki.math.statistics.distribution.ConstantDistribution
+de.lmu.ifi.dbs.elki.math.statistics.distribution.HaltonUniformDistribution \ No newline at end of file
diff --git a/src/META-INF/elki/de.lmu.ifi.dbs.elki.math.statistics.distribution.DistributionWithRandom b/src/META-INF/elki/de.lmu.ifi.dbs.elki.math.statistics.distribution.DistributionWithRandom
new file mode 100644
index 00000000..0e27040e
--- /dev/null
+++ b/src/META-INF/elki/de.lmu.ifi.dbs.elki.math.statistics.distribution.DistributionWithRandom
@@ -0,0 +1,9 @@
+de.lmu.ifi.dbs.elki.math.statistics.distribution.UniformDistribution
+de.lmu.ifi.dbs.elki.math.statistics.distribution.NormalDistribution
+de.lmu.ifi.dbs.elki.math.statistics.distribution.ExponentialDistribution
+de.lmu.ifi.dbs.elki.math.statistics.distribution.GammaDistribution
+de.lmu.ifi.dbs.elki.math.statistics.distribution.BetaDistribution
+de.lmu.ifi.dbs.elki.math.statistics.distribution.ChiDistribution
+de.lmu.ifi.dbs.elki.math.statistics.distribution.ChiSquaredDistribution
+de.lmu.ifi.dbs.elki.math.statistics.distribution.ConstantDistribution
+de.lmu.ifi.dbs.elki.math.statistics.distribution.HaltonUniformDistribution \ No newline at end of file
diff --git a/src/META-INF/elki/de.lmu.ifi.dbs.elki.result.ResultHandler b/src/META-INF/elki/de.lmu.ifi.dbs.elki.result.ResultHandler
index 66d8fef1..c16f1124 100644
--- a/src/META-INF/elki/de.lmu.ifi.dbs.elki.result.ResultHandler
+++ b/src/META-INF/elki/de.lmu.ifi.dbs.elki.result.ResultHandler
@@ -4,3 +4,4 @@ de.lmu.ifi.dbs.elki.result.DiscardResultHandler
de.lmu.ifi.dbs.elki.result.KMLOutputHandler
de.lmu.ifi.dbs.elki.visualization.ExportVisualizations
de.lmu.ifi.dbs.elki.application.jsmap.JSONResultHandler
+de.lmu.ifi.dbs.elki.result.LogResultStructureResultHandler \ No newline at end of file
diff --git a/src/META-INF/elki/de.lmu.ifi.dbs.elki.utilities.ensemble.EnsembleVoting b/src/META-INF/elki/de.lmu.ifi.dbs.elki.utilities.ensemble.EnsembleVoting
new file mode 100644
index 00000000..7cb9359d
--- /dev/null
+++ b/src/META-INF/elki/de.lmu.ifi.dbs.elki.utilities.ensemble.EnsembleVoting
@@ -0,0 +1,6 @@
+de.lmu.ifi.dbs.elki.utilities.ensemble.EnsembleVotingMean
+de.lmu.ifi.dbs.elki.utilities.ensemble.EnsembleVotingMin
+de.lmu.ifi.dbs.elki.utilities.ensemble.EnsembleVotingMax
+de.lmu.ifi.dbs.elki.utilities.ensemble.EnsembleVotingMedian
+de.lmu.ifi.dbs.elki.utilities.ensemble.EnsembleVotingBayes
+de.lmu.ifi.dbs.elki.utilities.ensemble.EnsembleVotingRestrictedBayes \ No newline at end of file
diff --git a/src/META-INF/elki/de.lmu.ifi.dbs.elki.visualization.visualizers.VisFactory b/src/META-INF/elki/de.lmu.ifi.dbs.elki.visualization.visualizers.VisFactory
index 06c0cc41..a4c5e72f 100644
--- a/src/META-INF/elki/de.lmu.ifi.dbs.elki.visualization.visualizers.VisFactory
+++ b/src/META-INF/elki/de.lmu.ifi.dbs.elki.visualization.visualizers.VisFactory
@@ -1,46 +1,50 @@
-de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.AxisVisualization$Factory
-de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.MarkerVisualization$Factory
-de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.PolygonVisualization$Factory
-de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.cluster.ClusterMeanVisualization$Factory
-de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.cluster.ClusterHullVisualization$Factory
-de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.cluster.EMClusterVisualization$Factory
-de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.cluster.VoronoiVisualization$Factory
-de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.cluster.ClusterOrderVisualization$Factory
-de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.index.TreeMBRVisualization$Factory
-de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.index.TreeSphereVisualization$Factory
-de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.outlier.BubbleVisualization$Factory
-de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.TooltipScoreVisualization$Factory
-de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.TooltipStringVisualization$Factory
-de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.ToolBox2DVisualization$Factory
-de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.ReferencePointsVisualization$Factory
-de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.density.DensityEstimationOverlay$Factory
-de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.selection.MoveObjectsToolVisualization$Factory
-de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.selection.SelectionDotVisualization$Factory
-de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.selection.SelectionConvexHullVisualization$Factory
-de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.selection.SelectionCubeVisualization$Factory
-de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.selection.SelectionToolCubeVisualization$Factory
-de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.selection.SelectionToolDotVisualization$Factory
-de.lmu.ifi.dbs.elki.visualization.visualizers.histogram.ColoredHistogramVisualizer$Factory
-de.lmu.ifi.dbs.elki.visualization.visualizers.parallel.LineVisualization$Factory
-de.lmu.ifi.dbs.elki.visualization.visualizers.parallel.ParallelAxisVisualization$Factory
-de.lmu.ifi.dbs.elki.visualization.visualizers.parallel.cluster.ClusterParallelMeanVisualization$Factory
-de.lmu.ifi.dbs.elki.visualization.visualizers.parallel.cluster.ClusterOutlineVisualization$Factory
-de.lmu.ifi.dbs.elki.visualization.visualizers.parallel.index.RTreeParallelVisualization$Factory
-de.lmu.ifi.dbs.elki.visualization.visualizers.parallel.selection.SelectionAxisRangeVisualization$Factory
-de.lmu.ifi.dbs.elki.visualization.visualizers.parallel.selection.SelectionLineVisualization$Factory
-de.lmu.ifi.dbs.elki.visualization.visualizers.parallel.selection.SelectionToolAxisRangeVisualization$Factory
-de.lmu.ifi.dbs.elki.visualization.visualizers.parallel.selection.SelectionToolLineVisualization$Factory
-de.lmu.ifi.dbs.elki.visualization.visualizers.pairsegments.CircleSegmentsVisualizer$Factory
-de.lmu.ifi.dbs.elki.visualization.visualizers.visunproj.HistogramVisFactory
-de.lmu.ifi.dbs.elki.visualization.visualizers.visunproj.ClusterEvaluationVisFactory
-de.lmu.ifi.dbs.elki.visualization.visualizers.visunproj.XYCurveVisFactory
-de.lmu.ifi.dbs.elki.visualization.visualizers.visunproj.LabelVisFactory
-de.lmu.ifi.dbs.elki.visualization.visualizers.visunproj.PixmapVisualizer$Factory
-de.lmu.ifi.dbs.elki.visualization.visualizers.visunproj.SimilarityMatrixVisualizer$Factory
-de.lmu.ifi.dbs.elki.visualization.visualizers.visunproj.KeyVisualization$Factory
-de.lmu.ifi.dbs.elki.visualization.visualizers.visunproj.SettingsVisFactory
-de.lmu.ifi.dbs.elki.visualization.visualizers.optics.OPTICSClusterVisualization$Factory
-de.lmu.ifi.dbs.elki.visualization.visualizers.optics.OPTICSPlotCutVisualization$Factory
-de.lmu.ifi.dbs.elki.visualization.visualizers.optics.OPTICSPlotSelectionVisualization$Factory
-de.lmu.ifi.dbs.elki.visualization.visualizers.optics.OPTICSPlotVisualizer$Factory
-de.lmu.ifi.dbs.elki.visualization.visualizers.optics.OPTICSSteepAreaVisualization$Factory
+de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.AxisVisualization
+de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.MarkerVisualization
+de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.PolygonVisualization
+de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.cluster.ClusterMeanVisualization
+de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.cluster.ClusterHullVisualization
+de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.cluster.EMClusterVisualization
+de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.cluster.VoronoiVisualization
+de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.cluster.ClusterOrderVisualization
+de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.index.TreeMBRVisualization
+de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.index.TreeSphereVisualization
+de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.outlier.BubbleVisualization
+de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.outlier.COPVectorVisualization
+de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.TooltipScoreVisualization
+de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.TooltipStringVisualization
+de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.ToolBox2DVisualization
+de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.ReferencePointsVisualization
+de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.density.DensityEstimationOverlay
+de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.selection.DistanceFunctionVisualization
+de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.selection.MoveObjectsToolVisualization
+de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.selection.SelectionDotVisualization
+de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.selection.SelectionConvexHullVisualization
+de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.selection.SelectionCubeVisualization
+de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.selection.SelectionToolCubeVisualization
+de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.selection.SelectionToolDotVisualization
+de.lmu.ifi.dbs.elki.visualization.visualizers.histogram.ColoredHistogramVisualizer
+de.lmu.ifi.dbs.elki.visualization.visualizers.parallel.LineVisualization
+de.lmu.ifi.dbs.elki.visualization.visualizers.parallel.ParallelAxisVisualization
+de.lmu.ifi.dbs.elki.visualization.visualizers.parallel.AxisVisibilityVisualization
+de.lmu.ifi.dbs.elki.visualization.visualizers.parallel.AxisReorderVisualization
+de.lmu.ifi.dbs.elki.visualization.visualizers.parallel.cluster.ClusterParallelMeanVisualization
+de.lmu.ifi.dbs.elki.visualization.visualizers.parallel.cluster.ClusterOutlineVisualization
+de.lmu.ifi.dbs.elki.visualization.visualizers.parallel.index.RTreeParallelVisualization
+de.lmu.ifi.dbs.elki.visualization.visualizers.parallel.selection.SelectionAxisRangeVisualization
+de.lmu.ifi.dbs.elki.visualization.visualizers.parallel.selection.SelectionLineVisualization
+de.lmu.ifi.dbs.elki.visualization.visualizers.parallel.selection.SelectionToolAxisRangeVisualization
+de.lmu.ifi.dbs.elki.visualization.visualizers.parallel.selection.SelectionToolLineVisualization
+de.lmu.ifi.dbs.elki.visualization.visualizers.pairsegments.CircleSegmentsVisualizer
+de.lmu.ifi.dbs.elki.visualization.visualizers.visunproj.HistogramVisualization
+de.lmu.ifi.dbs.elki.visualization.visualizers.visunproj.ClusterEvaluationVisualization
+de.lmu.ifi.dbs.elki.visualization.visualizers.visunproj.XYCurveVisualization
+de.lmu.ifi.dbs.elki.visualization.visualizers.visunproj.LabelVisualization
+de.lmu.ifi.dbs.elki.visualization.visualizers.visunproj.PixmapVisualizer
+de.lmu.ifi.dbs.elki.visualization.visualizers.visunproj.SimilarityMatrixVisualizer
+de.lmu.ifi.dbs.elki.visualization.visualizers.visunproj.KeyVisualization
+de.lmu.ifi.dbs.elki.visualization.visualizers.visunproj.SettingsVisualization
+de.lmu.ifi.dbs.elki.visualization.visualizers.optics.OPTICSClusterVisualization
+de.lmu.ifi.dbs.elki.visualization.visualizers.optics.OPTICSPlotCutVisualization
+de.lmu.ifi.dbs.elki.visualization.visualizers.optics.OPTICSPlotSelectionVisualization
+de.lmu.ifi.dbs.elki.visualization.visualizers.optics.OPTICSPlotVisualizer
+de.lmu.ifi.dbs.elki.visualization.visualizers.optics.OPTICSSteepAreaVisualization
diff --git a/src/de/lmu/ifi/dbs/elki/KDDTask.java b/src/de/lmu/ifi/dbs/elki/KDDTask.java
index cdad3198..3d71a8c4 100644
--- a/src/de/lmu/ifi/dbs/elki/KDDTask.java
+++ b/src/de/lmu/ifi/dbs/elki/KDDTask.java
@@ -58,7 +58,7 @@ public class KDDTask implements Parameterizable {
/**
* The settings used, for settings reporting.
*/
- private Collection<Pair<Object, Parameter<?, ?>>> settings;
+ private Collection<Pair<Object, Parameter<?>>> settings;
/**
* The data input step
@@ -94,7 +94,7 @@ public class KDDTask implements Parameterizable {
* @param outputStep
* @param settings
*/
- public KDDTask(InputStep inputStep, AlgorithmStep algorithmStep, EvaluationStep evaluationStep, OutputStep outputStep, Collection<Pair<Object, Parameter<?, ?>>> settings) {
+ public KDDTask(InputStep inputStep, AlgorithmStep algorithmStep, EvaluationStep evaluationStep, OutputStep outputStep, Collection<Pair<Object, Parameter<?>>> settings) {
super();
this.inputStep = inputStep;
this.algorithmStep = algorithmStep;
@@ -147,7 +147,7 @@ public class KDDTask implements Parameterizable {
EvaluationStep evaluationStep = null;
- Collection<Pair<Object, Parameter<?, ?>>> settings = null;
+ Collection<Pair<Object, Parameter<?>>> settings = null;
OutputStep outputStep = null;
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/APRIORI.java b/src/de/lmu/ifi/dbs/elki/algorithm/APRIORI.java
index 65339257..70706df8 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/APRIORI.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/APRIORI.java
@@ -26,7 +26,7 @@ package de.lmu.ifi.dbs.elki.algorithm;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
-import java.util.Hashtable;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -36,16 +36,16 @@ import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
import de.lmu.ifi.dbs.elki.database.Database;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.result.AprioriResult;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.utilities.documentation.Description;
import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
import de.lmu.ifi.dbs.elki.utilities.documentation.Title;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterEqualConstraint;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.IntervalConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.LessEqualConstraint;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.OneMustBeSetGlobalConstraint;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.OnlyOneIsAllowedToBeSetGlobalConstraint;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
@@ -72,14 +72,14 @@ public class APRIORI extends AbstractAlgorithm<AprioriResult> {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(APRIORI.class);
+ private static final Logging LOG = Logging.getLogger(APRIORI.class);
/**
* Optional parameter to specify the threshold for minimum frequency, must be
* a double greater than or equal to 0 and less than or equal to 1.
* Alternatively to parameter {@link #MINSUPP_ID}).
*/
- public static final OptionID MINFREQ_ID = OptionID.getOrCreateOptionID("apriori.minfreq", "Threshold for minimum frequency as percentage value " + "(alternatively to parameter apriori.minsupp).");
+ public static final OptionID MINFREQ_ID = new OptionID("apriori.minfreq", "Threshold for minimum frequency as percentage value " + "(alternatively to parameter apriori.minsupp).");
/**
* Parameter to specify the threshold for minimum support as minimally
@@ -88,7 +88,7 @@ public class APRIORI extends AbstractAlgorithm<AprioriResult> {
* {@link #MINSUPP_ID} is slightly preferable over setting {@link #MINFREQ_ID}
* in terms of efficiency.
*/
- public static final OptionID MINSUPP_ID = OptionID.getOrCreateOptionID("apriori.minsupp", "Threshold for minimum support as minimally required number of transactions " + "(alternatively to parameter apriori.minfreq" + " - setting apriori.minsupp is slightly preferable over setting " + "apriori.minfreq in terms of efficiency).");
+ public static final OptionID MINSUPP_ID = new OptionID("apriori.minsupp", "Threshold for minimum support as minimally required number of transactions " + "(alternatively to parameter apriori.minfreq" + " - setting apriori.minsupp is slightly preferable over setting " + "apriori.minfreq in terms of efficiency).");
/**
* Holds the value of {@link #MINFREQ_ID}.
@@ -128,13 +128,13 @@ public class APRIORI extends AbstractAlgorithm<AprioriResult> {
* @return the AprioriResult learned by this APRIORI
*/
public AprioriResult run(Database database, Relation<BitVector> relation) {
- Map<BitSet, Integer> support = new Hashtable<BitSet, Integer>();
+ Map<BitSet, Integer> support = new HashMap<BitSet, Integer>();
List<BitSet> solution = new ArrayList<BitSet>();
final int size = relation.size();
if(size > 0) {
int dim;
try {
- dim = DatabaseUtil.dimensionality(relation);
+ dim = RelationUtil.dimensionality(relation);
}
catch(UnsupportedOperationException e) {
dim = 0;
@@ -145,9 +145,9 @@ public class APRIORI extends AbstractAlgorithm<AprioriResult> {
candidates[i].set(i);
}
while(candidates.length > 0) {
- StringBuffer msg = new StringBuffer();
+ StringBuilder msg = new StringBuilder();
BitSet[] frequentItemsets = frequentItemsets(support, candidates, relation);
- if(logger.isVerbose()) {
+ if(LOG.isVerbose()) {
msg.append("\ncandidates").append(Arrays.asList(candidates));
msg.append("\nfrequentItemsets").append(Arrays.asList(frequentItemsets));
}
@@ -156,9 +156,9 @@ public class APRIORI extends AbstractAlgorithm<AprioriResult> {
}
BitSet[] joined = join(frequentItemsets);
candidates = prune(support, joined, size);
- if(logger.isVerbose()) {
+ if(LOG.isVerbose()) {
msg.append("\ncandidates after pruning").append(Arrays.asList(candidates));
- logger.verbose(msg.toString());
+ LOG.verbose(msg.toString());
}
}
}
@@ -300,7 +300,7 @@ public class APRIORI extends AbstractAlgorithm<AprioriResult> {
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -312,32 +312,35 @@ public class APRIORI extends AbstractAlgorithm<AprioriResult> {
*/
public static class Parameterizer extends AbstractParameterizer {
/**
- * Parameter for minFreq
+ * Parameter for minFreq.
*/
protected Double minfreq = null;
/**
- * Parameter for minSupp
+ * Parameter for minSupp.
*/
protected Integer minsupp = null;
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- DoubleParameter minfreqP = new DoubleParameter(MINFREQ_ID, true);
- minfreqP.addConstraint(new IntervalConstraint(0, IntervalConstraint.IntervalBoundary.CLOSE, 1, IntervalConstraint.IntervalBoundary.CLOSE));
+ DoubleParameter minfreqP = new DoubleParameter(MINFREQ_ID);
+ minfreqP.setOptional(true);
+ minfreqP.addConstraint(new GreaterEqualConstraint(0));
+ minfreqP.addConstraint(new LessEqualConstraint(1));
if(config.grab(minfreqP)) {
minfreq = minfreqP.getValue();
}
- IntParameter minsuppP = new IntParameter(MINSUPP_ID, true);
+ IntParameter minsuppP = new IntParameter(MINSUPP_ID);
+ minsuppP.setOptional(true);
minsuppP.addConstraint(new GreaterEqualConstraint(0));
if(config.grab(minsuppP)) {
minsupp = minsuppP.getValue();
}
// global parameter constraints
- ArrayList<Parameter<?, ?>> globalConstraints = new ArrayList<Parameter<?, ?>>();
+ ArrayList<Parameter<?>> globalConstraints = new ArrayList<Parameter<?>>();
globalConstraints.add(minfreqP);
globalConstraints.add(minsuppP);
config.checkConstraint(new OnlyOneIsAllowedToBeSetGlobalConstraint(globalConstraints));
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/AbstractAlgorithm.java b/src/de/lmu/ifi/dbs/elki/algorithm/AbstractAlgorithm.java
index 30e6e226..b36df094 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/AbstractAlgorithm.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/AbstractAlgorithm.java
@@ -110,10 +110,6 @@ public abstract class AbstractAlgorithm<R extends Result> implements Algorithm {
if(runmethod1 != null) {
try {
- StringBuffer buf = new StringBuffer();
- for(Class<?> cls : signature1) {
- buf.append(cls.toString()).append(",");
- }
return (R) runmethod1.invoke(this, relations1);
}
catch(IllegalArgumentException e) {
@@ -134,10 +130,6 @@ public abstract class AbstractAlgorithm<R extends Result> implements Algorithm {
}
else if(runmethod2 != null) {
try {
- StringBuffer buf = new StringBuffer();
- for(Class<?> cls : signature1) {
- buf.append(cls.toString()).append(",");
- }
return (R) runmethod2.invoke(this, relations2);
}
catch(IllegalArgumentException e) {
@@ -174,10 +166,10 @@ public abstract class AbstractAlgorithm<R extends Result> implements Algorithm {
*
* @return the static logger
*/
- abstract protected Logging getLogger();
+ protected abstract Logging getLogger();
/**
- * Make a default distance function configuration option
+ * Make a default distance function configuration option.
*
* @param <F> Distance function type
* @param defaultDistanceFunction Default value
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/AbstractDistanceBasedAlgorithm.java b/src/de/lmu/ifi/dbs/elki/algorithm/AbstractDistanceBasedAlgorithm.java
index 70d4ba3a..e9d638dc 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/AbstractDistanceBasedAlgorithm.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/AbstractDistanceBasedAlgorithm.java
@@ -47,9 +47,9 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ObjectParameter;
*/
public abstract class AbstractDistanceBasedAlgorithm<O, D extends Distance<D>, R extends Result> extends AbstractAlgorithm<R> {
/**
- * OptionID for {@link #DISTANCE_FUNCTION_ID}
+ * OptionID for {@link #DISTANCE_FUNCTION_ID}.
*/
- public static final OptionID DISTANCE_FUNCTION_ID = OptionID.getOrCreateOptionID("algorithm.distancefunction", "Distance function to determine the distance between database objects.");
+ public static final OptionID DISTANCE_FUNCTION_ID = new OptionID("algorithm.distancefunction", "Distance function to determine the distance between database objects.");
/**
* Holds the instance of the distance function specified by
@@ -84,6 +84,9 @@ public abstract class AbstractDistanceBasedAlgorithm<O, D extends Distance<D>, R
* @apiviz.exclude
*/
public abstract static class Parameterizer<O, D extends Distance<D>> extends AbstractParameterizer {
+ /**
+ * The distance function to use.
+ */
protected DistanceFunction<O, D> distanceFunction;
@Override
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/AbstractPrimitiveDistanceBasedAlgorithm.java b/src/de/lmu/ifi/dbs/elki/algorithm/AbstractPrimitiveDistanceBasedAlgorithm.java
index 4fa12e11..7bca1931 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/AbstractPrimitiveDistanceBasedAlgorithm.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/AbstractPrimitiveDistanceBasedAlgorithm.java
@@ -45,7 +45,7 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ObjectParameter;
* @param <D> the type of Distance used by this Algorithm
* @param <R> the type of result to retrieve from this Algorithm
*/
-public abstract class AbstractPrimitiveDistanceBasedAlgorithm<O, D extends Distance<D>, R extends Result> extends AbstractAlgorithm<R> {
+public abstract class AbstractPrimitiveDistanceBasedAlgorithm<O, D extends Distance<?>, R extends Result> extends AbstractAlgorithm<R> {
/**
* Holds the instance of the distance function specified by
* {@link AbstractDistanceBasedAlgorithm#DISTANCE_FUNCTION_ID}.
@@ -79,6 +79,9 @@ public abstract class AbstractPrimitiveDistanceBasedAlgorithm<O, D extends Dista
* @apiviz.exclude
*/
public abstract static class Parameterizer<O, D extends Distance<D>> extends AbstractParameterizer {
+ /**
+ * Distance function to use.
+ */
protected PrimitiveDistanceFunction<O, D> distanceFunction;
@Override
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/Algorithm.java b/src/de/lmu/ifi/dbs/elki/algorithm/Algorithm.java
index ae221ca7..e5a4cc07 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/Algorithm.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/Algorithm.java
@@ -61,5 +61,5 @@ public interface Algorithm extends Parameterizable {
*
* @return Type restriction
*/
- public TypeInformation[] getInputTypeRestriction();
+ TypeInformation[] getInputTypeRestriction();
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/DependencyDerivator.java b/src/de/lmu/ifi/dbs/elki/algorithm/DependencyDerivator.java
index e0eabf5c..2992ae4a 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/DependencyDerivator.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/DependencyDerivator.java
@@ -34,18 +34,18 @@ import de.lmu.ifi.dbs.elki.database.Database;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
-import de.lmu.ifi.dbs.elki.database.query.knn.KNNResult;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.distancefunction.PrimitiveDistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
import de.lmu.ifi.dbs.elki.logging.Logging;
+import de.lmu.ifi.dbs.elki.math.linearalgebra.Centroid;
import de.lmu.ifi.dbs.elki.math.linearalgebra.LinearEquationSystem;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Matrix;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector;
import de.lmu.ifi.dbs.elki.math.linearalgebra.pca.PCAFilteredResult;
import de.lmu.ifi.dbs.elki.math.linearalgebra.pca.PCAFilteredRunner;
import de.lmu.ifi.dbs.elki.utilities.ClassGenericsUtil;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.utilities.FormatUtil;
import de.lmu.ifi.dbs.elki.utilities.documentation.Description;
import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
@@ -63,6 +63,7 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
* attributes of a given dataset based on a linear correlation PCA.
* </p>
*
+ * <p>
* Reference: <br>
* E. Achtert, C. Böhm, H.-P. Kriegel, P. Kröger, A. Zimek: Deriving
* Quantitative Dependencies for Correlation Clusters. <br>
@@ -76,32 +77,32 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
@Title("Dependency Derivator: Deriving numerical inter-dependencies on data")
@Description("Derives an equality-system describing dependencies between attributes in a correlation-cluster")
@Reference(authors = "E. Achtert, C. Böhm, H.-P. Kriegel, P. Kröger, A. Zimek", title = "Deriving Quantitative Dependencies for Correlation Clusters", booktitle = "Proc. 12th Int. Conf. on Knowledge Discovery and Data Mining (KDD '06), Philadelphia, PA 2006.", url = "http://dx.doi.org/10.1145/1150402.1150408")
-public class DependencyDerivator<V extends NumberVector<V, ?>, D extends Distance<D>> extends AbstractPrimitiveDistanceBasedAlgorithm<V, D, CorrelationAnalysisSolution<V>> {
+public class DependencyDerivator<V extends NumberVector<?>, D extends Distance<D>> extends AbstractPrimitiveDistanceBasedAlgorithm<V, D, CorrelationAnalysisSolution<V>> {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(DependencyDerivator.class);
+ private static final Logging LOG = Logging.getLogger(DependencyDerivator.class);
/**
* Flag to use random sample (use knn query around centroid, if flag is not
* set).
*/
- public static final OptionID DEPENDENCY_DERIVATOR_RANDOM_SAMPLE = OptionID.getOrCreateOptionID("derivator.randomSample", "Flag to use random sample (use knn query around centroid, if flag is not set).");
+ public static final OptionID DEPENDENCY_DERIVATOR_RANDOM_SAMPLE = new OptionID("derivator.randomSample", "Flag to use random sample (use knn query around centroid, if flag is not set).");
/**
* Parameter to specify the threshold for output accuracy fraction digits,
* must be an integer equal to or greater than 0.
*/
- public static final OptionID OUTPUT_ACCURACY_ID = OptionID.getOrCreateOptionID("derivator.accuracy", "Threshold for output accuracy fraction digits.");
+ public static final OptionID OUTPUT_ACCURACY_ID = new OptionID("derivator.accuracy", "Threshold for output accuracy fraction digits.");
/**
* Optional parameter to specify the threshold for the size of the random
* sample to use, must be an integer greater than 0.
- * <p/>
+ * <p>
* Default value: the size of the complete dataset
* </p>
*/
- public static final OptionID SAMPLE_SIZE_ID = OptionID.getOrCreateOptionID("derivator.sampleSize", "Threshold for the size of the random sample to use. " + "Default value is size of the complete dataset.");
+ public static final OptionID SAMPLE_SIZE_ID = new OptionID("derivator.sampleSize", "Threshold for the size of the random sample to use. " + "Default value is size of the complete dataset.");
/**
* Holds the value of {@link #SAMPLE_SIZE_ID}.
@@ -116,7 +117,7 @@ public class DependencyDerivator<V extends NumberVector<V, ?>, D extends Distanc
/**
* Number format for output of solution.
*/
- public final NumberFormat NF;
+ private final NumberFormat nf;
/**
* Flag for random sampling vs. kNN
@@ -134,7 +135,7 @@ public class DependencyDerivator<V extends NumberVector<V, ?>, D extends Distanc
*/
public DependencyDerivator(PrimitiveDistanceFunction<V, D> distanceFunction, NumberFormat nf, PCAFilteredRunner<V> pca, int sampleSize, boolean randomsample) {
super(distanceFunction);
- this.NF = nf;
+ this.nf = nf;
this.pca = pca;
this.sampleSize = sampleSize;
this.randomsample = randomsample;
@@ -150,26 +151,27 @@ public class DependencyDerivator<V extends NumberVector<V, ?>, D extends Distanc
* DependencyDerivator
*/
public CorrelationAnalysisSolution<V> run(Database database, Relation<V> relation) {
- if(logger.isVerbose()) {
- logger.verbose("retrieving database objects...");
+ if(LOG.isVerbose()) {
+ LOG.verbose("retrieving database objects...");
}
- V centroidDV = DatabaseUtil.centroid(relation);
+ Centroid centroid = Centroid.make(relation);
+ V centroidDV = centroid.toVector(relation);
DBIDs ids;
if(this.sampleSize > 0) {
if(randomsample) {
- ids = DBIDUtil.randomSample(relation.getDBIDs(), this.sampleSize, 1l);
+ ids = DBIDUtil.randomSample(relation.getDBIDs(), this.sampleSize, 1L);
}
else {
DistanceQuery<V, D> distanceQuery = database.getDistanceQuery(relation, getDistanceFunction());
KNNResult<D> queryResults = database.getKNNQuery(distanceQuery, this.sampleSize).getKNNForObject(centroidDV, this.sampleSize);
- ids = DBIDUtil.newHashSet(queryResults.asDBIDs());
+ ids = DBIDUtil.newHashSet(queryResults);
}
}
else {
ids = relation.getDBIDs();
}
- return generateModel(relation, ids, centroidDV);
+ return generateModel(relation, ids, centroid);
}
/**
@@ -181,8 +183,7 @@ public class DependencyDerivator<V extends NumberVector<V, ?>, D extends Distanc
* @return a matrix of equations describing the dependencies
*/
public CorrelationAnalysisSolution<V> generateModel(Relation<V> db, DBIDs ids) {
- V centroidDV = DatabaseUtil.centroid(db, ids);
- return generateModel(db, ids, centroidDV);
+ return generateModel(db, ids, Centroid.make(db, ids));
}
/**
@@ -190,13 +191,13 @@ public class DependencyDerivator<V extends NumberVector<V, ?>, D extends Distanc
*
* @param db the database
* @param ids the set of ids
- * @param centroidDV the centroid
+ * @param centroid the centroid
* @return a matrix of equations describing the dependencies
*/
- public CorrelationAnalysisSolution<V> generateModel(Relation<V> db, DBIDs ids, V centroidDV) {
+ public CorrelationAnalysisSolution<V> generateModel(Relation<V> db, DBIDs ids, Vector centroid) {
CorrelationAnalysisSolution<V> sol;
- if(logger.isDebuggingFine()) {
- logger.debugFine("PCA...");
+ if(LOG.isDebuggingFine()) {
+ LOG.debugFine("PCA...");
}
PCAFilteredResult pcares = pca.processIds(ids, db);
@@ -206,7 +207,6 @@ public class DependencyDerivator<V extends NumberVector<V, ?>, D extends Distanc
// Matrix strongEigenvectors =
// pca.getEigenvectors().times(pca.selectionMatrixOfStrongEigenvectors());
Matrix strongEigenvectors = pcares.getStrongEigenvectors();
- Vector centroid = centroidDV.getColumnVector();
// TODO: what if we don't have any weak eigenvectors?
if(weakEigenvectors.getColumnDimensionality() == 0) {
@@ -214,50 +214,49 @@ public class DependencyDerivator<V extends NumberVector<V, ?>, D extends Distanc
}
else {
Matrix transposedWeakEigenvectors = weakEigenvectors.transpose();
- if(logger.isDebugging()) {
+ if(LOG.isDebugging()) {
StringBuilder log = new StringBuilder();
log.append("Strong Eigenvectors:\n");
- log.append(FormatUtil.format(pcares.getEigenvectors().times(pcares.selectionMatrixOfStrongEigenvectors()), NF)).append('\n');
+ log.append(FormatUtil.format(pcares.getEigenvectors().times(pcares.selectionMatrixOfStrongEigenvectors()), nf)).append('\n');
log.append("Transposed weak Eigenvectors:\n");
- log.append(FormatUtil.format(transposedWeakEigenvectors, NF)).append('\n');
+ log.append(FormatUtil.format(transposedWeakEigenvectors, nf)).append('\n');
log.append("Eigenvalues:\n");
log.append(FormatUtil.format(pcares.getEigenvalues(), " , ", 2));
- logger.debugFine(log.toString());
+ LOG.debugFine(log.toString());
}
- Vector B = transposedWeakEigenvectors.times(centroid);
- if(logger.isDebugging()) {
+ Vector b = transposedWeakEigenvectors.times(centroid);
+ if(LOG.isDebugging()) {
StringBuilder log = new StringBuilder();
log.append("Centroid:\n").append(centroid).append('\n');
log.append("tEV * Centroid\n");
- log.append(B);
- logger.debugFine(log.toString());
+ log.append(b);
+ LOG.debugFine(log.toString());
}
// +1 == + B.getColumnDimensionality()
Matrix gaussJordan = new Matrix(transposedWeakEigenvectors.getRowDimensionality(), transposedWeakEigenvectors.getColumnDimensionality() + 1);
gaussJordan.setMatrix(0, transposedWeakEigenvectors.getRowDimensionality() - 1, 0, transposedWeakEigenvectors.getColumnDimensionality() - 1, transposedWeakEigenvectors);
- gaussJordan.setCol(transposedWeakEigenvectors.getColumnDimensionality(), B);
+ gaussJordan.setCol(transposedWeakEigenvectors.getColumnDimensionality(), b);
- if(logger.isDebuggingFiner()) {
- logger.debugFiner("Gauss-Jordan-Elimination of " + FormatUtil.format(gaussJordan, NF));
+ if(LOG.isDebuggingFiner()) {
+ LOG.debugFiner("Gauss-Jordan-Elimination of " + FormatUtil.format(gaussJordan, nf));
}
double[][] a = new double[transposedWeakEigenvectors.getRowDimensionality()][transposedWeakEigenvectors.getColumnDimensionality()];
double[][] we = transposedWeakEigenvectors.getArrayRef();
- double[] b = B.getArrayRef();
System.arraycopy(we, 0, a, 0, transposedWeakEigenvectors.getRowDimensionality());
- LinearEquationSystem lq = new LinearEquationSystem(a, b);
+ LinearEquationSystem lq = new LinearEquationSystem(a, b.getArrayRef());
lq.solveByTotalPivotSearch();
sol = new CorrelationAnalysisSolution<V>(lq, db, strongEigenvectors, pcares.getWeakEigenvectors(), pcares.similarityMatrix(), centroid);
- if(logger.isDebuggingFine()) {
+ if(LOG.isDebuggingFine()) {
StringBuilder log = new StringBuilder();
log.append("Solution:\n");
log.append("Standard deviation ").append(sol.getStandardDeviation());
- log.append(lq.equationsToString(NF.getMaximumFractionDigits()));
- logger.debugFine(log.toString());
+ log.append(lq.equationsToString(nf.getMaximumFractionDigits()));
+ LOG.debugFine(log.toString());
}
}
return sol;
@@ -270,7 +269,7 @@ public class DependencyDerivator<V extends NumberVector<V, ?>, D extends Distanc
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -280,55 +279,59 @@ public class DependencyDerivator<V extends NumberVector<V, ?>, D extends Distanc
*
* @apiviz.exclude
*/
- public static class Parameterizer<V extends NumberVector<V, ?>, D extends Distance<D>> extends AbstractPrimitiveDistanceBasedAlgorithm.Parameterizer<V, D> {
+ public static class Parameterizer<V extends NumberVector<?>, D extends Distance<D>> extends AbstractPrimitiveDistanceBasedAlgorithm.Parameterizer<V, D> {
+ /**
+ * Output accuracy.
+ */
protected int outputAccuracy = 0;
+ /**
+ * Sample size.
+ */
protected int sampleSize = 0;
+ /**
+ * Flag to enable random sampling
+ */
protected boolean randomSample = false;
+ /**
+ * Class to compute PCA with
+ */
protected PCAFilteredRunner<V> pca = null;
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- configAccuracy(config);
- configSampleSize(config);
- configRandomSampleFlag(config);
- Class<PCAFilteredRunner<V>> cls = ClassGenericsUtil.uglyCastIntoSubclass(PCAFilteredRunner.class);
- pca = config.tryInstantiate(cls);
- }
-
- public void configRandomSampleFlag(Parameterization config) {
- Flag randomSampleF = new Flag(DEPENDENCY_DERIVATOR_RANDOM_SAMPLE);
- if(config.grab(randomSampleF)) {
- randomSample = randomSampleF.getValue();
+
+ IntParameter outputAccuracyP = new IntParameter(OUTPUT_ACCURACY_ID, 4);
+ outputAccuracyP.addConstraint(new GreaterEqualConstraint(0));
+ if(config.grab(outputAccuracyP)) {
+ outputAccuracy = outputAccuracyP.getValue();
}
- }
-
- public void configSampleSize(Parameterization config) {
- IntParameter sampleSizeP = new IntParameter(SAMPLE_SIZE_ID, true);
+
+ IntParameter sampleSizeP = new IntParameter(SAMPLE_SIZE_ID);
+ sampleSizeP.setOptional(true);
sampleSizeP.addConstraint(new GreaterConstraint(0));
if(config.grab(sampleSizeP)) {
sampleSize = sampleSizeP.getValue();
}
- }
-
- public void configAccuracy(Parameterization config) {
- IntParameter outputAccuracyP = new IntParameter(OUTPUT_ACCURACY_ID, 4);
- outputAccuracyP.addConstraint(new GreaterEqualConstraint(0));
- if(config.grab(outputAccuracyP)) {
- outputAccuracy = outputAccuracyP.getValue();
+
+ Flag randomSampleF = new Flag(DEPENDENCY_DERIVATOR_RANDOM_SAMPLE);
+ if(config.grab(randomSampleF)) {
+ randomSample = randomSampleF.getValue();
}
+ Class<PCAFilteredRunner<V>> cls = ClassGenericsUtil.uglyCastIntoSubclass(PCAFilteredRunner.class);
+ pca = config.tryInstantiate(cls);
}
@Override
protected DependencyDerivator<V, D> makeInstance() {
- NumberFormat NF = NumberFormat.getInstance(Locale.US);
- NF.setMaximumFractionDigits(outputAccuracy);
- NF.setMinimumFractionDigits(outputAccuracy);
+ NumberFormat nf = NumberFormat.getInstance(Locale.US);
+ nf.setMaximumFractionDigits(outputAccuracy);
+ nf.setMinimumFractionDigits(outputAccuracy);
- return new DependencyDerivator<V, D>(distanceFunction, NF, pca, sampleSize, randomSample);
+ return new DependencyDerivator<V, D>(distanceFunction, nf, pca, sampleSize, randomSample);
}
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/DummyAlgorithm.java b/src/de/lmu/ifi/dbs/elki/algorithm/DummyAlgorithm.java
index 64188502..0f871535 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/DummyAlgorithm.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/DummyAlgorithm.java
@@ -53,15 +53,14 @@ import de.lmu.ifi.dbs.elki.utilities.documentation.Title;
*/
@Title("Dummy Algorithm")
@Description("The algorithm executes an Euclidean 10NN query on all data points, and can be used in unit testing")
-public class DummyAlgorithm<O extends NumberVector<?, ?>> extends AbstractAlgorithm<Result> {
+public class DummyAlgorithm<O extends NumberVector<?>> extends AbstractAlgorithm<Result> {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(DummyAlgorithm.class);
+ private static final Logging LOG = Logging.getLogger(DummyAlgorithm.class);
/**
- * Constructor, adhering to
- * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
+ * Constructor.
*/
public DummyAlgorithm() {
super();
@@ -96,6 +95,6 @@ public class DummyAlgorithm<O extends NumberVector<?, ?>> extends AbstractAlgori
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/KNNDistanceOrder.java b/src/de/lmu/ifi/dbs/elki/algorithm/KNNDistanceOrder.java
index 137ffadf..7e9ce77e 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/KNNDistanceOrder.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/KNNDistanceOrder.java
@@ -34,9 +34,9 @@ import de.lmu.ifi.dbs.elki.database.Database;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.database.query.knn.KNNQuery;
-import de.lmu.ifi.dbs.elki.database.query.knn.KNNResult;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.result.KNNDistanceOrderResult;
@@ -44,7 +44,7 @@ import de.lmu.ifi.dbs.elki.utilities.documentation.Description;
import de.lmu.ifi.dbs.elki.utilities.documentation.Title;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterConstraint;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.IntervalConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.LessEqualConstraint;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.DoubleParameter;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
@@ -63,13 +63,13 @@ public class KNNDistanceOrder<O, D extends Distance<D>> extends AbstractDistance
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(KNNDistanceOrder.class);
+ private static final Logging LOG = Logging.getLogger(KNNDistanceOrder.class);
/**
* Parameter to specify the distance of the k-distant object to be assessed,
* must be an integer greater than 0.
*/
- public static final OptionID K_ID = OptionID.getOrCreateOptionID("knndistanceorder.k", "Specifies the distance of the k-distant object to be assessed.");
+ public static final OptionID K_ID = new OptionID("knndistanceorder.k", "Specifies the distance of the k-distant object to be assessed.");
/**
* Holds the value of {@link #K_ID}.
@@ -81,7 +81,7 @@ public class KNNDistanceOrder<O, D extends Distance<D>> extends AbstractDistance
* be provided in the result, must be a double greater than 0 and less than or
* equal to 1.
*/
- public static final OptionID PERCENTAGE_ID = OptionID.getOrCreateOptionID("knndistanceorder.percentage", "The average percentage of distances randomly choosen to be provided in the result.");
+ public static final OptionID PERCENTAGE_ID = new OptionID("knndistanceorder.percentage", "The average percentage of distances randomly choosen to be provided in the result.");
/**
* Holds the value of {@link #PERCENTAGE_ID}.
@@ -132,7 +132,7 @@ public class KNNDistanceOrder<O, D extends Distance<D>> extends AbstractDistance
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -143,10 +143,19 @@ public class KNNDistanceOrder<O, D extends Distance<D>> extends AbstractDistance
* @apiviz.exclude
*/
public static class Parameterizer<O, D extends Distance<D>> extends AbstractDistanceBasedAlgorithm.Parameterizer<O, D> {
+ /**
+ * Parameter k.
+ */
protected int k;
+ /**
+ * Percentage.
+ */
protected double percentage;
+ /**
+ * Constructor.
+ */
public Parameterizer() {
super();
}
@@ -161,7 +170,8 @@ public class KNNDistanceOrder<O, D extends Distance<D>> extends AbstractDistance
}
DoubleParameter percentageP = new DoubleParameter(PERCENTAGE_ID, 1.0);
- percentageP.addConstraint(new IntervalConstraint(0, IntervalConstraint.IntervalBoundary.OPEN, 1, IntervalConstraint.IntervalBoundary.CLOSE));
+ percentageP.addConstraint(new GreaterConstraint(0));
+ percentageP.addConstraint(new LessEqualConstraint(1));
if(config.grab(percentageP)) {
percentage = percentageP.getValue();
}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/KNNJoin.java b/src/de/lmu/ifi/dbs/elki/algorithm/KNNJoin.java
index 3eb789c7..9e73d959 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/KNNJoin.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/KNNJoin.java
@@ -37,14 +37,16 @@ import de.lmu.ifi.dbs.elki.database.datastore.DataStoreUtil;
import de.lmu.ifi.dbs.elki.database.datastore.WritableDataStore;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
-import de.lmu.ifi.dbs.elki.database.query.DoubleDistanceResultPair;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.DistanceUtil;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
import de.lmu.ifi.dbs.elki.distance.distancefunction.SpatialPrimitiveDistanceFunction;
import de.lmu.ifi.dbs.elki.distance.distancefunction.SpatialPrimitiveDoubleDistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DoubleDistanceKNNHeap;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNHeap;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNUtil;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
-import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
import de.lmu.ifi.dbs.elki.index.tree.LeafEntry;
import de.lmu.ifi.dbs.elki.index.tree.spatial.SpatialEntry;
import de.lmu.ifi.dbs.elki.index.tree.spatial.SpatialIndexTree;
@@ -55,8 +57,6 @@ import de.lmu.ifi.dbs.elki.logging.progress.FiniteProgress;
import de.lmu.ifi.dbs.elki.logging.progress.IndefiniteProgress;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.Heap;
-import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.KNNHeap;
-import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.KNNList;
import de.lmu.ifi.dbs.elki.utilities.documentation.Description;
import de.lmu.ifi.dbs.elki.utilities.documentation.Title;
import de.lmu.ifi.dbs.elki.utilities.exceptions.AbortException;
@@ -84,20 +84,20 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
*/
@Title("K-Nearest Neighbor Join")
@Description("Algorithm to find the k-nearest neighbors of each object in a spatial database")
-public class KNNJoin<V extends NumberVector<V, ?>, D extends Distance<D>, N extends SpatialNode<N, E>, E extends SpatialEntry> extends AbstractDistanceBasedAlgorithm<V, D, DataStore<KNNList<D>>> {
+public class KNNJoin<V extends NumberVector<?>, D extends Distance<D>, N extends SpatialNode<N, E>, E extends SpatialEntry> extends AbstractDistanceBasedAlgorithm<V, D, DataStore<KNNResult<D>>> {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(KNNJoin.class);
+ private static final Logging LOG = Logging.getLogger(KNNJoin.class);
/**
* Parameter that specifies the k-nearest neighbors to be assigned, must be an
* integer greater than 0. Default value: 1.
*/
- public static final OptionID K_ID = OptionID.getOrCreateOptionID("knnjoin.k", "Specifies the k-nearest neighbors to be assigned.");
+ public static final OptionID K_ID = new OptionID("knnjoin.k", "Specifies the k-nearest neighbors to be assigned.");
/**
- * The k parameter
+ * The k parameter.
*/
int k;
@@ -120,12 +120,12 @@ public class KNNJoin<V extends NumberVector<V, ?>, D extends Distance<D>, N exte
* @return result
*/
@SuppressWarnings("unchecked")
- public WritableDataStore<KNNList<D>> run(Database database, Relation<V> relation) {
- if(!(getDistanceFunction() instanceof SpatialPrimitiveDistanceFunction)) {
+ public WritableDataStore<KNNResult<D>> run(Database database, Relation<V> relation) {
+ if (!(getDistanceFunction() instanceof SpatialPrimitiveDistanceFunction)) {
throw new IllegalStateException("Distance Function must be an instance of " + SpatialPrimitiveDistanceFunction.class.getName());
}
Collection<SpatialIndexTree<N, E>> indexes = ResultUtil.filterResults(database, SpatialIndexTree.class);
- if(indexes.size() != 1) {
+ if (indexes.size() != 1) {
throw new AbortException("KNNJoin found " + indexes.size() + " spatial indexes, expected exactly one.");
}
// FIXME: Ensure were looking at the right relation!
@@ -133,9 +133,6 @@ public class KNNJoin<V extends NumberVector<V, ?>, D extends Distance<D>, N exte
SpatialPrimitiveDistanceFunction<V, D> distFunction = (SpatialPrimitiveDistanceFunction<V, D>) getDistanceFunction();
DBIDs ids = relation.getDBIDs();
- // Optimize for double?
- final boolean doubleOptimize = (getDistanceFunction() instanceof SpatialPrimitiveDoubleDistanceFunction);
-
// data pages
List<E> ps_candidates = new ArrayList<E>(index.getLeaves());
// knn heaps
@@ -143,50 +140,49 @@ public class KNNJoin<V extends NumberVector<V, ?>, D extends Distance<D>, N exte
Heap<Task> pq = new Heap<Task>(ps_candidates.size() * ps_candidates.size() / 10);
// Initialize with the page self-pairing
- for(int i = 0; i < ps_candidates.size(); i++) {
+ for (int i = 0; i < ps_candidates.size(); i++) {
E pr_entry = ps_candidates.get(i);
N pr = index.getNode(pr_entry);
- heaps.add(initHeaps(distFunction, doubleOptimize, pr));
+ heaps.add(initHeaps(distFunction, pr));
}
// Build priority queue
- final int sqsize = ps_candidates.size() * (ps_candidates.size() - 1) / 2;
- if(logger.isDebuggingFine()) {
- logger.debugFine("Number of leaves: " + ps_candidates.size() + " so " + sqsize + " MBR computations.");
+ final int sqsize = ps_candidates.size() * (ps_candidates.size() - 1) >> 1;
+ if (LOG.isDebuggingFine()) {
+ LOG.debugFine("Number of leaves: " + ps_candidates.size() + " so " + sqsize + " MBR computations.");
}
- FiniteProgress mprogress = logger.isVerbose() ? new FiniteProgress("Comparing leaf MBRs", sqsize, logger) : null;
- for(int i = 0; i < ps_candidates.size(); i++) {
+ FiniteProgress mprogress = LOG.isVerbose() ? new FiniteProgress("Comparing leaf MBRs", sqsize, LOG) : null;
+ for (int i = 0; i < ps_candidates.size(); i++) {
E pr_entry = ps_candidates.get(i);
List<KNNHeap<D>> pr_heaps = heaps.get(i);
D pr_knn_distance = computeStopDistance(pr_heaps);
- for(int j = i + 1; j < ps_candidates.size(); j++) {
+ for (int j = i + 1; j < ps_candidates.size(); j++) {
E ps_entry = ps_candidates.get(j);
List<KNNHeap<D>> ps_heaps = heaps.get(j);
D ps_knn_distance = computeStopDistance(ps_heaps);
D minDist = distFunction.minDist(pr_entry, ps_entry);
// Resolve immediately:
- if(minDist.isNullDistance()) {
+ if (minDist.isNullDistance()) {
N pr = index.getNode(ps_candidates.get(i));
N ps = index.getNode(ps_candidates.get(j));
- processDataPagesOptimize(distFunction, doubleOptimize, pr_heaps, ps_heaps, pr, ps);
- }
- else if(minDist.compareTo(pr_knn_distance) <= 0 || minDist.compareTo(ps_knn_distance) <= 0) {
+ processDataPagesOptimize(distFunction, pr_heaps, ps_heaps, pr, ps);
+ } else if (minDist.compareTo(pr_knn_distance) <= 0 || minDist.compareTo(ps_knn_distance) <= 0) {
pq.add(new Task(minDist, i, j));
}
- if(mprogress != null) {
- mprogress.incrementProcessed(logger);
+ if (mprogress != null) {
+ mprogress.incrementProcessed(LOG);
}
}
}
- if(mprogress != null) {
- mprogress.ensureCompleted(logger);
+ if (mprogress != null) {
+ mprogress.ensureCompleted(LOG);
}
// Process the queue
- FiniteProgress qprogress = logger.isVerbose() ? new FiniteProgress("Processing queue", pq.size(), logger) : null;
- IndefiniteProgress fprogress = logger.isVerbose() ? new IndefiniteProgress("Full comparisons", logger) : null;
- while(!pq.isEmpty()) {
+ FiniteProgress qprogress = LOG.isVerbose() ? new FiniteProgress("Processing queue", pq.size(), LOG) : null;
+ IndefiniteProgress fprogress = LOG.isVerbose() ? new IndefiniteProgress("Full comparisons", LOG) : null;
+ while (!pq.isEmpty()) {
Task task = pq.poll();
List<KNNHeap<D>> pr_heaps = heaps.get(task.i);
List<KNNHeap<D>> ps_heaps = heaps.get(task.j);
@@ -194,47 +190,45 @@ public class KNNJoin<V extends NumberVector<V, ?>, D extends Distance<D>, N exte
D ps_knn_distance = computeStopDistance(ps_heaps);
boolean dor = task.mindist.compareTo(pr_knn_distance) <= 0;
boolean dos = task.mindist.compareTo(ps_knn_distance) <= 0;
- if(dor || dos) {
+ if (dor || dos) {
N pr = index.getNode(ps_candidates.get(task.i));
N ps = index.getNode(ps_candidates.get(task.j));
- if(dor && dos) {
- processDataPagesOptimize(distFunction, doubleOptimize, pr_heaps, ps_heaps, pr, ps);
- }
- else {
- if(dor) {
- processDataPagesOptimize(distFunction, doubleOptimize, pr_heaps, null, pr, ps);
- }
- else /* dos */{
- processDataPagesOptimize(distFunction, doubleOptimize, ps_heaps, null, ps, pr);
+ if (dor && dos) {
+ processDataPagesOptimize(distFunction, pr_heaps, ps_heaps, pr, ps);
+ } else {
+ if (dor) {
+ processDataPagesOptimize(distFunction, pr_heaps, null, pr, ps);
+ } else /* dos */{
+ processDataPagesOptimize(distFunction, ps_heaps, null, ps, pr);
}
}
- if(fprogress != null) {
- fprogress.incrementProcessed(logger);
+ if (fprogress != null) {
+ fprogress.incrementProcessed(LOG);
}
}
- if(qprogress != null) {
- qprogress.incrementProcessed(logger);
+ if (qprogress != null) {
+ qprogress.incrementProcessed(LOG);
}
}
- if(qprogress != null) {
- qprogress.ensureCompleted(logger);
+ if (qprogress != null) {
+ qprogress.ensureCompleted(LOG);
}
- if(fprogress != null) {
- fprogress.setCompleted(logger);
+ if (fprogress != null) {
+ fprogress.setCompleted(LOG);
}
- WritableDataStore<KNNList<D>> knnLists = DataStoreUtil.makeStorage(ids, DataStoreFactory.HINT_STATIC, KNNList.class);
+ WritableDataStore<KNNResult<D>> knnLists = DataStoreUtil.makeStorage(ids, DataStoreFactory.HINT_STATIC, KNNResult.class);
// FiniteProgress progress = logger.isVerbose() ? new
// FiniteProgress(this.getClass().getName(), relation.size(), logger) :
// null;
- FiniteProgress pageprog = logger.isVerbose() ? new FiniteProgress("Number of processed data pages", ps_candidates.size(), logger) : null;
+ FiniteProgress pageprog = LOG.isVerbose() ? new FiniteProgress("Number of processed data pages", ps_candidates.size(), LOG) : null;
// int processed = 0;
- for(int i = 0; i < ps_candidates.size(); i++) {
+ for (int i = 0; i < ps_candidates.size(); i++) {
N pr = index.getNode(ps_candidates.get(i));
List<KNNHeap<D>> pr_heaps = heaps.get(i);
// Finalize lists
- for(int j = 0; j < pr.getNumEntries(); j++) {
+ for (int j = 0; j < pr.getNumEntries(); j++) {
knnLists.put(((LeafEntry) pr.getEntry(j)).getDBID(), pr_heaps.get(j).toKNNList());
}
// Forget heaps and pq
@@ -244,29 +238,35 @@ public class KNNJoin<V extends NumberVector<V, ?>, D extends Distance<D>, N exte
// if(progress != null) {
// progress.setProcessed(processed, logger);
// }
- if(pageprog != null) {
- pageprog.incrementProcessed(logger);
+ if (pageprog != null) {
+ pageprog.incrementProcessed(LOG);
}
}
// if(progress != null) {
// progress.ensureCompleted(logger);
// }
- if(pageprog != null) {
- pageprog.ensureCompleted(logger);
+ if (pageprog != null) {
+ pageprog.ensureCompleted(LOG);
}
return knnLists;
}
- private List<KNNHeap<D>> initHeaps(SpatialPrimitiveDistanceFunction<V, D> distFunction, final boolean doubleOptimize, N pr) {
- List<KNNHeap<D>> pr_heaps;
+ /**
+ * Initialize the heaps.
+ *
+ * @param distFunction Distance function
+ * @param pr Node to initialize for
+ * @return List of heaps
+ */
+ private List<KNNHeap<D>> initHeaps(SpatialPrimitiveDistanceFunction<V, D> distFunction, N pr) {
+ List<KNNHeap<D>> pr_heaps = new ArrayList<KNNHeap<D>>(pr.getNumEntries());
// Create for each data object a knn heap
- pr_heaps = new ArrayList<KNNHeap<D>>(pr.getNumEntries());
- for(int j = 0; j < pr.getNumEntries(); j++) {
- pr_heaps.add(new KNNHeap<D>(k, distFunction.getDistanceFactory().infiniteDistance()));
+ for (int j = 0; j < pr.getNumEntries(); j++) {
+ pr_heaps.add(KNNUtil.newHeap(distFunction, k));
}
// Self-join first, as this is expected to improve most and cannot be
// pruned.
- processDataPagesOptimize(distFunction, doubleOptimize, pr_heaps, null, pr, pr);
+ processDataPagesOptimize(distFunction, pr_heaps, null, pr, pr);
return pr_heaps;
}
@@ -275,27 +275,26 @@ public class KNNJoin<V extends NumberVector<V, ?>, D extends Distance<D>, N exte
* neighbors of pr in ps.
*
* @param distFunction the distance to use
- * @param doubleOptimize Flag whether to optimize for doubles.
* @param pr the first data page
* @param ps the second data page
* @param pr_heaps the knn lists for each data object in pr
* @param ps_heaps the knn lists for each data object in ps (if ps != pr)
*/
- private void processDataPagesOptimize(SpatialPrimitiveDistanceFunction<V, D> distFunction, final boolean doubleOptimize, List<KNNHeap<D>> pr_heaps, List<KNNHeap<D>> ps_heaps, N pr, N ps) {
- if(doubleOptimize) {
+ @SuppressWarnings("unchecked")
+ private void processDataPagesOptimize(SpatialPrimitiveDistanceFunction<V, D> distFunction, List<? extends KNNHeap<D>> pr_heaps, List<? extends KNNHeap<D>> ps_heaps, N pr, N ps) {
+ if (DistanceUtil.isDoubleDistanceFunction(distFunction)) {
List<?> khp = (List<?>) pr_heaps;
List<?> khs = (List<?>) ps_heaps;
- processDataPagesDouble((SpatialPrimitiveDoubleDistanceFunction<? super V>) distFunction, pr, ps, (List<KNNHeap<DoubleDistance>>) khp, (List<KNNHeap<DoubleDistance>>) khs);
- }
- else {
- for(int j = 0; j < ps.getNumEntries(); j++) {
+ processDataPagesDouble((SpatialPrimitiveDoubleDistanceFunction<? super V>) distFunction, pr, ps, (List<DoubleDistanceKNNHeap>) khp, (List<DoubleDistanceKNNHeap>) khs);
+ } else {
+ for (int j = 0; j < ps.getNumEntries(); j++) {
final SpatialPointLeafEntry s_e = (SpatialPointLeafEntry) ps.getEntry(j);
DBID s_id = s_e.getDBID();
- for(int i = 0; i < pr.getNumEntries(); i++) {
+ for (int i = 0; i < pr.getNumEntries(); i++) {
final SpatialPointLeafEntry r_e = (SpatialPointLeafEntry) pr.getEntry(i);
D distance = distFunction.minDist(s_e, r_e);
pr_heaps.get(i).add(distance, s_id);
- if(pr != ps && ps_heaps != null) {
+ if (pr != ps && ps_heaps != null) {
ps_heaps.get(j).add(distance, r_e.getDBID());
}
}
@@ -313,40 +312,42 @@ public class KNNJoin<V extends NumberVector<V, ?>, D extends Distance<D>, N exte
* @param pr_heaps the knn lists for each data object
* @param ps_heaps the knn lists for each data object in ps
*/
- private void processDataPagesDouble(SpatialPrimitiveDoubleDistanceFunction<? super V> df, N pr, N ps, List<KNNHeap<DoubleDistance>> pr_heaps, List<KNNHeap<DoubleDistance>> ps_heaps) {
+ private void processDataPagesDouble(SpatialPrimitiveDoubleDistanceFunction<? super V> df, N pr, N ps, List<DoubleDistanceKNNHeap> pr_heaps, List<DoubleDistanceKNNHeap> ps_heaps) {
// Compare pairwise
- for(int j = 0; j < ps.getNumEntries(); j++) {
+ for (int j = 0; j < ps.getNumEntries(); j++) {
final SpatialPointLeafEntry s_e = (SpatialPointLeafEntry) ps.getEntry(j);
DBID s_id = s_e.getDBID();
- for(int i = 0; i < pr.getNumEntries(); i++) {
+ for (int i = 0; i < pr.getNumEntries(); i++) {
final SpatialPointLeafEntry r_e = (SpatialPointLeafEntry) pr.getEntry(i);
double distance = df.doubleMinDist(s_e, r_e);
- pr_heaps.get(i).add(new DoubleDistanceResultPair(distance, s_id));
- if(pr != ps && ps_heaps != null) {
- ps_heaps.get(j).add(new DoubleDistanceResultPair(distance, r_e.getDBID()));
+ pr_heaps.get(i).add(distance, s_id);
+ if (pr != ps && ps_heaps != null) {
+ ps_heaps.get(j).add(distance, r_e.getDBID());
}
}
}
}
/**
- * Compute the maximum stop distance
+ * Compute the maximum stop distance.
*
- * @param heaps
+ * @param heaps Heaps list
* @return the k-nearest neighbor distance of pr in ps
*/
private D computeStopDistance(List<KNNHeap<D>> heaps) {
// Update pruning distance
D pr_knn_distance = null;
- for(KNNHeap<D> knnList : heaps) {
+ for (KNNHeap<D> knnList : heaps) {
// set kNN distance of r
- if(pr_knn_distance == null) {
+ if (pr_knn_distance == null) {
pr_knn_distance = knnList.getKNNDistance();
- }
- else {
+ } else {
pr_knn_distance = DistanceUtil.max(knnList.getKNNDistance(), pr_knn_distance);
}
}
+ if (pr_knn_distance == null) {
+ return getDistanceFunction().getDistanceFactory().infiniteDistance();
+ }
return pr_knn_distance;
}
@@ -357,29 +358,38 @@ public class KNNJoin<V extends NumberVector<V, ?>, D extends Distance<D>, N exte
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
- * Task in the processing queue
+ * Task in the processing queue.
*
* @author Erich Schubert
*
* @apiviz.exclude
*/
private class Task implements Comparable<Task> {
+ /**
+ * Minimum distance.
+ */
final D mindist;
+ /**
+ * First offset.
+ */
final int i;
+ /**
+ * Second offset.
+ */
final int j;
/**
* Constructor.
*
- * @param mindist
- * @param i
- * @param j
+ * @param mindist Minimum distance
+ * @param i First offset
+ * @param j Second offset
*/
public Task(D mindist, int i, int j) {
super();
@@ -401,7 +411,10 @@ public class KNNJoin<V extends NumberVector<V, ?>, D extends Distance<D>, N exte
*
* @apiviz.exclude
*/
- public static class Parameterizer<V extends NumberVector<V, ?>, D extends Distance<D>, N extends SpatialNode<N, E>, E extends SpatialEntry> extends AbstractPrimitiveDistanceBasedAlgorithm.Parameterizer<V, D> {
+ public static class Parameterizer<V extends NumberVector<?>, D extends Distance<D>, N extends SpatialNode<N, E>, E extends SpatialEntry> extends AbstractPrimitiveDistanceBasedAlgorithm.Parameterizer<V, D> {
+ /**
+ * K parameter.
+ */
protected int k;
@Override
@@ -409,7 +422,7 @@ public class KNNJoin<V extends NumberVector<V, ?>, D extends Distance<D>, N exte
super.makeOptions(config);
IntParameter kP = new IntParameter(K_ID, 1);
kP.addConstraint(new GreaterConstraint(0));
- if(config.grab(kP)) {
+ if (config.grab(kP)) {
k = kP.getValue();
}
}
@@ -419,4 +432,4 @@ public class KNNJoin<V extends NumberVector<V, ?>, D extends Distance<D>, N exte
return new KNNJoin<V, D, N, E>(distanceFunction, k);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/MaterializeDistances.java b/src/de/lmu/ifi/dbs/elki/algorithm/MaterializeDistances.java
index b09f7ac2..95a2a2b9 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/MaterializeDistances.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/MaterializeDistances.java
@@ -31,6 +31,7 @@ import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
import de.lmu.ifi.dbs.elki.database.Database;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
@@ -52,13 +53,14 @@ import de.lmu.ifi.dbs.elki.utilities.pairs.CTriple;
* @param <O> Object type
* @param <D> Distance type
*/
+// TODO: use DBIDPair -> D map?
@Title("MaterializeDistances")
@Description("Materialize all distances in the data set to use as cached/precalculated data.")
public class MaterializeDistances<O, D extends NumberDistance<D, ?>> extends AbstractDistanceBasedAlgorithm<O, D, CollectionResult<CTriple<DBID, DBID, Double>>> {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(MaterializeDistances.class);
+ private static final Logging LOG = Logging.getLogger(MaterializeDistances.class);
/**
* Constructor.
@@ -71,21 +73,25 @@ public class MaterializeDistances<O, D extends NumberDistance<D, ?>> extends Abs
/**
* Iterates over all points in the database.
+ *
+ * @param database Database to process
+ * @param relation Relation to process
+ * @return Distance matrix
*/
public CollectionResult<CTriple<DBID, DBID, Double>> run(Database database, Relation<O> relation) {
DistanceQuery<O, D> distFunc = database.getDistanceQuery(relation, getDistanceFunction());
final int size = relation.size();
- Collection<CTriple<DBID, DBID, Double>> r = new ArrayList<CTriple<DBID, DBID, Double>>(size * (size + 1) / 2);
+ Collection<CTriple<DBID, DBID, Double>> r = new ArrayList<CTriple<DBID, DBID, Double>>(size * (size + 1) >> 1);
for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
for(DBIDIter iditer2 = relation.iterDBIDs(); iditer2.valid(); iditer2.advance()) {
// skip inverted pairs
- if(iditer2.compareDBID(iditer) > 0) {
+ if(DBIDUtil.compare(iditer2, iditer) > 0) {
continue;
}
double d = distFunc.distance(iditer, iditer2).doubleValue();
- r.add(new CTriple<DBID, DBID, Double>(iditer.getDBID(), iditer2.getDBID(), d));
+ r.add(new CTriple<DBID, DBID, Double>(DBIDUtil.deref(iditer), DBIDUtil.deref(iditer2), d));
}
}
return new CollectionResult<CTriple<DBID, DBID, Double>>("Distance Matrix", "distance-matrix", r);
@@ -93,7 +99,7 @@ public class MaterializeDistances<O, D extends NumberDistance<D, ?>> extends Abs
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
@Override
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/NullAlgorithm.java b/src/de/lmu/ifi/dbs/elki/algorithm/NullAlgorithm.java
index 490d79fb..abd4c963 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/NullAlgorithm.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/NullAlgorithm.java
@@ -43,7 +43,7 @@ public class NullAlgorithm extends AbstractAlgorithm<Result> {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(NullAlgorithm.class);
+ private static final Logging LOG = Logging.getLogger(NullAlgorithm.class);
/**
* Constructor.
@@ -59,7 +59,7 @@ public class NullAlgorithm extends AbstractAlgorithm<Result> {
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
@Override
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/benchmark/KNNBenchmarkAlgorithm.java b/src/de/lmu/ifi/dbs/elki/algorithm/benchmark/KNNBenchmarkAlgorithm.java
new file mode 100644
index 00000000..b0ea8cc1
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/benchmark/KNNBenchmarkAlgorithm.java
@@ -0,0 +1,303 @@
+package de.lmu.ifi.dbs.elki.algorithm.benchmark;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import de.lmu.ifi.dbs.elki.algorithm.AbstractDistanceBasedAlgorithm;
+import de.lmu.ifi.dbs.elki.data.type.TypeInformation;
+import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
+import de.lmu.ifi.dbs.elki.database.Database;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRange;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
+import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
+import de.lmu.ifi.dbs.elki.database.query.knn.KNNQuery;
+import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.datasource.DatabaseConnection;
+import de.lmu.ifi.dbs.elki.datasource.bundle.MultipleObjectsBundle;
+import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
+import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
+import de.lmu.ifi.dbs.elki.logging.Logging;
+import de.lmu.ifi.dbs.elki.logging.progress.FiniteProgress;
+import de.lmu.ifi.dbs.elki.math.MeanVariance;
+import de.lmu.ifi.dbs.elki.result.Result;
+import de.lmu.ifi.dbs.elki.utilities.RandomFactory;
+import de.lmu.ifi.dbs.elki.utilities.Util;
+import de.lmu.ifi.dbs.elki.utilities.exceptions.AbortException;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.DoubleParameter;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ObjectParameter;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.RandomParameter;
+
+/**
+ * Benchmarking algorithm that computes the k nearest neighbors for each query
+ * point. The query points can either come from a separate data source, or from
+ * the original database.
+ *
+ * @author Erich Schubert
+ *
+ * @param <O> Object type
+ *
+ * @apiviz.uses KNNQuery
+ */
+public class KNNBenchmarkAlgorithm<O, D extends Distance<D>> extends AbstractDistanceBasedAlgorithm<O, D, Result> {
+ /**
+ * The logger for this class.
+ */
+ private static final Logging LOG = Logging.getLogger(KNNBenchmarkAlgorithm.class);
+
+ /**
+ * Number of neighbors to retrieve.
+ */
+ protected int k = 10;
+
+ /**
+ * The alternate query point source. Optional.
+ */
+ protected DatabaseConnection queries = null;
+
+ /**
+ * Sampling size.
+ */
+ protected double sampling = -1;
+
+ /**
+ * Random generator factory
+ */
+ protected RandomFactory random;
+
+ /**
+ * Constructor.
+ *
+ * @param distanceFunction Distance function to use
+ * @param k K parameter
+ * @param queries Query data set (may be null!)
+ * @param sampling Sampling rate
+ * @param random Random factory
+ */
+ public KNNBenchmarkAlgorithm(DistanceFunction<? super O, D> distanceFunction, int k, DatabaseConnection queries, double sampling, RandomFactory random) {
+ super(distanceFunction);
+ this.k = k;
+ this.queries = queries;
+ this.sampling = sampling;
+ this.random = random;
+ }
+
+ /**
+ * Run the algorithm.
+ *
+ * @param database Database
+ * @param relation Relation
+ * @return Null result
+ */
+ public Result run(Database database, Relation<O> relation) {
+ // Get a distance and kNN query instance.
+ DistanceQuery<O, D> distQuery = database.getDistanceQuery(relation, getDistanceFunction());
+ KNNQuery<O, D> knnQuery = database.getKNNQuery(distQuery, 10);
+
+ // No query set - use original database.
+ if (queries == null) {
+ final DBIDs sample;
+ if (sampling <= 0) {
+ sample = relation.getDBIDs();
+ } else if (sampling < 1.1) {
+ int size = (int) Math.min(sampling * relation.size(), relation.size());
+ sample = DBIDUtil.randomSample(relation.getDBIDs(), size, random);
+ } else {
+ int size = (int) Math.min(sampling, relation.size());
+ sample = DBIDUtil.randomSample(relation.getDBIDs(), size, random);
+ }
+ FiniteProgress prog = LOG.isVerbose() ? new FiniteProgress("kNN queries", sample.size(), LOG) : null;
+ int hash = 0;
+ MeanVariance mv = new MeanVariance();
+ for (DBIDIter iditer = sample.iter(); iditer.valid(); iditer.advance()) {
+ KNNResult<D> knns = knnQuery.getKNNForDBID(iditer, k);
+ int ichecksum = 0;
+ for (DBIDIter it = knns.iter(); it.valid(); it.advance()) {
+ ichecksum += it.internalGetIndex();
+ }
+ hash = Util.mixHashCodes(hash, ichecksum);
+ mv.put(knns.size());
+ if (prog != null) {
+ prog.incrementProcessed(LOG);
+ }
+ }
+ if (prog != null) {
+ prog.ensureCompleted(LOG);
+ }
+ if (LOG.isVerbose()) {
+ LOG.verbose("Result hashcode: " + hash);
+ LOG.verbose("Mean number of results: "+mv.toString());
+ }
+ } else {
+ // Separate query set.
+ TypeInformation res = getDistanceFunction().getInputTypeRestriction();
+ MultipleObjectsBundle bundle = queries.loadData();
+ int col = -1;
+ for (int i = 0; i < bundle.metaLength(); i++) {
+ if (res.isAssignableFromType(bundle.meta(i))) {
+ col = i;
+ break;
+ }
+ }
+ if (col < 0) {
+ throw new AbortException("No compatible data type in query input was found. Expected: " + res.toString());
+ }
+ // Random sampling is a bit of hack, sorry.
+ // But currently, we don't (yet) have an "integer random sample" function.
+ DBIDRange sids = DBIDUtil.generateStaticDBIDRange(bundle.dataLength());
+
+ final DBIDs sample;
+ if (sampling <= 0) {
+ sample = sids;
+ } else if (sampling < 1.1) {
+ int size = (int) Math.min(sampling * relation.size(), relation.size());
+ sample = DBIDUtil.randomSample(sids, size, random);
+ } else {
+ int size = (int) Math.min(sampling, sids.size());
+ sample = DBIDUtil.randomSample(sids, size, random);
+ }
+ FiniteProgress prog = LOG.isVerbose() ? new FiniteProgress("kNN queries", sample.size(), LOG) : null;
+ int hash = 0;
+ MeanVariance mv = new MeanVariance();
+ for (DBIDIter iditer = sample.iter(); iditer.valid(); iditer.advance()) {
+ int off = sids.binarySearch(iditer);
+ assert (off >= 0);
+ @SuppressWarnings("unchecked")
+ O o = (O) bundle.data(off, col);
+ KNNResult<D> knns = knnQuery.getKNNForObject(o, k);
+ int ichecksum = 0;
+ for (DBIDIter it = knns.iter(); it.valid(); it.advance()) {
+ ichecksum += it.internalGetIndex();
+ }
+ hash = Util.mixHashCodes(hash, ichecksum);
+ mv.put(knns.size());
+ if (prog != null) {
+ prog.incrementProcessed(LOG);
+ }
+ if (LOG.isVerbose()) {
+ LOG.verbose("Result hashcode: " + hash);
+ LOG.verbose("Mean number of results: "+mv.toString());
+ }
+ }
+ if (prog != null) {
+ prog.ensureCompleted(LOG);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public TypeInformation[] getInputTypeRestriction() {
+ return TypeUtil.array(getDistanceFunction().getInputTypeRestriction());
+ }
+
+ @Override
+ protected Logging getLogger() {
+ return LOG;
+ }
+
+ /**
+ * Parameterization class
+ *
+ * @apiviz.exclude
+ *
+ * @author Erich Schubert
+ *
+ * @param <O> Object type
+ * @param <D> Distance type
+ */
+ public static class Parameterizer<O, D extends Distance<D>> extends AbstractDistanceBasedAlgorithm.Parameterizer<O, D> {
+ /**
+ * Parameter for the number of neighbors.
+ */
+ public static final OptionID K_ID = new OptionID("knnbench.k", "Number of neighbors to retreive for kNN benchmarking.");
+
+ /**
+ * Parameter for the query dataset.
+ */
+ public static final OptionID QUERY_ID = new OptionID("knnbench.query", "Data source for the queries. If not set, the queries are taken from the database.");
+
+ /**
+ * Parameter for the sampling size.
+ */
+ public static final OptionID SAMPLING_ID = new OptionID("knnbench.sampling", "Sampling size parameter. If the value is less or equal 1, it is assumed to be the relative share. Larger values will be interpreted as integer sizes. By default, all data will be used.");
+
+ /**
+ * Parameter for the random generator
+ */
+ public static final OptionID RANDOM_ID = new OptionID("knnbench.random", "Random generator for sampling.");
+
+ /**
+ * K parameter
+ */
+ protected int k = 10;
+
+ /**
+ * The alternate query point source. Optional.
+ */
+ protected DatabaseConnection queries = null;
+
+ /**
+ * Sampling size.
+ */
+ protected double sampling = -1;
+
+ /**
+ * Random generator factory
+ */
+ protected RandomFactory random;
+
+ @Override
+ protected void makeOptions(Parameterization config) {
+ super.makeOptions(config);
+ IntParameter kP = new IntParameter(K_ID);
+ if (config.grab(kP)) {
+ k = kP.intValue();
+ }
+ ObjectParameter<DatabaseConnection> queryP = new ObjectParameter<DatabaseConnection>(QUERY_ID, DatabaseConnection.class);
+ queryP.setOptional(true);
+ if (config.grab(queryP)) {
+ queries = queryP.instantiateClass(config);
+ }
+ DoubleParameter samplingP = new DoubleParameter(SAMPLING_ID);
+ samplingP.setOptional(true);
+ if (config.grab(samplingP)) {
+ sampling = samplingP.doubleValue();
+ }
+ RandomParameter randomP = new RandomParameter(RANDOM_ID, RandomFactory.DEFAULT);
+ if (config.grab(randomP)) {
+ random = randomP.getValue();
+ }
+ }
+
+ @Override
+ protected KNNBenchmarkAlgorithm<O, D> makeInstance() {
+ return new KNNBenchmarkAlgorithm<O, D>(distanceFunction, k, queries, sampling, random);
+ }
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/benchmark/RangeQueryBenchmarkAlgorithm.java b/src/de/lmu/ifi/dbs/elki/algorithm/benchmark/RangeQueryBenchmarkAlgorithm.java
new file mode 100644
index 00000000..f483321d
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/benchmark/RangeQueryBenchmarkAlgorithm.java
@@ -0,0 +1,357 @@
+package de.lmu.ifi.dbs.elki.algorithm.benchmark;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import de.lmu.ifi.dbs.elki.algorithm.AbstractDistanceBasedAlgorithm;
+import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.data.NumberVector.Factory;
+import de.lmu.ifi.dbs.elki.data.type.TypeInformation;
+import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
+import de.lmu.ifi.dbs.elki.data.type.VectorFieldTypeInformation;
+import de.lmu.ifi.dbs.elki.database.Database;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRange;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
+import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
+import de.lmu.ifi.dbs.elki.database.query.range.RangeQuery;
+import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
+import de.lmu.ifi.dbs.elki.datasource.DatabaseConnection;
+import de.lmu.ifi.dbs.elki.datasource.bundle.MultipleObjectsBundle;
+import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult;
+import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance;
+import de.lmu.ifi.dbs.elki.logging.Logging;
+import de.lmu.ifi.dbs.elki.logging.progress.FiniteProgress;
+import de.lmu.ifi.dbs.elki.math.MeanVariance;
+import de.lmu.ifi.dbs.elki.result.Result;
+import de.lmu.ifi.dbs.elki.utilities.RandomFactory;
+import de.lmu.ifi.dbs.elki.utilities.Util;
+import de.lmu.ifi.dbs.elki.utilities.exceptions.AbortException;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.DoubleParameter;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ObjectParameter;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.RandomParameter;
+
+/**
+ * Benchmarking algorithm that computes a range query for each point. The query
+ * points can either come from a separate data source, or from the original
+ * database. In the latter case, the database is expected to have an additional,
+ * 1-dimensional vector field. For the separate data source, the last dimension
+ * will be cut off and used as query radius.
+ *
+ * The simplest data setup clearly is to have an input file:
+ *
+ * <pre>
+ * x y z label
+ * 1 2 3 Example1
+ * 4 5 6 Example2
+ * 7 8 9 Example3
+ * </pre>
+ *
+ * and a query file:
+ *
+ * <pre>
+ * x y z radius
+ * 1 2 3 1.2
+ * 4 5 6 3.3
+ * 7 8 9 4.1
+ * </pre>
+ *
+ * where the additional column is the radius.
+ *
+ * Alternatively, if you work with a single file, you need to use the filter
+ * command <tt>-dbc.filter SplitNumberVectorFilter -split.dims 1,2,3</tt> to
+ * split the relation into a 3-dimensional data vector, and 1 dimensional radius
+ * vector.
+ *
+ * TODO: alternatively, allow using a fixed radius?
+ *
+ * @author Erich Schubert
+ *
+ * @param <O> Vector type
+ * @param <D> Distance type
+ *
+ * @apiviz.uses RangeQuery
+ */
+public class RangeQueryBenchmarkAlgorithm<O extends NumberVector<?>, D extends NumberDistance<D, ?>> extends AbstractDistanceBasedAlgorithm<O, D, Result> {
+ /**
+ * The logger for this class.
+ */
+ private static final Logging LOG = Logging.getLogger(RangeQueryBenchmarkAlgorithm.class);
+
+ /**
+ * The alternate query point source. Optional.
+ */
+ protected DatabaseConnection queries = null;
+
+ /**
+ * Sampling size.
+ */
+ protected double sampling = -1;
+
+ /**
+ * Random generator factory
+ */
+ protected RandomFactory random;
+
+ /**
+ * Constructor.
+ *
+ * @param distanceFunction Distance function to use
+ * @param queries Query data set (may be null!)
+ * @param sampling Sampling rate
+ * @param random Random factory
+ */
+ public RangeQueryBenchmarkAlgorithm(DistanceFunction<? super O, D> distanceFunction, DatabaseConnection queries, double sampling, RandomFactory random) {
+ super(distanceFunction);
+ this.queries = queries;
+ this.sampling = sampling;
+ this.random = random;
+ }
+
+ /**
+ * Run the algorithm, with separate radius relation
+ *
+ * @param database Database
+ * @param relation Relation
+ * @param radrel Radius relation
+ * @return Null result
+ */
+ public Result run(Database database, Relation<O> relation, Relation<NumberVector<?>> radrel) {
+ if (queries != null) {
+ throw new AbortException("This 'run' method will not use the given query set!");
+ }
+ // Get a distance and kNN query instance.
+ DistanceQuery<O, D> distQuery = database.getDistanceQuery(relation, getDistanceFunction());
+ RangeQuery<O, D> rangeQuery = database.getRangeQuery(distQuery);
+ D dfactory = distQuery.getDistanceFactory();
+
+ final DBIDs sample;
+ if (sampling <= 0) {
+ sample = relation.getDBIDs();
+ } else if (sampling < 1.1) {
+ int size = (int) Math.min(sampling * relation.size(), relation.size());
+ sample = DBIDUtil.randomSample(relation.getDBIDs(), size, random);
+ } else {
+ int size = (int) Math.min(sampling, relation.size());
+ sample = DBIDUtil.randomSample(relation.getDBIDs(), size, random);
+ }
+ FiniteProgress prog = LOG.isVerbose() ? new FiniteProgress("kNN queries", sample.size(), LOG) : null;
+ int hash = 0;
+ MeanVariance mv = new MeanVariance();
+ for (DBIDIter iditer = sample.iter(); iditer.valid(); iditer.advance()) {
+ D r = dfactory.fromDouble(radrel.get(iditer).doubleValue(0));
+ DistanceDBIDResult<D> rres = rangeQuery.getRangeForDBID(iditer, r);
+ int ichecksum = 0;
+ for (DBIDIter it = rres.iter(); it.valid(); it.advance()) {
+ ichecksum += it.internalGetIndex();
+ }
+ hash = Util.mixHashCodes(hash, ichecksum);
+ mv.put(rres.size());
+ if (prog != null) {
+ prog.incrementProcessed(LOG);
+ }
+ }
+ if (prog != null) {
+ prog.ensureCompleted(LOG);
+ }
+ if (LOG.isVerbose()) {
+ LOG.verbose("Result hashcode: " + hash);
+ LOG.verbose("Mean number of results: "+mv.toString());
+ }
+ return null;
+ }
+
+ /**
+ * Run the algorithm, with a separate query set.
+ *
+ * @param database Database
+ * @param relation Relation
+ * @return Null result
+ */
+ public Result run(Database database, Relation<O> relation) {
+ if (queries == null) {
+ throw new AbortException("A query set is required for this 'run' method.");
+ }
+ // Get a distance and kNN query instance.
+ DistanceQuery<O, D> distQuery = database.getDistanceQuery(relation, getDistanceFunction());
+ RangeQuery<O, D> rangeQuery = database.getRangeQuery(distQuery);
+ D dfactory = distQuery.getDistanceFactory();
+ Factory<O, ?> ofactory = RelationUtil.getNumberVectorFactory(relation);
+ int dim = RelationUtil.dimensionality(relation);
+
+ // Separate query set.
+ TypeInformation res = new VectorFieldTypeInformation<NumberVector<?>>(NumberVector.class, dim + 1);
+ MultipleObjectsBundle bundle = queries.loadData();
+ int col = -1;
+ for (int i = 0; i < bundle.metaLength(); i++) {
+ if (res.isAssignableFromType(bundle.meta(i))) {
+ col = i;
+ break;
+ }
+ }
+ if (col < 0) {
+ StringBuilder buf = new StringBuilder();
+ buf.append("No compatible data type in query input was found. Expected: ");
+ buf.append(res.toString());
+ buf.append(" have: ");
+ for (int i = 0; i < bundle.metaLength(); i++) {
+ if (i > 0) {
+ buf.append(' ');
+ }
+ buf.append(bundle.meta(i).toString());
+ }
+ throw new AbortException(buf.toString());
+ }
+ // Random sampling is a bit of hack, sorry.
+ // But currently, we don't (yet) have an "integer random sample" function.
+ DBIDRange sids = DBIDUtil.generateStaticDBIDRange(bundle.dataLength());
+
+ final DBIDs sample;
+ if (sampling <= 0) {
+ sample = sids;
+ } else if (sampling < 1.1) {
+ int size = (int) Math.min(sampling * relation.size(), relation.size());
+ sample = DBIDUtil.randomSample(sids, size, random);
+ } else {
+ int size = (int) Math.min(sampling, sids.size());
+ sample = DBIDUtil.randomSample(sids, size, random);
+ }
+ FiniteProgress prog = LOG.isVerbose() ? new FiniteProgress("kNN queries", sample.size(), LOG) : null;
+ int hash = 0;
+ MeanVariance mv = new MeanVariance();
+ double[] buf = new double[dim];
+ for (DBIDIter iditer = sample.iter(); iditer.valid(); iditer.advance()) {
+ int off = sids.binarySearch(iditer);
+ assert (off >= 0);
+ NumberVector<?> o = (NumberVector<?>) bundle.data(off, col);
+ for (int i = 0; i < dim; i++) {
+ buf[i] = o.doubleValue(i);
+ }
+ O v = ofactory.newNumberVector(buf);
+ D r = dfactory.fromDouble(o.doubleValue(dim));
+ DistanceDBIDResult<D> rres = rangeQuery.getRangeForObject(v, r);
+ int ichecksum = 0;
+ for (DBIDIter it = rres.iter(); it.valid(); it.advance()) {
+ ichecksum += it.internalGetIndex();
+ }
+ hash = Util.mixHashCodes(hash, ichecksum);
+ mv.put(rres.size());
+ if (prog != null) {
+ prog.incrementProcessed(LOG);
+ }
+ }
+ if (prog != null) {
+ prog.ensureCompleted(LOG);
+ }
+ if (LOG.isVerbose()) {
+ LOG.verbose("Result hashcode: " + hash);
+ LOG.verbose("Mean number of results: "+mv.toString());
+ }
+ return null;
+ }
+
+ @Override
+ public TypeInformation[] getInputTypeRestriction() {
+ if (queries == null) {
+ return TypeUtil.array(getDistanceFunction().getInputTypeRestriction(), new VectorFieldTypeInformation<NumberVector<?>>(NumberVector.class, 1));
+ } else {
+ return TypeUtil.array(getDistanceFunction().getInputTypeRestriction());
+ }
+ }
+
+ @Override
+ protected Logging getLogger() {
+ return LOG;
+ }
+
+ /**
+ * Parameterization class
+ *
+ * @apiviz.exclude
+ *
+ * @author Erich Schubert
+ *
+ * @param <O> Object type
+ * @param <D> Distance type
+ */
+ public static class Parameterizer<O extends NumberVector<?>, D extends NumberDistance<D, ?>> extends AbstractDistanceBasedAlgorithm.Parameterizer<O, D> {
+ /**
+ * Parameter for the query dataset.
+ */
+ public static final OptionID QUERY_ID = new OptionID("rangebench.query", "Data source for the queries. If not set, the queries are taken from the database.");
+
+ /**
+ * Parameter for the sampling size.
+ */
+ public static final OptionID SAMPLING_ID = new OptionID("rangebench.sampling", "Sampling size parameter. If the value is less or equal 1, it is assumed to be the relative share. Larger values will be interpreted as integer sizes. By default, all data will be used.");
+
+ /**
+ * Parameter for the random generator
+ */
+ public static final OptionID RANDOM_ID = new OptionID("rangebench.random", "Random generator for sampling.");
+
+ /**
+ * The alternate query point source. Optional.
+ */
+ protected DatabaseConnection queries = null;
+
+ /**
+ * Sampling size.
+ */
+ protected double sampling = -1;
+
+ /**
+ * Random generator factory
+ */
+ protected RandomFactory random;
+
+ @Override
+ protected void makeOptions(Parameterization config) {
+ super.makeOptions(config);
+ ObjectParameter<DatabaseConnection> queryP = new ObjectParameter<DatabaseConnection>(QUERY_ID, DatabaseConnection.class);
+ queryP.setOptional(true);
+ if (config.grab(queryP)) {
+ queries = queryP.instantiateClass(config);
+ }
+ DoubleParameter samplingP = new DoubleParameter(SAMPLING_ID);
+ samplingP.setOptional(true);
+ if (config.grab(samplingP)) {
+ sampling = samplingP.doubleValue();
+ }
+ RandomParameter randomP = new RandomParameter(RANDOM_ID, RandomFactory.DEFAULT);
+ if (config.grab(randomP)) {
+ random = randomP.getValue();
+ }
+ }
+
+ @Override
+ protected RangeQueryBenchmarkAlgorithm<O, D> makeInstance() {
+ return new RangeQueryBenchmarkAlgorithm<O, D>(distanceFunction, queries, sampling, random);
+ }
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/benchmark/package-info.java b/src/de/lmu/ifi/dbs/elki/algorithm/benchmark/package-info.java
new file mode 100644
index 00000000..6a98fa64
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/benchmark/package-info.java
@@ -0,0 +1,30 @@
+/**
+ * <p>Benchmarking pseudo algorithms.</p>
+ *
+ * The algorithms in this package are meant to be used in run time benchmarks,
+ * to evalute e.g. the performance of an index structure.
+ */
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package de.lmu.ifi.dbs.elki.algorithm.benchmark;
+
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/AbstractProjectedClustering.java b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/AbstractProjectedClustering.java
index 670a3f0f..05cc2b4f 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/AbstractProjectedClustering.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/AbstractProjectedClustering.java
@@ -48,49 +48,19 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
* @param <R> the result we return
* @param <V> the type of FeatureVector handled by this Algorithm
*/
-public abstract class AbstractProjectedClustering<R extends Clustering<?>, V extends NumberVector<V, ?>> extends AbstractAlgorithm<R> implements ClusteringAlgorithm<R> {
+public abstract class AbstractProjectedClustering<R extends Clustering<?>, V extends NumberVector<?>> extends AbstractAlgorithm<R> implements ClusteringAlgorithm<R> {
/**
- * Parameter to specify the number of clusters to find, must be an integer
- * greater than 0.
- * <p>
- * Key: {@code -projectedclustering.k}
- * </p>
- */
- public static final OptionID K_ID = OptionID.getOrCreateOptionID("projectedclustering.k", "The number of clusters to find.");
-
- /**
- * Parameter to specify the multiplier for the initial number of seeds, must
- * be an integer greater than 0.
- * <p>
- * Default value: {@code 30}
- * </p>
- * <p>
- * Key: {@code -projectedclustering.k_i}
- * </p>
- */
- public static final OptionID K_I_ID = OptionID.getOrCreateOptionID("projectedclustering.k_i", "The multiplier for the initial number of seeds.");
-
- /**
- * Parameter to specify the dimensionality of the clusters to find, must be an
- * integer greater than 0.
- * <p>
- * Key: {@code -projectedclustering.l}
- * </p>
- */
- public static final OptionID L_ID = OptionID.getOrCreateOptionID("projectedclustering.l", "The dimensionality of the clusters to find.");
-
- /**
- * Holds the value of {@link #K_ID}.
+ * Holds the value of {@link Parameterizer#K_ID}.
*/
protected int k;
/**
- * Holds the value of {@link #K_I_ID}.
+ * Holds the value of {@link Parameterizer#K_I_ID}.
*/
protected int k_i;
/**
- * Holds the value of {@link #L_ID}.
+ * Holds the value of {@link Parameterizer#L_ID}.
*/
protected int l;
@@ -138,7 +108,37 @@ public abstract class AbstractProjectedClustering<R extends Clustering<?>, V ext
*
* @apiviz.exclude
*/
- public static abstract class Parameterizer extends AbstractParameterizer {
+ public abstract static class Parameterizer extends AbstractParameterizer {
+ /**
+ * Parameter to specify the number of clusters to find, must be an integer
+ * greater than 0.
+ * <p>
+ * Key: {@code -projectedclustering.k}
+ * </p>
+ */
+ public static final OptionID K_ID = new OptionID("projectedclustering.k", "The number of clusters to find.");
+
+ /**
+ * Parameter to specify the multiplier for the initial number of seeds, must
+ * be an integer greater than 0.
+ * <p>
+ * Default value: {@code 30}
+ * </p>
+ * <p>
+ * Key: {@code -projectedclustering.k_i}
+ * </p>
+ */
+ public static final OptionID K_I_ID = new OptionID("projectedclustering.k_i", "The multiplier for the initial number of seeds.");
+
+ /**
+ * Parameter to specify the dimensionality of the clusters to find, must be
+ * an integer greater than 0.
+ * <p>
+ * Key: {@code -projectedclustering.l}
+ * </p>
+ */
+ public static final OptionID L_ID = new OptionID("projectedclustering.l", "The dimensionality of the clusters to find.");
+
protected int k;
protected int k_i;
@@ -151,8 +151,9 @@ public abstract class AbstractProjectedClustering<R extends Clustering<?>, V ext
* @param config Parameterization
*/
protected void configK(Parameterization config) {
- IntParameter kP = new IntParameter(K_ID, new GreaterConstraint(0));
- if(config.grab(kP)) {
+ IntParameter kP = new IntParameter(K_ID);
+ kP.addConstraint(new GreaterConstraint(0));
+ if (config.grab(kP)) {
k = kP.getValue();
}
}
@@ -163,8 +164,9 @@ public abstract class AbstractProjectedClustering<R extends Clustering<?>, V ext
* @param config Parameterization
*/
protected void configKI(Parameterization config) {
- IntParameter k_iP = new IntParameter(K_I_ID, new GreaterConstraint(0), 30);
- if(config.grab(k_iP)) {
+ IntParameter k_iP = new IntParameter(K_I_ID, 30);
+ k_iP.addConstraint(new GreaterConstraint(0));
+ if (config.grab(k_iP)) {
k_i = k_iP.getValue();
}
}
@@ -175,10 +177,11 @@ public abstract class AbstractProjectedClustering<R extends Clustering<?>, V ext
* @param config Parameterization
*/
protected void configL(Parameterization config) {
- IntParameter lP = new IntParameter(L_ID, new GreaterConstraint(0));
- if(config.grab(lP)) {
+ IntParameter lP = new IntParameter(L_ID);
+ lP.addConstraint(new GreaterConstraint(0));
+ if (config.grab(lP)) {
l = lP.getValue();
}
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/AbstractProjectedDBSCAN.java b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/AbstractProjectedDBSCAN.java
index 250cc70b..f8b73f48 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/AbstractProjectedDBSCAN.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/AbstractProjectedDBSCAN.java
@@ -38,15 +38,17 @@ import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
import de.lmu.ifi.dbs.elki.database.Database;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDMIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.ModifiableDBIDs;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
import de.lmu.ifi.dbs.elki.database.query.range.RangeQuery;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
import de.lmu.ifi.dbs.elki.distance.distancefunction.EuclideanDistanceFunction;
import de.lmu.ifi.dbs.elki.distance.distancefunction.IndexBasedDistanceFunction;
import de.lmu.ifi.dbs.elki.distance.distancefunction.LocallyWeightedDistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResultIter;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
import de.lmu.ifi.dbs.elki.logging.progress.FiniteProgress;
@@ -67,7 +69,7 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ObjectParameter;
* @author Arthur Zimek
* @param <V> the type of NumberVector handled by this Algorithm
*/
-public abstract class AbstractProjectedDBSCAN<R extends Clustering<Model>, V extends NumberVector<V, ?>> extends AbstractAlgorithm<R> implements ClusteringAlgorithm<R> {
+public abstract class AbstractProjectedDBSCAN<R extends Clustering<Model>, V extends NumberVector<?>> extends AbstractAlgorithm<R> implements ClusteringAlgorithm<R> {
/**
* Parameter to specify the distance function to determine the distance
* between database objects, must extend
@@ -81,12 +83,12 @@ public abstract class AbstractProjectedDBSCAN<R extends Clustering<Model>, V ext
* {@link de.lmu.ifi.dbs.elki.distance.distancefunction.LocallyWeightedDistanceFunction}
* </p>
*/
- public static final OptionID OUTER_DISTANCE_FUNCTION_ID = OptionID.getOrCreateOptionID("projdbscan.outerdistancefunction", "Distance function to determine the distance between database objects.");
+ public static final OptionID OUTER_DISTANCE_FUNCTION_ID = new OptionID("projdbscan.outerdistancefunction", "Distance function to determine the distance between database objects.");
/**
* Parameter distance function
*/
- public static final OptionID INNER_DISTANCE_FUNCTION_ID = OptionID.getOrCreateOptionID("projdbscan.distancefunction", "Distance function to determine the neighbors for variance analysis.");
+ public static final OptionID INNER_DISTANCE_FUNCTION_ID = new OptionID("projdbscan.distancefunction", "Distance function to determine the neighbors for variance analysis.");
/**
* Parameter to specify the maximum radius of the neighborhood to be
@@ -95,7 +97,7 @@ public abstract class AbstractProjectedDBSCAN<R extends Clustering<Model>, V ext
* Key: {@code -projdbscan.epsilon}
* </p>
*/
- public static final OptionID EPSILON_ID = OptionID.getOrCreateOptionID("projdbscan.epsilon", "The maximum radius of the neighborhood to be considered.");
+ public static final OptionID EPSILON_ID = new OptionID("projdbscan.epsilon", "The maximum radius of the neighborhood to be considered.");
/**
* Parameter to specify the intrinsic dimensionality of the clusters to find,
@@ -104,7 +106,7 @@ public abstract class AbstractProjectedDBSCAN<R extends Clustering<Model>, V ext
* Key: {@code -projdbscan.lambda}
* </p>
*/
- public static final OptionID LAMBDA_ID = OptionID.getOrCreateOptionID("projdbscan.lambda", "The intrinsic dimensionality of the clusters to find.");
+ public static final OptionID LAMBDA_ID = new OptionID("projdbscan.lambda", "The intrinsic dimensionality of the clusters to find.");
/**
* Parameter to specify the threshold for minimum number of points in the
@@ -113,7 +115,7 @@ public abstract class AbstractProjectedDBSCAN<R extends Clustering<Model>, V ext
* Key: {@code -projdbscan.minpts}
* </p>
*/
- public static final OptionID MINPTS_ID = OptionID.getOrCreateOptionID("projdbscan.minpts", "Threshold for minimum number of points in " + "the epsilon-neighborhood of a point.");
+ public static final OptionID MINPTS_ID = new OptionID("projdbscan.minpts", "Threshold for minimum number of points in " + "the epsilon-neighborhood of a point.");
/**
* Holds the instance of the distance function specified by
@@ -187,7 +189,7 @@ public abstract class AbstractProjectedDBSCAN<R extends Clustering<Model>, V ext
if(relation.size() >= minpts) {
for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
if(!processedIDs.contains(iditer)) {
- expandCluster(distFunc, rangeQuery, iditer.getDBID(), objprog, clusprog);
+ expandCluster(distFunc, rangeQuery, DBIDUtil.deref(iditer), objprog, clusprog);
if(processedIDs.size() == relation.size() && noise.size() == 0) {
break;
}
@@ -277,9 +279,9 @@ public abstract class AbstractProjectedDBSCAN<R extends Clustering<Model>, V ext
}
// compute weighted epsilon neighborhood
- List<DistanceResultPair<DoubleDistance>> seeds = rangeQuery.getRangeForDBID(startObjectID, epsilon);
+ DistanceDBIDResult<DoubleDistance> neighbors = rangeQuery.getRangeForDBID(startObjectID, epsilon);
// neighbors < minPts -> noise
- if(seeds.size() < minpts) {
+ if(neighbors.size() < minpts) {
noise.add(startObjectID);
processedIDs.add(startObjectID);
if(objprog != null && clusprog != null) {
@@ -291,7 +293,8 @@ public abstract class AbstractProjectedDBSCAN<R extends Clustering<Model>, V ext
// try to expand the cluster
ModifiableDBIDs currentCluster = DBIDUtil.newArray();
- for(DistanceResultPair<DoubleDistance> seed : seeds) {
+ ModifiableDBIDs seeds = DBIDUtil.newHashSet();
+ for (DistanceDBIDResultIter<DoubleDistance> seed = neighbors.iter(); seed.valid(); seed.advance()) {
int nextID_corrDim = distFunc.getIndex().getLocalProjection(seed).getCorrelationDimension();
// nextID is not reachable from start object
if(nextID_corrDim > lambda) {
@@ -301,25 +304,27 @@ public abstract class AbstractProjectedDBSCAN<R extends Clustering<Model>, V ext
if(!processedIDs.contains(seed)) {
currentCluster.add(seed);
processedIDs.add(seed);
+ seeds.add(seed);
}
else if(noise.contains(seed)) {
currentCluster.add(seed);
noise.remove(seed);
}
}
- seeds.remove(0);
while(seeds.size() > 0) {
- DistanceResultPair<DoubleDistance> q = seeds.remove(0);
- int corrDim_q = distFunc.getIndex().getLocalProjection(q).getCorrelationDimension();
+ DBIDMIter iter = seeds.iter();
+ int corrDim_q = distFunc.getIndex().getLocalProjection(iter).getCorrelationDimension();
// q forms no lambda-dim hyperplane
if(corrDim_q > lambda) {
continue;
}
- List<DistanceResultPair<DoubleDistance>> reachables = rangeQuery.getRangeForDBID(q, epsilon);
+ DistanceDBIDResult<DoubleDistance> reachables = rangeQuery.getRangeForDBID(iter, epsilon);
+ iter.remove();
+
if(reachables.size() > minpts) {
- for(DistanceResultPair<DoubleDistance> r : reachables) {
+ for (DistanceDBIDResultIter<DoubleDistance> r = reachables.iter(); r.valid(); r.advance()) {
int corrDim_r = distFunc.getIndex().getLocalProjection(r).getCorrelationDimension();
// r is not reachable from q
if(corrDim_r > lambda) {
@@ -378,7 +383,7 @@ public abstract class AbstractProjectedDBSCAN<R extends Clustering<Model>, V ext
*
* @apiviz.exclude
*/
- public static abstract class Parameterizer<V extends NumberVector<V, ?>, D extends Distance<D>> extends AbstractParameterizer {
+ public abstract static class Parameterizer<V extends NumberVector<?>, D extends Distance<D>> extends AbstractParameterizer {
protected DistanceFunction<V, D> innerdist;
protected D epsilon;
@@ -405,7 +410,8 @@ public abstract class AbstractProjectedDBSCAN<R extends Clustering<Model>, V ext
}
protected void configMinPts(Parameterization config) {
- IntParameter minptsP = new IntParameter(MINPTS_ID, new GreaterConstraint(0));
+ IntParameter minptsP = new IntParameter(MINPTS_ID);
+ minptsP.addConstraint(new GreaterConstraint(0));
if(config.grab(minptsP)) {
minpts = minptsP.getValue();
}
@@ -428,7 +434,8 @@ public abstract class AbstractProjectedDBSCAN<R extends Clustering<Model>, V ext
}
protected void configLambda(Parameterization config) {
- IntParameter lambdaP = new IntParameter(LAMBDA_ID, new GreaterConstraint(0));
+ IntParameter lambdaP = new IntParameter(LAMBDA_ID);
+ lambdaP.addConstraint(new GreaterConstraint(0));
if(config.grab(lambdaP)) {
lambda = lambdaP.getValue();
}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/DBSCAN.java b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/DBSCAN.java
index 6bafa9e9..fcf81faa 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/DBSCAN.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/DBSCAN.java
@@ -34,14 +34,16 @@ import de.lmu.ifi.dbs.elki.data.model.Model;
import de.lmu.ifi.dbs.elki.data.type.TypeInformation;
import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
import de.lmu.ifi.dbs.elki.database.QueryUtil;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDMIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
+import de.lmu.ifi.dbs.elki.database.ids.HashSetModifiableDBIDs;
import de.lmu.ifi.dbs.elki.database.ids.ModifiableDBIDs;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
import de.lmu.ifi.dbs.elki.database.query.range.RangeQuery;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.logging.progress.FiniteProgress;
@@ -77,13 +79,13 @@ public class DBSCAN<O, D extends Distance<D>> extends AbstractDistanceBasedAlgor
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(DBSCAN.class);
+ private static final Logging LOG = Logging.getLogger(DBSCAN.class);
/**
* Parameter to specify the maximum radius of the neighborhood to be
* considered, must be suitable to the distance function specified.
*/
- public static final OptionID EPSILON_ID = OptionID.getOrCreateOptionID("dbscan.epsilon", "The maximum radius of the neighborhood to be considered.");
+ public static final OptionID EPSILON_ID = new OptionID("dbscan.epsilon", "The maximum radius of the neighborhood to be considered.");
/**
* Holds the value of {@link #EPSILON_ID}.
@@ -94,7 +96,7 @@ public class DBSCAN<O, D extends Distance<D>> extends AbstractDistanceBasedAlgor
* Parameter to specify the threshold for minimum number of points in the
* epsilon-neighborhood of a point, must be an integer greater than 0.
*/
- public static final OptionID MINPTS_ID = OptionID.getOrCreateOptionID("dbscan.minpts", "Threshold for minimum number of points in the epsilon-neighborhood of a point.");
+ public static final OptionID MINPTS_ID = new OptionID("dbscan.minpts", "Threshold for minimum number of points in the epsilon-neighborhood of a point.");
/**
* Holds the value of {@link #MINPTS_ID}.
@@ -136,40 +138,36 @@ public class DBSCAN<O, D extends Distance<D>> extends AbstractDistanceBasedAlgor
RangeQuery<O, D> rangeQuery = QueryUtil.getRangeQuery(relation, getDistanceFunction());
final int size = relation.size();
- FiniteProgress objprog = logger.isVerbose() ? new FiniteProgress("Processing objects", size, logger) : null;
- IndefiniteProgress clusprog = logger.isVerbose() ? new IndefiniteProgress("Number of clusters", logger) : null;
+ FiniteProgress objprog = LOG.isVerbose() ? new FiniteProgress("Processing objects", size, LOG) : null;
+ IndefiniteProgress clusprog = LOG.isVerbose() ? new IndefiniteProgress("Number of clusters", LOG) : null;
resultList = new ArrayList<ModifiableDBIDs>();
noise = DBIDUtil.newHashSet();
processedIDs = DBIDUtil.newHashSet(size);
- if(size >= minpts) {
+ if(size < minpts) {
+ // The can't be any clusters
+ noise.addDBIDs(relation.getDBIDs());
+ objprog.setProcessed(noise.size(), LOG);
+ }
+ else {
for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
if(!processedIDs.contains(iditer)) {
- expandCluster(relation, rangeQuery, iditer.getDBID(), objprog, clusprog);
+ expandCluster(relation, rangeQuery, iditer, objprog, clusprog);
}
if(objprog != null && clusprog != null) {
- objprog.setProcessed(processedIDs.size(), logger);
- clusprog.setProcessed(resultList.size(), logger);
+ objprog.setProcessed(processedIDs.size(), LOG);
+ clusprog.setProcessed(resultList.size(), LOG);
}
if(processedIDs.size() == size) {
break;
}
}
}
- else {
- for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
- noise.add(iditer);
- if(objprog != null && clusprog != null) {
- objprog.setProcessed(noise.size(), logger);
- clusprog.setProcessed(resultList.size(), logger);
- }
- }
- }
// Finish progress logging
if(objprog != null) {
- objprog.ensureCompleted(logger);
+ objprog.ensureCompleted(LOG);
}
if(clusprog != null) {
- clusprog.setCompleted(logger);
+ clusprog.setCompleted(LOG);
}
Clustering<Model> result = new Clustering<Model>("DBSCAN Clustering", "dbscan-clustering");
@@ -194,40 +192,43 @@ public class DBSCAN<O, D extends Distance<D>> extends AbstractDistanceBasedAlgor
* @param startObjectID potential seed of a new potential cluster
* @param objprog the progress object for logging the current status
*/
- protected void expandCluster(Relation<O> relation, RangeQuery<O, D> rangeQuery, DBID startObjectID, FiniteProgress objprog, IndefiniteProgress clusprog) {
- List<DistanceResultPair<D>> seeds = rangeQuery.getRangeForDBID(startObjectID, epsilon);
+ protected void expandCluster(Relation<O> relation, RangeQuery<O, D> rangeQuery, DBIDRef startObjectID, FiniteProgress objprog, IndefiniteProgress clusprog) {
+ DistanceDBIDResult<D> neighbors = rangeQuery.getRangeForDBID(startObjectID, epsilon);
// startObject is no core-object
- if(seeds.size() < minpts) {
+ if(neighbors.size() < minpts) {
noise.add(startObjectID);
processedIDs.add(startObjectID);
if(objprog != null && clusprog != null) {
- objprog.setProcessed(processedIDs.size(), logger);
- clusprog.setProcessed(resultList.size(), logger);
+ objprog.setProcessed(processedIDs.size(), LOG);
+ clusprog.setProcessed(resultList.size(), LOG);
}
return;
}
// try to expand the cluster
+ HashSetModifiableDBIDs seeds = DBIDUtil.newHashSet();
ModifiableDBIDs currentCluster = DBIDUtil.newArray();
- for(DistanceResultPair<D> seed : seeds) {
+ for(DBIDIter seed = neighbors.iter(); seed.valid(); seed.advance()) {
if(!processedIDs.contains(seed)) {
currentCluster.add(seed);
processedIDs.add(seed);
+ seeds.add(seed);
}
else if(noise.contains(seed)) {
currentCluster.add(seed);
noise.remove(seed);
}
}
- seeds.remove(0);
+ seeds.remove(startObjectID);
while(seeds.size() > 0) {
- DistanceResultPair<D> o = seeds.remove(0);
- List<DistanceResultPair<D>> neighborhood = rangeQuery.getRangeForDBID(o, epsilon);
+ DBIDMIter o = seeds.iter();
+ DistanceDBIDResult<D> neighborhood = rangeQuery.getRangeForDBID(o, epsilon);
+ o.remove();
if(neighborhood.size() >= minpts) {
- for(DistanceResultPair<D> neighbor : neighborhood) {
+ for(DBIDIter neighbor = neighborhood.iter(); neighbor.valid(); neighbor.advance()) {
boolean inNoise = noise.contains(neighbor);
boolean unclassified = !processedIDs.contains(neighbor);
if(inNoise || unclassified) {
@@ -248,9 +249,9 @@ public class DBSCAN<O, D extends Distance<D>> extends AbstractDistanceBasedAlgor
}
if(objprog != null && clusprog != null) {
- objprog.setProcessed(processedIDs.size(), logger);
+ objprog.setProcessed(processedIDs.size(), LOG);
int numClusters = currentCluster.size() > minpts ? resultList.size() + 1 : resultList.size();
- clusprog.setProcessed(numClusters, logger);
+ clusprog.setProcessed(numClusters, LOG);
}
}
if(currentCluster.size() >= minpts) {
@@ -270,7 +271,7 @@ public class DBSCAN<O, D extends Distance<D>> extends AbstractDistanceBasedAlgor
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/DeLiClu.java b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/DeLiClu.java
index a0780e3d..22875715 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/DeLiClu.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/DeLiClu.java
@@ -36,10 +36,12 @@ import de.lmu.ifi.dbs.elki.database.Database;
import de.lmu.ifi.dbs.elki.database.datastore.DataStore;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.DistanceUtil;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
import de.lmu.ifi.dbs.elki.distance.distancefunction.SpatialPrimitiveDistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
import de.lmu.ifi.dbs.elki.index.tree.LeafEntry;
import de.lmu.ifi.dbs.elki.index.tree.TreeIndexPathComponent;
@@ -53,7 +55,6 @@ import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.logging.progress.FiniteProgress;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
import de.lmu.ifi.dbs.elki.result.optics.ClusterOrderResult;
-import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.KNNList;
import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.UpdatableHeap;
import de.lmu.ifi.dbs.elki.utilities.documentation.Description;
import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
@@ -83,17 +84,17 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
@Title("DeliClu: Density-Based Hierarchical Clustering")
@Description("Hierachical algorithm to find density-connected sets in a database based on the parameter 'minpts'.")
@Reference(authors = "E. Achtert, C. Böhm, P. Kröger", title = "DeLiClu: Boosting Robustness, Completeness, Usability, and Efficiency of Hierarchical Clustering by a Closest Pair Ranking", booktitle = "Proc. 10th Pacific-Asia Conference on Knowledge Discovery and Data Mining (PAKDD 2006), Singapore, 2006", url = "http://dx.doi.org/10.1007/11731139_16")
-public class DeLiClu<NV extends NumberVector<NV, ?>, D extends Distance<D>> extends AbstractDistanceBasedAlgorithm<NV, D, ClusterOrderResult<D>> implements OPTICSTypeAlgorithm<D> {
+public class DeLiClu<NV extends NumberVector<?>, D extends Distance<D>> extends AbstractDistanceBasedAlgorithm<NV, D, ClusterOrderResult<D>> implements OPTICSTypeAlgorithm<D> {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(DeLiClu.class);
+ private static final Logging LOG = Logging.getLogger(DeLiClu.class);
/**
* Parameter to specify the threshold for minimum number of points within a
* cluster, must be an integer greater than 0.
*/
- public static final OptionID MINPTS_ID = OptionID.getOrCreateOptionID("deliclu.minpts", "Threshold for minimum number of points within a cluster.");
+ public static final OptionID MINPTS_ID = new OptionID("deliclu.minpts", "Threshold for minimum number of points within a cluster.");
/**
* The priority queue for the algorithm.
@@ -137,12 +138,12 @@ public class DeLiClu<NV extends NumberVector<NV, ?>, D extends Distance<D>> exte
SpatialPrimitiveDistanceFunction<NV, D> distFunction = (SpatialPrimitiveDistanceFunction<NV, D>) getDistanceFunction();
// first do the knn-Join
- if(logger.isVerbose()) {
- logger.verbose("knnJoin...");
+ if(LOG.isVerbose()) {
+ LOG.verbose("knnJoin...");
}
- DataStore<KNNList<D>> knns = knnJoin.run(database, relation);
+ DataStore<KNNResult<D>> knns = knnJoin.run(database, relation);
- FiniteProgress progress = logger.isVerbose() ? new FiniteProgress("DeLiClu", relation.size(), logger) : null;
+ FiniteProgress progress = LOG.isVerbose() ? new FiniteProgress("DeLiClu", relation.size(), LOG) : null;
final int size = relation.size();
ClusterOrderResult<D> clusterOrder = new ClusterOrderResult<D>("DeLiClu Clustering", "deliclu-clustering");
@@ -184,12 +185,12 @@ public class DeLiClu<NV extends NumberVector<NV, ?>, D extends Distance<D>> exte
reinsertExpanded(distFunction, index, path, knns);
if(progress != null) {
- progress.setProcessed(numHandled, logger);
+ progress.setProcessed(numHandled, LOG);
}
}
}
if(progress != null) {
- progress.ensureCompleted(logger);
+ progress.ensureCompleted(LOG);
}
return clusterOrder;
}
@@ -205,7 +206,7 @@ public class DeLiClu<NV extends NumberVector<NV, ?>, D extends Distance<D>> exte
if(!it.valid()) {
return null;
}
- return it.getDBID();
+ return DBIDUtil.deref(it);
}
/**
@@ -216,7 +217,7 @@ public class DeLiClu<NV extends NumberVector<NV, ?>, D extends Distance<D>> exte
* @param nodePair the pair of nodes to be expanded
* @param knns the knn list
*/
- private void expandNodes(DeLiCluTree index, SpatialPrimitiveDistanceFunction<NV, D> distFunction, SpatialObjectPair nodePair, DataStore<KNNList<D>> knns) {
+ private void expandNodes(DeLiCluTree index, SpatialPrimitiveDistanceFunction<NV, D> distFunction, SpatialObjectPair nodePair, DataStore<KNNResult<D>> knns) {
DeLiCluNode node1 = index.getNode(((SpatialDirectoryEntry) nodePair.entry1).getPageID());
DeLiCluNode node2 = index.getNode(((SpatialDirectoryEntry) nodePair.entry2).getPageID());
@@ -238,8 +239,8 @@ public class DeLiClu<NV extends NumberVector<NV, ?>, D extends Distance<D>> exte
* @param node2 the second node
*/
private void expandDirNodes(SpatialPrimitiveDistanceFunction<NV, D> distFunction, DeLiCluNode node1, DeLiCluNode node2) {
- if(logger.isDebuggingFinest()) {
- logger.debugFinest("ExpandDirNodes: " + node1.getPageID() + " + " + node2.getPageID());
+ if(LOG.isDebuggingFinest()) {
+ LOG.debugFinest("ExpandDirNodes: " + node1.getPageID() + " + " + node2.getPageID());
}
int numEntries_1 = node1.getNumEntries();
int numEntries_2 = node2.getNumEntries();
@@ -273,9 +274,9 @@ public class DeLiClu<NV extends NumberVector<NV, ?>, D extends Distance<D>> exte
* @param node2 the second node
* @param knns the knn list
*/
- private void expandLeafNodes(SpatialPrimitiveDistanceFunction<NV, D> distFunction, DeLiCluNode node1, DeLiCluNode node2, DataStore<KNNList<D>> knns) {
- if(logger.isDebuggingFinest()) {
- logger.debugFinest("ExpandLeafNodes: " + node1.getPageID() + " + " + node2.getPageID());
+ private void expandLeafNodes(SpatialPrimitiveDistanceFunction<NV, D> distFunction, DeLiCluNode node1, DeLiCluNode node2, DataStore<KNNResult<D>> knns) {
+ if(LOG.isDebuggingFinest()) {
+ LOG.debugFinest("ExpandLeafNodes: " + node1.getPageID() + " + " + node2.getPageID());
}
int numEntries_1 = node1.getNumEntries();
int numEntries_2 = node2.getNumEntries();
@@ -309,12 +310,12 @@ public class DeLiClu<NV extends NumberVector<NV, ?>, D extends Distance<D>> exte
* @param path the path of the object inserted last
* @param knns the knn list
*/
- private void reinsertExpanded(SpatialPrimitiveDistanceFunction<NV, D> distFunction, DeLiCluTree index, List<TreeIndexPathComponent<DeLiCluEntry>> path, DataStore<KNNList<D>> knns) {
+ private void reinsertExpanded(SpatialPrimitiveDistanceFunction<NV, D> distFunction, DeLiCluTree index, List<TreeIndexPathComponent<DeLiCluEntry>> path, DataStore<KNNResult<D>> knns) {
SpatialDirectoryEntry rootEntry = (SpatialDirectoryEntry) path.remove(0).getEntry();
reinsertExpanded(distFunction, index, path, 0, rootEntry, knns);
}
- private void reinsertExpanded(SpatialPrimitiveDistanceFunction<NV, D> distFunction, DeLiCluTree index, List<TreeIndexPathComponent<DeLiCluEntry>> path, int pos, SpatialDirectoryEntry parentEntry, DataStore<KNNList<D>> knns) {
+ private void reinsertExpanded(SpatialPrimitiveDistanceFunction<NV, D> distFunction, DeLiCluTree index, List<TreeIndexPathComponent<DeLiCluEntry>> path, int pos, SpatialDirectoryEntry parentEntry, DataStore<KNNResult<D>> knns) {
DeLiCluNode parentNode = index.getNode(parentEntry.getPageID());
SpatialEntry entry2 = path.get(pos).getEntry();
@@ -367,7 +368,7 @@ public class DeLiClu<NV extends NumberVector<NV, ?>, D extends Distance<D>> exte
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -487,7 +488,7 @@ public class DeLiClu<NV extends NumberVector<NV, ?>, D extends Distance<D>> exte
*
* @apiviz.exclude
*/
- public static class Parameterizer<NV extends NumberVector<NV, ?>, D extends Distance<D>> extends AbstractDistanceBasedAlgorithm.Parameterizer<NV, D> {
+ public static class Parameterizer<NV extends NumberVector<?>, D extends Distance<D>> extends AbstractDistanceBasedAlgorithm.Parameterizer<NV, D> {
protected int minpts = 0;
@Override
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/EM.java b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/EM.java
index 63ebbabb..514e63bd 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/EM.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/EM.java
@@ -27,6 +27,7 @@ import java.util.ArrayList;
import java.util.List;
import de.lmu.ifi.dbs.elki.algorithm.AbstractAlgorithm;
+import de.lmu.ifi.dbs.elki.algorithm.clustering.kmeans.KMeans;
import de.lmu.ifi.dbs.elki.algorithm.clustering.kmeans.KMeansInitialization;
import de.lmu.ifi.dbs.elki.algorithm.clustering.kmeans.RandomlyGeneratedInitialMeans;
import de.lmu.ifi.dbs.elki.data.Cluster;
@@ -44,12 +45,12 @@ import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.ModifiableDBIDs;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.distance.distancefunction.EuclideanDistanceFunction;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.math.MathUtil;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Matrix;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.utilities.FormatUtil;
import de.lmu.ifi.dbs.elki.utilities.documentation.Description;
import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
@@ -86,11 +87,11 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ObjectParameter;
@Title("EM-Clustering: Clustering by Expectation Maximization")
@Description("Provides k Gaussian mixtures maximizing the probability of the given data")
@Reference(authors = "A. P. Dempster, N. M. Laird, D. B. Rubin", title = "Maximum Likelihood from Incomplete Data via the EM algorithm", booktitle = "Journal of the Royal Statistical Society, Series B, 39(1), 1977, pp. 1-31", url = "http://www.jstor.org/stable/2984875")
-public class EM<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Clustering<EMModel<V>>> implements ClusteringAlgorithm<Clustering<EMModel<V>>> {
+public class EM<V extends NumberVector<?>> extends AbstractAlgorithm<Clustering<EMModel<V>>> implements ClusteringAlgorithm<Clustering<EMModel<V>>> {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(EM.class);
+ private static final Logging LOG = Logging.getLogger(EM.class);
/**
* Small value to increment diagonally of a matrix in order to avoid
@@ -102,7 +103,7 @@ public class EM<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Clusteri
* Parameter to specify the number of clusters to find, must be an integer
* greater than 0.
*/
- public static final OptionID K_ID = OptionID.getOrCreateOptionID("em.k", "The number of clusters to find.");
+ public static final OptionID K_ID = new OptionID("em.k", "The number of clusters to find.");
/**
* Holds the value of {@link #K_ID}.
@@ -113,12 +114,12 @@ public class EM<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Clusteri
* Parameter to specify the termination criterion for maximization of E(M):
* E(M) - E(M') < em.delta, must be a double equal to or greater than 0.
*/
- public static final OptionID DELTA_ID = OptionID.getOrCreateOptionID("em.delta", "The termination criterion for maximization of E(M): " + "E(M) - E(M') < em.delta");
+ public static final OptionID DELTA_ID = new OptionID("em.delta", "The termination criterion for maximization of E(M): " + "E(M) - E(M') < em.delta");
/**
* Parameter to specify the initialization method
*/
- public static final OptionID INIT_ID = OptionID.getOrCreateOptionID("kmeans.initialization", "Method to choose the initial means.");
+ public static final OptionID INIT_ID = new OptionID("kmeans.initialization", "Method to choose the initial means.");
private static final double MIN_LOGLIKELIHOOD = -100000;
@@ -138,17 +139,24 @@ public class EM<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Clusteri
private KMeansInitialization<V> initializer;
/**
+ * Maximum number of iterations to allow
+ */
+ private int maxiter;
+
+ /**
* Constructor.
*
* @param k k parameter
* @param delta delta parameter
* @param initializer Class to choose the initial means
+ * @param maxiter Maximum number of iterations
*/
- public EM(int k, double delta, KMeansInitialization<V> initializer) {
+ public EM(int k, double delta, KMeansInitialization<V> initializer, int maxiter) {
super();
this.k = k;
this.delta = delta;
this.initializer = initializer;
+ this.maxiter = maxiter;
}
/**
@@ -164,15 +172,15 @@ public class EM<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Clusteri
* @return Result
*/
public Clustering<EMModel<V>> run(Database database, Relation<V> relation) {
- if(relation.size() == 0) {
+ if (relation.size() == 0) {
throw new IllegalArgumentException("database empty: must contain elements");
}
// initial models
- if(logger.isVerbose()) {
- logger.verbose("initializing " + k + " models");
+ if (LOG.isVerbose()) {
+ LOG.verbose("initializing " + k + " models");
}
List<Vector> means = new ArrayList<Vector>();
- for(NumberVector<?, ?> nv : initializer.chooseInitialMeans(relation, k, EuclideanDistanceFunction.STATIC)) {
+ for (NumberVector<?> nv : initializer.chooseInitialMeans(relation, k, EuclideanDistanceFunction.STATIC)) {
means.add(nv.getColumnVector());
}
List<Matrix> covarianceMatrices = new ArrayList<Matrix>(k);
@@ -182,113 +190,117 @@ public class EM<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Clusteri
probClusterIGivenX = DataStoreUtil.makeStorage(relation.getDBIDs(), DataStoreFactory.HINT_HOT | DataStoreFactory.HINT_SORTED, double[].class);
final int dimensionality = means.get(0).getDimensionality();
- for(int i = 0; i < k; i++) {
+ for (int i = 0; i < k; i++) {
Matrix m = Matrix.identity(dimensionality, dimensionality);
covarianceMatrices.add(m);
normDistrFactor[i] = 1.0 / Math.sqrt(Math.pow(MathUtil.TWOPI, dimensionality) * m.det());
invCovMatr.add(m.inverse());
clusterWeights[i] = 1.0 / k;
- if(logger.isDebuggingFinest()) {
- StringBuffer msg = new StringBuffer();
+ if (LOG.isDebuggingFinest()) {
+ StringBuilder msg = new StringBuilder();
msg.append(" model ").append(i).append(":\n");
- msg.append(" mean: ").append(means.get(i)).append("\n");
- msg.append(" m:\n").append(FormatUtil.format(m, " ")).append("\n");
- msg.append(" m.det(): ").append(m.det()).append("\n");
- msg.append(" cluster weight: ").append(clusterWeights[i]).append("\n");
- msg.append(" normDistFact: ").append(normDistrFactor[i]).append("\n");
- logger.debugFine(msg.toString());
+ msg.append(" mean: ").append(means.get(i)).append('\n');
+ msg.append(" m:\n").append(FormatUtil.format(m, " ")).append('\n');
+ msg.append(" m.det(): ").append(m.det()).append('\n');
+ msg.append(" cluster weight: ").append(clusterWeights[i]).append('\n');
+ msg.append(" normDistFact: ").append(normDistrFactor[i]).append('\n');
+ LOG.debugFine(msg.toString());
}
}
double emNew = assignProbabilitiesToInstances(relation, normDistrFactor, means, invCovMatr, clusterWeights, probClusterIGivenX);
// iteration unless no change
- if(logger.isVerbose()) {
- logger.verbose("iterating EM");
+ if (LOG.isVerbose()) {
+ LOG.verbose("iterating EM");
+ }
+ if (LOG.isVerbose()) {
+ LOG.verbose("iteration " + 0 + " - expectation value: " + emNew);
}
double em;
- int it = 0;
- do {
- it++;
- if(logger.isVerbose()) {
- logger.verbose("iteration " + it + " - expectation value: " + emNew);
- }
+ for (int it = 1; it <= maxiter || maxiter < 0; it++) {
em = emNew;
// recompute models
List<Vector> meanSums = new ArrayList<Vector>(k);
double[] sumOfClusterProbabilities = new double[k];
- for(int i = 0; i < k; i++) {
+ for (int i = 0; i < k; i++) {
clusterWeights[i] = 0.0;
meanSums.add(new Vector(dimensionality));
covarianceMatrices.set(i, Matrix.zeroMatrix(dimensionality));
}
// weights and means
- for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
+ for (DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
double[] clusterProbabilities = probClusterIGivenX.get(iditer);
- for(int i = 0; i < k; i++) {
+ for (int i = 0; i < k; i++) {
sumOfClusterProbabilities[i] += clusterProbabilities[i];
Vector summand = relation.get(iditer).getColumnVector().timesEquals(clusterProbabilities[i]);
meanSums.get(i).plusEquals(summand);
}
}
final int n = relation.size();
- for(int i = 0; i < k; i++) {
+ for (int i = 0; i < k; i++) {
clusterWeights[i] = sumOfClusterProbabilities[i] / n;
Vector newMean = meanSums.get(i).timesEquals(1 / sumOfClusterProbabilities[i]);
means.set(i, newMean);
}
// covariance matrices
- for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
+ for (DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
double[] clusterProbabilities = probClusterIGivenX.get(iditer);
Vector instance = relation.get(iditer).getColumnVector();
- for(int i = 0; i < k; i++) {
+ for (int i = 0; i < k; i++) {
Vector difference = instance.minus(means.get(i));
covarianceMatrices.get(i).plusEquals(difference.timesTranspose(difference).timesEquals(clusterProbabilities[i]));
}
}
- for(int i = 0; i < k; i++) {
+ for (int i = 0; i < k; i++) {
covarianceMatrices.set(i, covarianceMatrices.get(i).times(1 / sumOfClusterProbabilities[i]).cheatToAvoidSingularity(SINGULARITY_CHEAT));
}
- for(int i = 0; i < k; i++) {
+ for (int i = 0; i < k; i++) {
normDistrFactor[i] = 1.0 / Math.sqrt(Math.pow(MathUtil.TWOPI, dimensionality) * covarianceMatrices.get(i).det());
invCovMatr.set(i, covarianceMatrices.get(i).inverse());
}
// reassign probabilities
emNew = assignProbabilitiesToInstances(relation, normDistrFactor, means, invCovMatr, clusterWeights, probClusterIGivenX);
+
+ if (LOG.isVerbose()) {
+ LOG.verbose("iteration " + it + " - expectation value: " + emNew);
+ }
+ if (Math.abs(em - emNew) <= delta) {
+ break;
+ }
}
- while(Math.abs(em - emNew) > delta);
- if(logger.isVerbose()) {
- logger.verbose("assigning clusters");
+ if (LOG.isVerbose()) {
+ LOG.verbose("assigning clusters");
}
// fill result with clusters and models
List<ModifiableDBIDs> hardClusters = new ArrayList<ModifiableDBIDs>(k);
- for(int i = 0; i < k; i++) {
+ for (int i = 0; i < k; i++) {
hardClusters.add(DBIDUtil.newHashSet());
}
// provide a hard clustering
- for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
+ for (DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
double[] clusterProbabilities = probClusterIGivenX.get(iditer);
int maxIndex = 0;
double currentMax = 0.0;
- for(int i = 0; i < k; i++) {
- if(clusterProbabilities[i] > currentMax) {
+ for (int i = 0; i < k; i++) {
+ if (clusterProbabilities[i] > currentMax) {
maxIndex = i;
currentMax = clusterProbabilities[i];
}
}
hardClusters.get(maxIndex).add(iditer);
}
- final V factory = DatabaseUtil.assumeVectorField(relation).getFactory();
+ final NumberVector.Factory<V, ?> factory = RelationUtil.getNumberVectorFactory(relation);
Clustering<EMModel<V>> result = new Clustering<EMModel<V>>("EM Clustering", "em-clustering");
// provide models within the result
- for(int i = 0; i < k; i++) {
+ for (int i = 0; i < k; i++) {
// TODO: re-do labeling.
// SimpleClassLabel label = new SimpleClassLabel();
// label.init(result.canonicalClusterLabel(i));
@@ -316,37 +328,36 @@ public class EM<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Clusteri
protected double assignProbabilitiesToInstances(Relation<V> database, double[] normDistrFactor, List<Vector> means, List<Matrix> invCovMatr, double[] clusterWeights, WritableDataStore<double[]> probClusterIGivenX) {
double emSum = 0.0;
- for(DBIDIter iditer = database.iterDBIDs(); iditer.valid(); iditer.advance()) {
+ for (DBIDIter iditer = database.iterDBIDs(); iditer.valid(); iditer.advance()) {
Vector x = database.get(iditer).getColumnVector();
double[] probabilities = new double[k];
- for(int i = 0; i < k; i++) {
+ for (int i = 0; i < k; i++) {
Vector difference = x.minus(means.get(i));
double rowTimesCovTimesCol = difference.transposeTimesTimes(invCovMatr.get(i), difference);
double power = rowTimesCovTimesCol / 2.0;
double prob = normDistrFactor[i] * Math.exp(-power);
- if(logger.isDebuggingFinest()) {
- logger.debugFinest(" difference vector= ( " + difference.toString() + " )\n" + " difference:\n" + FormatUtil.format(difference, " ") + "\n" + " rowTimesCovTimesCol:\n" + rowTimesCovTimesCol + "\n" + " power= " + power + "\n" + " prob=" + prob + "\n" + " inv cov matrix: \n" + FormatUtil.format(invCovMatr.get(i), " "));
+ if (LOG.isDebuggingFinest()) {
+ LOG.debugFinest(" difference vector= ( " + difference.toString() + " )\n" + " difference:\n" + FormatUtil.format(difference, " ") + "\n" + " rowTimesCovTimesCol:\n" + rowTimesCovTimesCol + "\n" + " power= " + power + "\n" + " prob=" + prob + "\n" + " inv cov matrix: \n" + FormatUtil.format(invCovMatr.get(i), " "));
}
probabilities[i] = prob;
}
double priorProbability = 0.0;
- for(int i = 0; i < k; i++) {
+ for (int i = 0; i < k; i++) {
priorProbability += probabilities[i] * clusterWeights[i];
}
double logP = Math.max(Math.log(priorProbability), MIN_LOGLIKELIHOOD);
- if(!Double.isNaN(logP)) {
+ if (!Double.isNaN(logP)) {
emSum += logP;
}
double[] clusterProbabilities = new double[k];
- for(int i = 0; i < k; i++) {
+ for (int i = 0; i < k; i++) {
assert (priorProbability >= 0.0);
assert (clusterWeights[i] >= 0.0);
// do not divide by zero!
- if(priorProbability == 0.0) {
+ if (priorProbability == 0.0) {
clusterProbabilities[i] = 0.0;
- }
- else {
+ } else {
clusterProbabilities[i] = probabilities[i] / priorProbability * clusterWeights[i];
}
}
@@ -373,7 +384,7 @@ public class EM<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Clusteri
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -383,35 +394,46 @@ public class EM<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Clusteri
*
* @apiviz.exclude
*/
- public static class Parameterizer<V extends NumberVector<V, ?>> extends AbstractParameterizer {
+ public static class Parameterizer<V extends NumberVector<?>> extends AbstractParameterizer {
protected int k;
protected double delta;
protected KMeansInitialization<V> initializer;
+ protected int maxiter = -1;
+
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- IntParameter kP = new IntParameter(K_ID, new GreaterConstraint(0));
- if(config.grab(kP)) {
+ IntParameter kP = new IntParameter(K_ID);
+ kP.addConstraint(new GreaterConstraint(0));
+ if (config.grab(kP)) {
k = kP.getValue();
}
ObjectParameter<KMeansInitialization<V>> initialP = new ObjectParameter<KMeansInitialization<V>>(INIT_ID, KMeansInitialization.class, RandomlyGeneratedInitialMeans.class);
- if(config.grab(initialP)) {
+ if (config.grab(initialP)) {
initializer = initialP.instantiateClass(config);
}
- DoubleParameter deltaP = new DoubleParameter(DELTA_ID, new GreaterEqualConstraint(0.0), 0.0);
- if(config.grab(deltaP)) {
+ DoubleParameter deltaP = new DoubleParameter(DELTA_ID, 0.0);
+ deltaP.addConstraint(new GreaterEqualConstraint(0.0));
+ if (config.grab(deltaP)) {
delta = deltaP.getValue();
}
+
+ IntParameter maxiterP = new IntParameter(KMeans.MAXITER_ID);
+ maxiterP.addConstraint(new GreaterEqualConstraint(0));
+ maxiterP.setOptional(true);
+ if (config.grab(maxiterP)) {
+ maxiter = maxiterP.getValue();
+ }
}
@Override
protected EM<V> makeInstance() {
- return new EM<V>(k, delta, initializer);
+ return new EM<V>(k, delta, initializer, maxiter);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/NaiveMeanShiftClustering.java b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/NaiveMeanShiftClustering.java
new file mode 100644
index 00000000..8429d8ac
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/NaiveMeanShiftClustering.java
@@ -0,0 +1,279 @@
+package de.lmu.ifi.dbs.elki.algorithm.clustering;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+import java.util.ArrayList;
+
+import de.lmu.ifi.dbs.elki.algorithm.AbstractDistanceBasedAlgorithm;
+import de.lmu.ifi.dbs.elki.data.Cluster;
+import de.lmu.ifi.dbs.elki.data.Clustering;
+import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.data.model.MeanModel;
+import de.lmu.ifi.dbs.elki.data.type.TypeInformation;
+import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
+import de.lmu.ifi.dbs.elki.database.Database;
+import de.lmu.ifi.dbs.elki.database.ids.ArrayModifiableDBIDs;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
+import de.lmu.ifi.dbs.elki.database.ids.ModifiableDBIDs;
+import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
+import de.lmu.ifi.dbs.elki.database.query.range.RangeQuery;
+import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
+import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResultIter;
+import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance;
+import de.lmu.ifi.dbs.elki.logging.Logging;
+import de.lmu.ifi.dbs.elki.logging.progress.FiniteProgress;
+import de.lmu.ifi.dbs.elki.math.linearalgebra.Centroid;
+import de.lmu.ifi.dbs.elki.math.statistics.EpanechnikovKernelDensityFunction;
+import de.lmu.ifi.dbs.elki.math.statistics.KernelDensityFunction;
+import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.DistanceParameter;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ObjectParameter;
+import de.lmu.ifi.dbs.elki.utilities.pairs.Pair;
+
+/**
+ * Mean-shift based clustering algorithm. Naive implementation: there does not
+ * seem to be "the" mean-shift clustering algorithm, but it is a general
+ * concept. For the naive implementation, mean-shift is applied to all objects
+ * until they converge to other. This implementation is quite naive, and various
+ * optimizations can be made.
+ *
+ * It also is not really parameter-free: the kernel needs to be specified,
+ * including a radius/bandwidth.
+ *
+ * By using range queries, the algorithm does benefit from index structures!
+ *
+ * TODO: add methods to automatically choose the bandwidth?
+ *
+ * <p>
+ * Reference:<br />
+ * Y. Cheng<br />
+ * Mean shift, mode seeking, and clustering<br />
+ * IEEE Transactions on Pattern Analysis and Machine Intelligence 17-8
+ * </p>
+ *
+ * @author Erich Schubert
+ *
+ * @param <V> Vector type
+ * @param <D> Distance type
+ */
+@Reference(authors = "Y. Cheng", title = "Mean shift, mode seeking, and clustering", booktitle = "IEEE Transactions on Pattern Analysis and Machine Intelligence 17-8", url = "http://dx.doi.org/10.1109/34.400568")
+public class NaiveMeanShiftClustering<V extends NumberVector<?>, D extends NumberDistance<D, ?>> extends AbstractDistanceBasedAlgorithm<V, D, Clustering<MeanModel<V>>> implements ClusteringAlgorithm<Clustering<MeanModel<V>>> {
+ /**
+ * Class logger.
+ */
+ private static final Logging LOG = Logging.getLogger(NaiveMeanShiftClustering.class);
+
+ /**
+ * Density estimation kernel.
+ */
+ KernelDensityFunction kernel = EpanechnikovKernelDensityFunction.KERNEL;
+
+ /**
+ * Range of the kernel.
+ */
+ D range;
+
+ /**
+ * Maximum number of iterations.
+ */
+ static final int MAXITER = 1000;
+
+ /**
+ * Constructor.
+ *
+ * @param distanceFunction Distance function
+ * @param kernel Kernel function
+ * @param range Kernel radius
+ */
+ public NaiveMeanShiftClustering(DistanceFunction<? super V, D> distanceFunction, KernelDensityFunction kernel, D range) {
+ super(distanceFunction);
+ this.kernel = kernel;
+ this.range = range;
+ }
+
+ /**
+ * Run the mean-shift clustering algorithm.
+ *
+ * @param database Database
+ * @param relation Data relation
+ * @return Clustering result
+ */
+ public Clustering<MeanModel<V>> run(Database database, Relation<V> relation) {
+ final DistanceQuery<V, D> distq = database.getDistanceQuery(relation, getDistanceFunction());
+ final RangeQuery<V, D> rangeq = database.getRangeQuery(distq);
+ final int dim = RelationUtil.dimensionality(relation);
+
+ // Kernel bandwidth, for normalization
+ final double bandwidth = range.doubleValue();
+ // Stopping threshold
+ final double threshold = bandwidth * 1E-10;
+
+ // Result store:
+ ArrayList<Pair<V, ModifiableDBIDs>> clusters = new ArrayList<Pair<V, ModifiableDBIDs>>();
+
+ ModifiableDBIDs noise = DBIDUtil.newArray();
+
+ FiniteProgress prog = LOG.isVerbose() ? new FiniteProgress("Mean-shift clustering", relation.size(), LOG) : null;
+
+ for (DBIDIter iter = relation.iterDBIDs(); iter.valid(); iter.advance()) {
+ // Initial position:
+ V position = relation.get(iter);
+ iterations: for (int j = 1; j <= MAXITER; j++) {
+ // Compute new position:
+ V newvec = null;
+ {
+ DistanceDBIDResult<D> neigh = rangeq.getRangeForObject(position, range);
+ boolean okay = (neigh.size() > 1) || (neigh.size() >= 1 && j > 1);
+ if (okay) {
+ Centroid newpos = new Centroid(dim);
+ for (DistanceDBIDResultIter<D> niter = neigh.iter(); niter.valid(); niter.advance()) {
+ final double weight = kernel.density(niter.getDistance().doubleValue() / bandwidth);
+ newpos.put(relation.get(niter), weight);
+ }
+ newvec = newpos.toVector(relation);
+ // TODO: detect 0 weight!
+ }
+ if (!okay) {
+ noise.add(iter);
+ break iterations;
+ }
+ }
+ // Test if we are close to one of the known clusters:
+ double bestd = Double.POSITIVE_INFINITY;
+ Pair<V, ModifiableDBIDs> bestp = null;
+ for (Pair<V, ModifiableDBIDs> pair : clusters) {
+ final double merged = distq.distance(newvec, pair.first).doubleValue();
+ if (merged < bestd) {
+ bestd = merged;
+ bestp = pair;
+ }
+ }
+ // Check for convergence:
+ D delta = distq.distance(position, newvec);
+ if (bestd < 10 * threshold || bestd * 2 < delta.doubleValue()) {
+ bestp.second.add(iter);
+ break iterations;
+ }
+ if (j == MAXITER) {
+ LOG.warning("No convergence after " + MAXITER + " iterations. Distance: " + delta.toString());
+ }
+ if (Double.isNaN(delta.doubleValue())) {
+ LOG.warning("Encountered NaN distance. Invalid center vector? " + newvec.toString());
+ break iterations;
+ }
+ if (j == MAXITER || delta.doubleValue() < threshold) {
+ if (LOG.isDebuggingFine()) {
+ LOG.debugFine("New cluster:" + newvec + " delta: " + delta + " threshold: " + threshold + " bestd: " + bestd);
+ }
+ ArrayModifiableDBIDs cids = DBIDUtil.newArray();
+ cids.add(iter);
+ clusters.add(new Pair<V, ModifiableDBIDs>(newvec, cids));
+ break iterations;
+ }
+ position = newvec;
+ }
+ if (prog != null) {
+ prog.incrementProcessed(LOG);
+ }
+ }
+ if (prog != null) {
+ prog.ensureCompleted(LOG);
+ }
+
+ ArrayList<Cluster<MeanModel<V>>> cs = new ArrayList<Cluster<MeanModel<V>>>(clusters.size());
+ for (Pair<V, ModifiableDBIDs> pair : clusters) {
+ cs.add(new Cluster<MeanModel<V>>(pair.second, new MeanModel<V>(pair.first)));
+ }
+ if (noise.size() > 0) {
+ cs.add(new Cluster<MeanModel<V>>(noise, true));
+ }
+ Clustering<MeanModel<V>> c = new Clustering<MeanModel<V>>("Mean-shift Clustering", "mean-shift-clustering", cs);
+ return c;
+ }
+
+ @Override
+ public TypeInformation[] getInputTypeRestriction() {
+ return TypeUtil.array(TypeUtil.NUMBER_VECTOR_FIELD);
+ }
+
+ @Override
+ protected Logging getLogger() {
+ return LOG;
+ }
+
+ /**
+ * Parameterizer.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ *
+ * @param <V> Vector type
+ * @param <D> Distance type
+ */
+ public static class Parameterizer<V extends NumberVector<?>, D extends NumberDistance<D, ?>> extends AbstractDistanceBasedAlgorithm.Parameterizer<V, D> {
+ /**
+ * Parameter for kernel function.
+ */
+ public static final OptionID KERNEL_ID = new OptionID("meanshift.kernel", "Kernel function to use with mean-shift clustering.");
+
+ /**
+ * Parameter for kernel radius/range/bandwidth.
+ */
+ public static final OptionID RANGE_ID = new OptionID("meanshift.kernel-bandwidth", "Range of the kernel to use (aka: radius, bandwidth).");
+
+ /**
+ * Kernel function.
+ */
+ KernelDensityFunction kernel = EpanechnikovKernelDensityFunction.KERNEL;
+
+ /**
+ * Kernel radius.
+ */
+ D range;
+
+ @Override
+ protected void makeOptions(Parameterization config) {
+ super.makeOptions(config);
+ ObjectParameter<KernelDensityFunction> kernelP = new ObjectParameter<KernelDensityFunction>(KERNEL_ID, KernelDensityFunction.class, EpanechnikovKernelDensityFunction.class);
+ if (config.grab(kernelP)) {
+ kernel = kernelP.instantiateClass(config);
+ }
+ DistanceParameter<D> rangeP = new DistanceParameter<D>(RANGE_ID, distanceFunction);
+ if (config.grab(rangeP)) {
+ range = rangeP.getValue();
+ }
+ }
+
+ @Override
+ protected NaiveMeanShiftClustering<V, D> makeInstance() {
+ return new NaiveMeanShiftClustering<V, D>(distanceFunction, kernel, range);
+ }
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/OPTICS.java b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/OPTICS.java
index 04b57081..2c098dc0 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/OPTICS.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/OPTICS.java
@@ -23,8 +23,6 @@ package de.lmu.ifi.dbs.elki.algorithm.clustering;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.util.List;
-
import de.lmu.ifi.dbs.elki.algorithm.AbstractDistanceBasedAlgorithm;
import de.lmu.ifi.dbs.elki.data.type.TypeInformation;
import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
@@ -33,14 +31,17 @@ import de.lmu.ifi.dbs.elki.database.QueryUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
+import de.lmu.ifi.dbs.elki.database.ids.DistanceDBIDPair;
+import de.lmu.ifi.dbs.elki.database.ids.DoubleDistanceDBIDPair;
import de.lmu.ifi.dbs.elki.database.ids.ModifiableDBIDs;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
-import de.lmu.ifi.dbs.elki.database.query.DoubleDistanceResultPair;
import de.lmu.ifi.dbs.elki.database.query.range.RangeQuery;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.DistanceUtil;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
import de.lmu.ifi.dbs.elki.distance.distancefunction.PrimitiveDoubleDistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResultIter;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DoubleDistanceDBIDResultIter;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
import de.lmu.ifi.dbs.elki.logging.Logging;
@@ -78,19 +79,19 @@ public class OPTICS<O, D extends Distance<D>> extends AbstractDistanceBasedAlgor
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(OPTICS.class);
+ private static final Logging LOG = Logging.getLogger(OPTICS.class);
/**
* Parameter to specify the maximum radius of the neighborhood to be
* considered, must be suitable to the distance function specified.
*/
- public static final OptionID EPSILON_ID = OptionID.getOrCreateOptionID("optics.epsilon", "The maximum radius of the neighborhood to be considered.");
+ public static final OptionID EPSILON_ID = new OptionID("optics.epsilon", "The maximum radius of the neighborhood to be considered.");
/**
* Parameter to specify the threshold for minimum number of points in the
* epsilon-neighborhood of a point, must be an integer greater than 0.
*/
- public static final OptionID MINPTS_ID = OptionID.getOrCreateOptionID("optics.minpts", "Threshold for minimum number of points in the epsilon-neighborhood of a point.");
+ public static final OptionID MINPTS_ID = new OptionID("optics.minpts", "Threshold for minimum number of points in the epsilon-neighborhood of a point.");
/**
* Hold the value of {@link #EPSILON_ID}.
@@ -135,7 +136,7 @@ public class OPTICS<O, D extends Distance<D>> extends AbstractDistanceBasedAlgor
RangeQuery<O, D> rangeQuery = QueryUtil.getRangeQuery(relation, getDistanceFunction(), epsilon);
int size = relation.size();
- final FiniteProgress progress = logger.isVerbose() ? new FiniteProgress("OPTICS", size, logger) : null;
+ final FiniteProgress progress = LOG.isVerbose() ? new FiniteProgress("OPTICS", size, LOG) : null;
processedIDs = DBIDUtil.newHashSet(size);
ClusterOrderResult<D> clusterOrder = new ClusterOrderResult<D>("OPTICS Clusterorder", "optics-clusterorder");
@@ -151,19 +152,19 @@ public class OPTICS<O, D extends Distance<D>> extends AbstractDistanceBasedAlgor
@SuppressWarnings("unchecked")
final RangeQuery<O, DoubleDistance> doubleRangeQuery = RangeQuery.class.cast(rangeQuery);
final DoubleDistance depsilon = DoubleDistance.class.cast(epsilon);
- expandClusterOrderDouble(doubleClusterOrder, database, doubleRangeQuery, iditer.getDBID(), depsilon, progress);
+ expandClusterOrderDouble(doubleClusterOrder, database, doubleRangeQuery, DBIDUtil.deref(iditer), depsilon, progress);
}
}
}
else {
for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
if(!processedIDs.contains(iditer)) {
- expandClusterOrder(clusterOrder, database, rangeQuery, iditer.getDBID(), epsilon, progress);
+ expandClusterOrder(clusterOrder, database, rangeQuery, DBIDUtil.deref(iditer), epsilon, progress);
}
}
}
if(progress != null) {
- progress.ensureCompleted(logger);
+ progress.ensureCompleted(LOG);
}
return clusterOrder;
@@ -189,21 +190,21 @@ public class OPTICS<O, D extends Distance<D>> extends AbstractDistanceBasedAlgor
clusterOrder.add(current);
processedIDs.add(current.getID());
- List<DistanceResultPair<D>> neighbors = rangeQuery.getRangeForDBID(current.getID(), epsilon);
+ DistanceDBIDResult<D> neighbors = rangeQuery.getRangeForDBID(current.getID(), epsilon);
if(neighbors.size() >= minpts) {
- final DistanceResultPair<D> last = neighbors.get(minpts - 1);
+ final DistanceDBIDPair<D> last = neighbors.get(minpts - 1);
D coreDistance = last.getDistance();
- for(DistanceResultPair<D> neighbor : neighbors) {
+ for(DistanceDBIDResultIter<D> neighbor = neighbors.iter(); neighbor.valid(); neighbor.advance()) {
if(processedIDs.contains(neighbor)) {
continue;
}
D reachability = DistanceUtil.max(neighbor.getDistance(), coreDistance);
- heap.add(new GenericClusterOrderEntry<D>(neighbor.getDBID(), current.getID(), reachability));
+ heap.add(new GenericClusterOrderEntry<D>(DBIDUtil.deref(neighbor), current.getID(), reachability));
}
}
if(progress != null) {
- progress.setProcessed(processedIDs.size(), logger);
+ progress.setProcessed(processedIDs.size(), LOG);
}
}
}
@@ -228,18 +229,18 @@ public class OPTICS<O, D extends Distance<D>> extends AbstractDistanceBasedAlgor
clusterOrder.add(current);
processedIDs.add(current.getID());
- List<DistanceResultPair<DoubleDistance>> neighbors = rangeQuery.getRangeForDBID(current.getID(), epsilon);
+ DistanceDBIDResult<DoubleDistance> neighbors = rangeQuery.getRangeForDBID(current.getID(), epsilon);
if(neighbors.size() >= minpts) {
- final DistanceResultPair<DoubleDistance> last = neighbors.get(minpts - 1);
- if(last instanceof DoubleDistanceResultPair) {
- double coreDistance = ((DoubleDistanceResultPair) last).getDoubleDistance();
+ final DistanceDBIDPair<DoubleDistance> last = neighbors.get(minpts - 1);
+ if(last instanceof DoubleDistanceDBIDPair) {
+ double coreDistance = ((DoubleDistanceDBIDPair) last).doubleDistance();
- for(DistanceResultPair<DoubleDistance> neighbor : neighbors) {
+ for(DistanceDBIDResultIter<DoubleDistance> neighbor = neighbors.iter(); neighbor.valid(); neighbor.advance()) {
if(processedIDs.contains(neighbor)) {
continue;
}
- double reachability = Math.max(((DoubleDistanceResultPair) neighbor).getDoubleDistance(), coreDistance);
- heap.add(new DoubleDistanceClusterOrderEntry(neighbor.getDBID(), current.getID(), reachability));
+ double reachability = Math.max(((DoubleDistanceDBIDResultIter) neighbor).doubleDistance(), coreDistance);
+ heap.add(new DoubleDistanceClusterOrderEntry(DBIDUtil.deref(neighbor), current.getID(), reachability));
}
}
else {
@@ -247,17 +248,17 @@ public class OPTICS<O, D extends Distance<D>> extends AbstractDistanceBasedAlgor
// Only if we got an optimized result before.
double coreDistance = last.getDistance().doubleValue();
- for(DistanceResultPair<DoubleDistance> neighbor : neighbors) {
+ for(DistanceDBIDResultIter<DoubleDistance> neighbor = neighbors.iter(); neighbor.valid(); neighbor.advance()) {
if(processedIDs.contains(neighbor)) {
continue;
}
double reachability = Math.max(neighbor.getDistance().doubleValue(), coreDistance);
- heap.add(new DoubleDistanceClusterOrderEntry(neighbor.getDBID(), current.getID(), reachability));
+ heap.add(new DoubleDistanceClusterOrderEntry(DBIDUtil.deref(neighbor), current.getID(), reachability));
}
}
}
if(progress != null) {
- progress.setProcessed(processedIDs.size(), logger);
+ progress.setProcessed(processedIDs.size(), LOG);
}
}
}
@@ -279,7 +280,7 @@ public class OPTICS<O, D extends Distance<D>> extends AbstractDistanceBasedAlgor
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -302,9 +303,10 @@ public class OPTICS<O, D extends Distance<D>> extends AbstractDistanceBasedAlgor
epsilon = epsilonP.getValue();
}
- IntParameter minptsP = new IntParameter(MINPTS_ID, new GreaterConstraint(0));
+ IntParameter minptsP = new IntParameter(MINPTS_ID);
+ minptsP.addConstraint(new GreaterConstraint(0));
if(config.grab(minptsP)) {
- minpts = minptsP.getValue();
+ minpts = minptsP.intValue();
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/OPTICSXi.java b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/OPTICSXi.java
index 41e48b89..39a0ebd6 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/OPTICSXi.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/OPTICSXi.java
@@ -50,7 +50,8 @@ import de.lmu.ifi.dbs.elki.utilities.datastructures.hierarchy.HierarchyHashmapLi
import de.lmu.ifi.dbs.elki.utilities.datastructures.hierarchy.ModifiableHierarchy;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.IntervalConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterEqualConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.LessConstraint;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ClassParameter;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.DoubleParameter;
@@ -60,7 +61,7 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.DoubleParameter;
*
* @author Erich Schubert
*
- * @apiviz.uses OPTICSTypeAlgorithm oneway
+ * @apiviz.composedOf OPTICSTypeAlgorithm oneway
* @apiviz.uses ClusterOrderResult oneway
* @apiviz.has SteepAreaResult
*
@@ -70,17 +71,17 @@ public class OPTICSXi<N extends NumberDistance<N, ?>> extends AbstractAlgorithm<
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(OPTICSXi.class);
+ private static final Logging LOG = Logging.getLogger(OPTICSXi.class);
/**
* Parameter to specify the actual OPTICS algorithm to use.
*/
- public static final OptionID XIALG_ID = OptionID.getOrCreateOptionID("opticsxi.algorithm", "The actual OPTICS-type algorithm to use.");
+ public static final OptionID XIALG_ID = new OptionID("opticsxi.algorithm", "The actual OPTICS-type algorithm to use.");
/**
* Parameter to specify the steepness threshold.
*/
- public static final OptionID XI_ID = OptionID.getOrCreateOptionID("opticsxi.xi", "Threshold for the steepness requirement.");
+ public static final OptionID XI_ID = new OptionID("opticsxi.xi", "Threshold for the steepness requirement.");
/**
* The actual algorithm we use.
@@ -109,12 +110,12 @@ public class OPTICSXi<N extends NumberDistance<N, ?>> extends AbstractAlgorithm<
ClusterOrderResult<N> opticsresult = optics.run(database);
if(!NumberDistance.class.isInstance(optics.getDistanceFactory())) {
- logger.verbose("Xi cluster extraction only supported for number distances!");
+ LOG.verbose("Xi cluster extraction only supported for number distances!");
return null;
}
- if(logger.isVerbose()) {
- logger.verbose("Extracting clusters with Xi: " + xi);
+ if(LOG.isVerbose()) {
+ LOG.verbose("Extracting clusters with Xi: " + xi);
}
return extractClusters(opticsresult, relation, 1.0 - xi, optics.getMinPts());
}
@@ -135,7 +136,7 @@ public class OPTICSXi<N extends NumberDistance<N, ?>> extends AbstractAlgorithm<
// TODO: make it configurable to keep this list; this is mostly useful for
// visualization
List<SteepArea> salist = new ArrayList<SteepArea>();
- List<SteepDownArea> sdaset = new java.util.Vector<SteepDownArea>();
+ List<SteepDownArea> sdaset = new ArrayList<SteepDownArea>();
ModifiableHierarchy<Cluster<OPTICSModel>> hier = new HierarchyHashmapList<Cluster<OPTICSModel>>();
HashSet<Cluster<OPTICSModel>> curclusters = new HashSet<Cluster<OPTICSModel>>();
HashSetModifiableDBIDs unclaimedids = DBIDUtil.newHashSet(relation.getDBIDs());
@@ -175,8 +176,8 @@ public class OPTICSXi<N extends NumberDistance<N, ?>> extends AbstractAlgorithm<
}
mib = clusterOrder.get(endsteep).getReachability().doubleValue();
final SteepDownArea sda = new SteepDownArea(startsteep, endsteep, startval, 0);
- if(logger.isDebuggingFinest()) {
- logger.debugFinest("Xi " + sda.toString());
+ if(LOG.isDebuggingFinest()) {
+ LOG.debugFinest("Xi " + sda.toString());
}
sdaset.add(sda);
if(salist != null) {
@@ -220,8 +221,8 @@ public class OPTICSXi<N extends NumberDistance<N, ?>> extends AbstractAlgorithm<
}
}
sua = new SteepUpArea(startsteep, endsteep, esuccr);
- if(logger.isDebuggingFinest()) {
- logger.debugFinest("Xi " + sua.toString());
+ if(LOG.isDebuggingFinest()) {
+ LOG.debugFinest("Xi " + sua.toString());
}
if(salist != null) {
salist.add(sua);
@@ -280,8 +281,8 @@ public class OPTICSXi<N extends NumberDistance<N, ?>> extends AbstractAlgorithm<
dbids.add(dbid);
}
}
- if(logger.isDebuggingFine()) {
- logger.debugFine("Found cluster with " + dbids.size() + " new objects, length " + (cstart - cend + 1));
+ if(LOG.isDebuggingFine()) {
+ LOG.debugFine("Found cluster with " + dbids.size() + " new objects, length " + (cstart - cend + 1));
}
OPTICSModel model = new OPTICSModel(cstart, cend);
Cluster<OPTICSModel> cluster = new Cluster<OPTICSModel>("Cluster_" + cstart + "_" + cend, dbids, model, hier);
@@ -362,7 +363,7 @@ public class OPTICSXi<N extends NumberDistance<N, ?>> extends AbstractAlgorithm<
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -656,9 +657,10 @@ public class OPTICSXi<N extends NumberDistance<N, ?>> extends AbstractAlgorithm<
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
DoubleParameter xiP = new DoubleParameter(XI_ID);
- xiP.addConstraint(new IntervalConstraint(0.0, IntervalConstraint.IntervalBoundary.CLOSE, 1.0, IntervalConstraint.IntervalBoundary.OPEN));
+ xiP.addConstraint(new GreaterEqualConstraint(0.0));
+ xiP.addConstraint(new LessConstraint(1.0));
if(config.grab(xiP)) {
- xi = xiP.getValue();
+ xi = xiP.doubleValue();
}
ClassParameter<OPTICSTypeAlgorithm<D>> opticsP = new ClassParameter<OPTICSTypeAlgorithm<D>>(XIALG_ID, OPTICSTypeAlgorithm.class, OPTICS.class);
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/SLINK.java b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/SLINK.java
index 2aa38bdd..3e1f0650 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/SLINK.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/SLINK.java
@@ -23,34 +23,35 @@ package de.lmu.ifi.dbs.elki.algorithm.clustering;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+import gnu.trove.list.array.TDoubleArrayList;
+
import java.util.ArrayList;
-import java.util.Collections;
import java.util.Comparator;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
import de.lmu.ifi.dbs.elki.algorithm.AbstractDistanceBasedAlgorithm;
import de.lmu.ifi.dbs.elki.data.Cluster;
import de.lmu.ifi.dbs.elki.data.Clustering;
-import de.lmu.ifi.dbs.elki.data.model.ClusterModel;
import de.lmu.ifi.dbs.elki.data.model.DendrogramModel;
-import de.lmu.ifi.dbs.elki.data.model.Model;
import de.lmu.ifi.dbs.elki.data.type.SimpleTypeInformation;
import de.lmu.ifi.dbs.elki.data.type.TypeInformation;
import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
import de.lmu.ifi.dbs.elki.database.Database;
+import de.lmu.ifi.dbs.elki.database.datastore.DBIDDataStore;
import de.lmu.ifi.dbs.elki.database.datastore.DataStore;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreFactory;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreUtil;
+import de.lmu.ifi.dbs.elki.database.datastore.DoubleDistanceDataStore;
+import de.lmu.ifi.dbs.elki.database.datastore.WritableDBIDDataStore;
import de.lmu.ifi.dbs.elki.database.datastore.WritableDataStore;
-import de.lmu.ifi.dbs.elki.database.datastore.WritableRecordStore;
+import de.lmu.ifi.dbs.elki.database.datastore.WritableDoubleDistanceDataStore;
+import de.lmu.ifi.dbs.elki.database.datastore.WritableIntegerDataStore;
import de.lmu.ifi.dbs.elki.database.ids.ArrayModifiableDBIDs;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDArrayIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
-import de.lmu.ifi.dbs.elki.database.ids.DBIDMIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDVar;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.ids.ModifiableDBIDs;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
@@ -58,7 +59,9 @@ import de.lmu.ifi.dbs.elki.database.relation.MaterializedRelation;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.DistanceUtil;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distancefunction.PrimitiveDoubleDistanceFunction;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
+import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.logging.progress.FiniteProgress;
import de.lmu.ifi.dbs.elki.result.BasicResult;
@@ -73,10 +76,9 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterEqualConstraint;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
-import de.lmu.ifi.dbs.elki.utilities.pairs.Pair;
/**
- * Efficient implementation of the Single-Link Algorithm SLINK of R. Sibson.
+ * Implementation of the efficient Single-Link Algorithm SLINK of R. Sibson.
* <p>
* Reference: R. Sibson: SLINK: An optimally efficient algorithm for the
* single-link cluster method. <br>
@@ -94,35 +96,20 @@ public class SLINK<O, D extends Distance<D>> extends AbstractDistanceBasedAlgori
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(SLINK.class);
-
- /**
- * The minimum number of clusters to extract
- */
- public static final OptionID SLINK_MINCLUSTERS_ID = OptionID.getOrCreateOptionID("slink.minclusters", "The maximum number of clusters to extract.");
-
- /**
- * The values of the function Pi of the pointer representation.
- */
- private WritableDataStore<DBID> pi;
-
- /**
- * The values of the function Lambda of the pointer representation.
- */
- private WritableDataStore<D> lambda;
+ private static final Logging LOG = Logging.getLogger(SLINK.class);
/**
* Minimum number of clusters to extract
*/
- private Integer minclusters;
+ private int minclusters = -1;
/**
* Constructor.
*
* @param distanceFunction Distance function
- * @param minclusters Minimum clusters to extract. Can be null
+ * @param minclusters Minimum clusters to extract. Can be {@code -1}.
*/
- public SLINK(DistanceFunction<? super O, D> distanceFunction, Integer minclusters) {
+ public SLINK(DistanceFunction<? super O, D> distanceFunction, int minclusters) {
super(distanceFunction);
this.minclusters = minclusters;
}
@@ -130,48 +117,71 @@ public class SLINK<O, D extends Distance<D>> extends AbstractDistanceBasedAlgori
/**
* Performs the SLINK algorithm on the given database.
*/
- @SuppressWarnings("unchecked")
public Result run(Database database, Relation<O> relation) {
DistanceQuery<O, D> distQuery = database.getDistanceQuery(relation, getDistanceFunction());
+ @SuppressWarnings("unchecked")
Class<D> distCls = (Class<D>) getDistanceFunction().getDistanceFactory().getClass();
- WritableRecordStore store = DataStoreUtil.makeRecordStorage(relation.getDBIDs(), DataStoreFactory.HINT_HOT | DataStoreFactory.HINT_STATIC, DBID.class, distCls);
- pi = store.getStorage(0, DBID.class);
- lambda = store.getStorage(1, distCls);
+ WritableDBIDDataStore pi = DataStoreUtil.makeDBIDStorage(relation.getDBIDs(), DataStoreFactory.HINT_HOT | DataStoreFactory.HINT_STATIC);
+ WritableDataStore<D> lambda = DataStoreUtil.makeStorage(relation.getDBIDs(), DataStoreFactory.HINT_HOT | DataStoreFactory.HINT_STATIC, distCls);
// Temporary storage for m.
WritableDataStore<D> m = DataStoreUtil.makeStorage(relation.getDBIDs(), DataStoreFactory.HINT_HOT | DataStoreFactory.HINT_TEMP, distCls);
- FiniteProgress progress = logger.isVerbose() ? new FiniteProgress("Clustering", relation.size(), logger) : null;
+ FiniteProgress progress = LOG.isVerbose() ? new FiniteProgress("Running SLINK", relation.size(), LOG) : null;
// has to be an array for monotonicity reasons!
ModifiableDBIDs processedIDs = DBIDUtil.newArray(relation.size());
- // apply the algorithm
- for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
- step1(id);
- step2(id, processedIDs, distQuery, m);
- step3(id, processedIDs, m);
- step4(id, processedIDs);
-
- processedIDs.add(id);
-
- if(progress != null) {
- progress.incrementProcessed(logger);
+ // Optimized code path for double distances
+ if (getDistanceFunction() instanceof PrimitiveDoubleDistanceFunction && lambda instanceof WritableDoubleDistanceDataStore && m instanceof WritableDoubleDistanceDataStore) {
+ @SuppressWarnings("unchecked")
+ PrimitiveDoubleDistanceFunction<? super O> dist = (PrimitiveDoubleDistanceFunction<? super O>) getDistanceFunction();
+ WritableDoubleDistanceDataStore lambdad = (WritableDoubleDistanceDataStore) lambda;
+ WritableDoubleDistanceDataStore md = (WritableDoubleDistanceDataStore) m;
+ // apply the algorithm
+ for (DBIDIter id = relation.iterDBIDs(); id.valid(); id.advance()) {
+ step1double(id, pi, lambdad);
+ step2double(id, processedIDs, distQuery.getRelation(), dist, md);
+ step3double(id, pi, lambdad, processedIDs, md);
+ step4double(id, pi, lambdad, processedIDs);
+
+ processedIDs.add(id);
+
+ if (progress != null) {
+ progress.incrementProcessed(LOG);
+ }
+ }
+ } else {
+ // apply the algorithm
+ for (DBIDIter id = relation.iterDBIDs(); id.valid(); id.advance()) {
+ step1(id, pi, lambda);
+ step2(id, processedIDs, distQuery, m);
+ step3(id, pi, lambda, processedIDs, m);
+ step4(id, pi, lambda, processedIDs);
+
+ processedIDs.add(id);
+
+ if (progress != null) {
+ progress.incrementProcessed(LOG);
+ }
}
}
- if(progress != null) {
- progress.ensureCompleted(logger);
+ if (progress != null) {
+ progress.ensureCompleted(LOG);
}
// We don't need m anymore.
m.destroy();
m = null;
- // build dendrogram
- BasicResult result = null;
-
- // Build clusters identified by their target object
- int minc = minclusters != null ? minclusters : relation.size();
- result = extractClusters(relation.getDBIDs(), pi, lambda, minc);
+ // Build dendrogam clusters identified by their target object
+ if (LOG.isVerbose()) {
+ LOG.verbose("Extracting clusters.");
+ }
+ final BasicResult result;
+ if (lambda instanceof DoubleDistanceDataStore) {
+ result = extractClustersDouble(relation.getDBIDs(), pi, (DoubleDistanceDataStore) lambda, minclusters);
+ } else {
+ result = extractClusters(relation.getDBIDs(), pi, lambda, minclusters);
+ }
result.addChildResult(new MaterializedRelation<DBID>("SLINK pi", "slink-order", TypeUtil.DBID, pi, processedIDs));
result.addChildResult(new MaterializedRelation<D>("SLINK lambda", "slink-order", new SimpleTypeInformation<D>(distCls), lambda, processedIDs));
@@ -182,61 +192,66 @@ public class SLINK<O, D extends Distance<D>> extends AbstractDistanceBasedAlgori
/**
* First step: Initialize P(id) = id, L(id) = infinity.
*
- * @param newID the id of the object to be inserted into the pointer
+ * @param id the id of the object to be inserted into the pointer
* representation
+ * @param pi Pi data store
+ * @param lambda Lambda data store
*/
- private void step1(DBID newID) {
+ private void step1(DBIDRef id, WritableDBIDDataStore pi, WritableDataStore<D> lambda) {
// P(n+1) = n+1:
- pi.put(newID, newID);
+ pi.put(id, id);
// L(n+1) = infinity
- lambda.put(newID, getDistanceFunction().getDistanceFactory().infiniteDistance());
+ lambda.put(id, getDistanceFunction().getDistanceFactory().infiniteDistance());
}
/**
* Second step: Determine the pairwise distances from all objects in the
* pointer representation to the new object with the specified id.
*
- * @param newID the id of the object to be inserted into the pointer
+ * @param id the id of the object to be inserted into the pointer
* representation
* @param processedIDs the already processed ids
+ * @param m Data store
* @param distFunc Distance function to use
*/
- private void step2(DBID newID, DBIDs processedIDs, DistanceQuery<O, D> distFunc, WritableDataStore<D> m) {
- for(DBIDIter it = processedIDs.iter(); it.valid(); it.advance()) {
- DBID id = it.getDBID();
+ private void step2(DBIDRef id, DBIDs processedIDs, DistanceQuery<O, D> distFunc, WritableDataStore<D> m) {
+ O newObj = distFunc.getRelation().get(id);
+ for (DBIDIter it = processedIDs.iter(); it.valid(); it.advance()) {
// M(i) = dist(i, n+1)
- m.put(id, distFunc.distance(id, newID));
+ m.put(it, distFunc.distance(it, newObj));
}
}
/**
* Third step: Determine the values for P and L
*
- * @param newID the id of the object to be inserted into the pointer
+ * @param id the id of the object to be inserted into the pointer
* representation
+ * @param pi Pi data store
+ * @param lambda Lambda data store
* @param processedIDs the already processed ids
+ * @param m Data store
*/
- private void step3(DBID newID, DBIDs processedIDs, WritableDataStore<D> m) {
+ private void step3(DBIDRef id, WritableDBIDDataStore pi, WritableDataStore<D> lambda, DBIDs processedIDs, WritableDataStore<D> m) {
+ DBIDVar p_i = DBIDUtil.newVar();
// for i = 1..n
- for(DBIDIter it = processedIDs.iter(); it.valid(); it.advance()) {
- DBID id = it.getDBID();
- D l_i = lambda.get(id);
- D m_i = m.get(id);
- DBID p_i = pi.get(id);
+ for (DBIDIter it = processedIDs.iter(); it.valid(); it.advance()) {
+ D l_i = lambda.get(it);
+ D m_i = m.get(it);
+ pi.assignVar(it, p_i); // p_i = pi(it)
D mp_i = m.get(p_i);
// if L(i) >= M(i)
- if(l_i.compareTo(m_i) >= 0) {
+ if (l_i.compareTo(m_i) >= 0) {
// M(P(i)) = min { M(P(i)), L(i) }
m.put(p_i, DistanceUtil.min(mp_i, l_i));
// L(i) = M(i)
- lambda.put(id, m_i);
+ lambda.put(it, m_i);
// P(i) = n+1;
- pi.put(id, newID);
- }
- else {
+ pi.put(it, id);
+ } else {
// M(P(i)) = min { M(P(i)), M(i) }
m.put(p_i, DistanceUtil.min(mp_i, m_i));
}
@@ -246,34 +261,119 @@ public class SLINK<O, D extends Distance<D>> extends AbstractDistanceBasedAlgori
/**
* Fourth step: Actualize the clusters if necessary
*
- * @param newID the id of the current object
+ * @param id the id of the current object
+ * @param pi Pi data store
+ * @param lambda Lambda data store
* @param processedIDs the already processed ids
*/
- private void step4(DBID newID, DBIDs processedIDs) {
+ private void step4(DBIDRef id, WritableDBIDDataStore pi, WritableDataStore<D> lambda, DBIDs processedIDs) {
+ DBIDVar p_i = DBIDUtil.newVar();
// for i = 1..n
- for(DBIDIter it = processedIDs.iter(); it.valid(); it.advance()) {
- DBID id = it.getDBID();
- D l_i = lambda.get(id);
- D lp_i = lambda.get(pi.get(id));
+ for (DBIDIter it = processedIDs.iter(); it.valid(); it.advance()) {
+ D l_i = lambda.get(it);
+ pi.assignVar(it, p_i); // p_i = pi(it)
+ D lp_i = lambda.get(p_i);
// if L(i) >= L(P(i))
- if(l_i.compareTo(lp_i) >= 0) {
+ if (l_i.compareTo(lp_i) >= 0) {
// P(i) = n+1
- pi.put(id, newID);
+ pi.put(it, id);
}
}
}
- private DBID lastObjectInCluster(DBID id, D stopdist, final DataStore<DBID> pi, final DataStore<D> lambda) {
- if(stopdist == null) {
- return id;
+ /**
+ * First step: Initialize P(id) = id, L(id) = infinity.
+ *
+ * @param id the id of the object to be inserted into the pointer
+ * representation
+ * @param pi Pi data store
+ * @param lambda Lambda data store
+ */
+ private void step1double(DBIDRef id, WritableDBIDDataStore pi, WritableDoubleDistanceDataStore lambda) {
+ // P(n+1) = n+1:
+ pi.put(id, id);
+ // L(n+1) = infinity
+ lambda.putDouble(id, Double.POSITIVE_INFINITY);
+ }
+
+ /**
+ * Second step: Determine the pairwise distances from all objects in the
+ * pointer representation to the new object with the specified id.
+ *
+ * @param id the id of the object to be inserted into the pointer
+ * representation
+ * @param processedIDs the already processed ids
+ * @param m Data store
+ * @param relation Data relation
+ * @param distFunc Distance function to use
+ */
+ private void step2double(DBIDRef id, DBIDs processedIDs, Relation<? extends O> relation, PrimitiveDoubleDistanceFunction<? super O> distFunc, WritableDoubleDistanceDataStore m) {
+ O newObj = relation.get(id);
+ for (DBIDIter it = processedIDs.iter(); it.valid(); it.advance()) {
+ // M(i) = dist(i, n+1)
+ m.putDouble(it, distFunc.doubleDistance(relation.get(it), newObj));
+ }
+ }
+
+ /**
+ * Third step: Determine the values for P and L
+ *
+ * @param id the id of the object to be inserted into the pointer
+ * representation
+ * @param pi Pi data store
+ * @param lambda Lambda data store
+ * @param processedIDs the already processed ids
+ * @param m Data store
+ */
+ private void step3double(DBIDRef id, WritableDBIDDataStore pi, WritableDoubleDistanceDataStore lambda, DBIDs processedIDs, WritableDoubleDistanceDataStore m) {
+ DBIDVar p_i = DBIDUtil.newVar();
+ // for i = 1..n
+ for (DBIDIter it = processedIDs.iter(); it.valid(); it.advance()) {
+ double l_i = lambda.doubleValue(it);
+ double m_i = m.doubleValue(it);
+ pi.assignVar(it, p_i); // p_i = pi(it)
+ double mp_i = m.doubleValue(p_i);
+
+ // if L(i) >= M(i)
+ if (l_i >= m_i) {
+ // M(P(i)) = min { M(P(i)), L(i) }
+ m.putDouble(p_i, Math.min(mp_i, l_i));
+
+ // L(i) = M(i)
+ lambda.putDouble(it, m_i);
+
+ // P(i) = n+1;
+ pi.put(it, id);
+ } else {
+ // M(P(i)) = min { M(P(i)), M(i) }
+ m.putDouble(p_i, Math.min(mp_i, m_i));
+ }
}
+ }
+
+ /**
+ * Fourth step: Actualize the clusters if necessary
+ *
+ * @param id the id of the current object
+ * @param pi Pi data store
+ * @param lambda Lambda data store
+ * @param processedIDs the already processed ids
+ */
+ private void step4double(DBIDRef id, WritableDBIDDataStore pi, WritableDoubleDistanceDataStore lambda, DBIDs processedIDs) {
+ DBIDVar p_i = DBIDUtil.newVar();
+ // for i = 1..n
+ for (DBIDIter it = processedIDs.iter(); it.valid(); it.advance()) {
+ double l_i = lambda.doubleValue(it);
+ pi.assignVar(it, p_i); // p_i = pi(it)
+ double lp_i = lambda.doubleValue(p_i);
- DBID currentID = id;
- while(lambda.get(currentID).compareTo(stopdist) < 1) {
- currentID = pi.get(currentID);
+ // if L(i) >= L(P(i))
+ if (l_i >= lp_i) {
+ // P(i) = n+1
+ pi.put(it, id);
+ }
}
- return currentID;
}
/**
@@ -286,167 +386,148 @@ public class SLINK<O, D extends Distance<D>> extends AbstractDistanceBasedAlgori
*
* @return Hierarchical clustering
*/
- private Clustering<DendrogramModel<D>> extractClusters(DBIDs ids, final DataStore<DBID> pi, final DataStore<D> lambda, int minclusters) {
- FiniteProgress progress = logger.isVerbose() ? new FiniteProgress("Extracting clusters", ids.size(), logger) : null;
+ private Clustering<DendrogramModel<D>> extractClusters(DBIDs ids, final DBIDDataStore pi, final DataStore<D> lambda, int minclusters) {
+ FiniteProgress progress = LOG.isVerbose() ? new FiniteProgress("Extracting clusters", ids.size(), LOG) : null;
+ D nulldist = getDistanceFunction().getDistanceFactory().nullDistance();
- // stopdist
- D stopdist = null;
- // sort by lambda
+ // Sort DBIDs by lambda. We need this for two things:
+ // a) to determine the stop distance from "minclusters" parameter
+ // b) to process arrows in decreasing / increasing order
ArrayModifiableDBIDs order = DBIDUtil.newArray(ids);
order.sort(new CompareByLambda<D>(lambda));
- int index = ids.size() - minclusters - 1;
- while(index >= 0) {
- if(lambda.get(order.get(index)).equals(lambda.get(order.get(index + 1)))) {
- index--;
- }
- else {
- stopdist = lambda.get(order.get(index));
- break;
- }
- }
- // extract the child clusters
- Map<DBID, ModifiableDBIDs> cluster_ids = new HashMap<DBID, ModifiableDBIDs>();
- Map<DBID, D> cluster_distances = new HashMap<DBID, D>();
- for(DBIDIter it = ids.iter(); it.valid(); it.advance()) {
- DBID id = it.getDBID();
- DBID lastObjectInCluster = lastObjectInCluster(id, stopdist, pi, lambda);
- ModifiableDBIDs cluster = cluster_ids.get(lastObjectInCluster);
- if(cluster == null) {
- cluster = DBIDUtil.newArray();
- cluster_ids.put(lastObjectInCluster, cluster);
+ // Stop distance:
+ final D stopdist = (minclusters > 0) ? lambda.get(order.get(ids.size() - minclusters)) : null;
+
+ // The initial pass is top-down.
+ DBIDArrayIter it = order.iter();
+ int split = (minclusters > 0) ? Math.max(ids.size() - minclusters, 0) : 0;
+ // Tie handling: decrement split.
+ if (stopdist != null) {
+ while (split > 0) {
+ it.seek(split - 1);
+ if (stopdist.compareTo(lambda.get(it)) == 0) {
+ split--;
+ minclusters++;
+ } else {
+ break;
+ }
}
- cluster.add(id);
+ }
- D lambda_id = lambda.get(id);
- if(stopdist != null && lambda_id.compareTo(stopdist) <= 0 && (cluster_distances.get(lastObjectInCluster) == null || lambda_id.compareTo(cluster_distances.get(lastObjectInCluster)) > 0)) {
- cluster_distances.put(lastObjectInCluster, lambda_id);
+ // Extract the child clusters
+ int cnum = 0;
+ int expcnum = Math.max(0, minclusters);
+ WritableIntegerDataStore cluster_map = DataStoreUtil.makeIntegerStorage(ids, DataStoreFactory.HINT_TEMP, -1);
+ ArrayList<ModifiableDBIDs> cluster_dbids = new ArrayList<ModifiableDBIDs>(expcnum);
+ ArrayList<D> cluster_dist = new ArrayList<D>(expcnum);
+ ArrayModifiableDBIDs cluster_leads = DBIDUtil.newArray(expcnum);
+
+ DBIDVar succ = DBIDUtil.newVar(); // Variable for successor.
+ // Go backwards on the lower part.
+ for (it.seek(split - 1); it.valid(); it.retract()) {
+ D dist = lambda.get(it); // Distance to successor
+ pi.assignVar(it, succ); // succ = pi(it)
+ int clusterid = cluster_map.intValue(succ);
+ // Successor cluster has already been created:
+ if (clusterid >= 0) {
+ cluster_dbids.get(clusterid).add(it);
+ cluster_map.putInt(it, clusterid);
+ // Update distance to maximum encountered:
+ if (cluster_dist.get(clusterid).compareTo(dist) < 0) {
+ cluster_dist.set(clusterid, dist);
+ }
+ } else {
+ // Need to start a new cluster:
+ clusterid = cnum; // next cluster number.
+ ModifiableDBIDs cids = DBIDUtil.newArray();
+ // Add element and successor as initial members:
+ cids.add(succ);
+ cluster_map.putInt(succ, clusterid);
+ cids.add(it);
+ cluster_map.putInt(it, clusterid);
+ // Store new cluster.
+ cluster_dbids.add(cids);
+ cluster_leads.add(succ);
+ cluster_dist.add(dist);
+ cnum++;
}
// Decrement counter
- if(progress != null) {
- progress.incrementProcessed(logger);
+ if (progress != null) {
+ progress.incrementProcessed(LOG);
}
}
- if(progress != null) {
- progress.ensureCompleted(logger);
- }
-
- // build hierarchy
- final Clustering<DendrogramModel<D>> dendrogram = new Clustering<DendrogramModel<D>>("Single-Link-Dendrogram", "slink-dendrogram");
+ // Build a hierarchy out of these clusters.
+ Cluster<DendrogramModel<D>> root = null;
ModifiableHierarchy<Cluster<DendrogramModel<D>>> hier = new HierarchyHashmapList<Cluster<DendrogramModel<D>>>();
- Cluster<DendrogramModel<D>> root = root(cluster_ids, cluster_distances, pi, lambda, hier, progress);
- dendrogram.addCluster(root);
-
- return dendrogram;
- }
-
- private Cluster<DendrogramModel<D>> root(Map<DBID, ModifiableDBIDs> cluster_ids, Map<DBID, D> cluster_distances, final DataStore<DBID> pi, final DataStore<D> lambda, ModifiableHierarchy<Cluster<DendrogramModel<D>>> hier, FiniteProgress progress) {
- if(cluster_ids.size() == 1) {
- DBID id = cluster_ids.keySet().iterator().next();
- String name = "cluster_" + id + "_" + cluster_distances.get(id);
- return new Cluster<DendrogramModel<D>>(name, cluster_ids.get(id), new DendrogramModel<D>(cluster_distances.get(id)), hier);
- }
-
- // sort leafs by lambda
- List<Pair<DBID, D>> leafs = new ArrayList<Pair<DBID, D>>(cluster_ids.size());
- for(DBID id : cluster_ids.keySet()) {
- leafs.add(new Pair<DBID, D>(id, lambda.get(id)));
- }
-
- Collections.sort(leafs, new Comparator<Pair<DBID, D>>() {
- @Override
- public int compare(Pair<DBID, D> o1, Pair<DBID, D> o2) {
- D k1 = lambda.get(o1.first);
- D k2 = lambda.get(o2.first);
- if(k1 == null && k2 == null) {
- return 0;
- }
- else if(k1 == null) {
- return -1;
- }
- else if(k2 == null) {
- return 1;
- }
- else {
- return k1.compareTo(k2);
- }
+ ArrayList<Cluster<DendrogramModel<D>>> clusters = new ArrayList<Cluster<DendrogramModel<D>>>(ids.size() + expcnum - split);
+ // Convert initial clusters to cluster objects
+ {
+ int i = 0;
+ for (DBIDIter it2 = cluster_leads.iter(); it2.valid(); it2.advance(), i++) {
+ clusters.add(makeCluster(it2, cluster_dist.get(i), cluster_dbids.get(i), hier));
}
- });
-
- // create nodes of the dendrogram
- Cluster<DendrogramModel<D>> parent = null;
- Map<DBID, Cluster<DendrogramModel<D>>> nodes = new HashMap<DBID, Cluster<DendrogramModel<D>>>();
- int nodeCount = 0;
- int clusterCount = 0;
- while(!leafs.isEmpty()) {
- // left child
- Pair<DBID, D> leaf = leafs.remove(0);
- DBID leftID = leaf.first;
- Cluster<DendrogramModel<D>> left = nodes.get(leftID);
- if(left == null) {
- // String name = "cluster_" + leftID + "_" +
- // cluster_distances.get(leftID);
- String name = "cluster_" + (++clusterCount);
- left = new Cluster<DendrogramModel<D>>(name, cluster_ids.get(leftID), new DendrogramModel<D>(cluster_distances.get(leftID)), hier);
- nodes.put(leftID, left);
- }
- // right child
- DBID rightID = pi.get(leftID);
- if(leftID.sameDBID(rightID)) {
- break;
+ cluster_dist = null; // Invalidate
+ cluster_dbids = null; // Invalidate
+ }
+ // Process the upper part, bottom-up.
+ for (it.seek(split); it.valid(); it.advance()) {
+ int clusterid = cluster_map.intValue(it);
+ // The current cluster:
+ final Cluster<DendrogramModel<D>> clus;
+ if (clusterid >= 0) {
+ clus = clusters.get(clusterid);
+ } else {
+ ArrayModifiableDBIDs cids = DBIDUtil.newArray(1);
+ cids.add(it);
+ clus = makeCluster(it, nulldist, cids, hier);
+ // No need to store in clusters: cannot have another incoming pi
+ // pointer!
}
- Cluster<DendrogramModel<D>> right = nodes.get(rightID);
- if(right == null) {
- // String name = "cluster_" + rightID + "_" +
- // cluster_distances.get(rightID);
- String name = "cluster_" + (++clusterCount);
- right = new Cluster<DendrogramModel<D>>(name, cluster_ids.get(rightID), new DendrogramModel<D>(cluster_distances.get(rightID)), hier);
- nodes.put(rightID, right);
+ // The successor to join:
+ pi.assignVar(it, succ); // succ = pi(it)
+ if (DBIDUtil.equal(it, succ)) {
+ assert (root == null);
+ root = clus;
+ } else {
+ // Parent cluster:
+ int parentid = cluster_map.intValue(succ);
+ D depth = lambda.get(it);
+ // Parent cluster exists - merge as a new cluster:
+ if (parentid >= 0) {
+ Cluster<DendrogramModel<D>> pclus = makeCluster(succ, depth, DBIDUtil.EMPTYDBIDS, hier);
+ hier.add(pclus, clusters.get(parentid));
+ hier.add(pclus, clus);
+ clusters.set(parentid, pclus); // Replace existing parent cluster
+ } else {
+ // Create a new, one-element, parent cluster.
+ parentid = cnum;
+ cnum++;
+ ArrayModifiableDBIDs cids = DBIDUtil.newArray(1);
+ cids.add(succ);
+ Cluster<DendrogramModel<D>> pclus = makeCluster(succ, depth, cids, hier);
+ hier.add(pclus, clus);
+ assert (clusters.size() == parentid);
+ clusters.add(pclus); // Remember parent cluster
+ cluster_map.putInt(succ, parentid); // Reference
+ }
}
- // parent
- // String name = "node" + (++nodeCount) + "_" + leaf.second;
- String name = "node_" + (++nodeCount);
- parent = createParent(name, lastAncestor(left, hier), lastAncestor(right, hier), leaf.second, hier);
// Decrement counter
- if(progress != null) {
- progress.incrementProcessed(logger);
+ if (progress != null) {
+ progress.incrementProcessed(LOG);
}
}
- // root = parent
- return parent;
- }
- /**
- * Determines recursively the last ancestor of the specified cluster.
- *
- * @param cluster the child
- * @param hier the cluster hierarchy
- * @return the (currently) last ancestor
- */
- private Cluster<DendrogramModel<D>> lastAncestor(Cluster<DendrogramModel<D>> cluster, ModifiableHierarchy<Cluster<DendrogramModel<D>>> hier) {
- List<Cluster<DendrogramModel<D>>> parents = hier.getParents(cluster);
- if(parents.isEmpty()) {
- return cluster;
+ if (progress != null) {
+ progress.ensureCompleted(LOG);
}
- else {
- if(parents.size() > 1) {
- logger.warning("More than one parent in Single-Link dendrogram: " + cluster + " parents: " + parents);
- return null;
- }
- return lastAncestor(parents.get(0), hier);
- }
- }
-
- private Cluster<DendrogramModel<D>> createParent(String name, Cluster<DendrogramModel<D>> leftChild, Cluster<DendrogramModel<D>> rightChild, D distance, ModifiableHierarchy<Cluster<DendrogramModel<D>>> hier) {
- // DBIDs ids = DBIDUtil.union(leftChild.getIDs(), rightChild.getIDs());
- Cluster<DendrogramModel<D>> parent = new Cluster<DendrogramModel<D>>(name, DBIDUtil.EMPTYDBIDS, new DendrogramModel<D>(distance), hier);
-
- hier.add(parent, leftChild);
- hier.add(parent, rightChild);
+ // build hierarchy
+ final Clustering<DendrogramModel<D>> dendrogram = new Clustering<DendrogramModel<D>>("Single-Link-Dendrogram", "slink-dendrogram");
+ dendrogram.addCluster(root);
- return parent;
+ return dendrogram;
}
/**
@@ -459,119 +540,174 @@ public class SLINK<O, D extends Distance<D>> extends AbstractDistanceBasedAlgori
*
* @return Hierarchical clustering
*/
- @SuppressWarnings("unused")
- private Clustering<Model> extractClusters_erich(DBIDs ids, final DataStore<DBID> pi, final DataStore<D> lambda, int minclusters) {
- // extract a hierarchical clustering
- ArrayModifiableDBIDs order = DBIDUtil.newArray(ids);
- // sort by lambda
- order.sort(new CompareByLambda<D>(lambda));
- D curdist = null;
+ private Clustering<DendrogramModel<D>> extractClustersDouble(DBIDs ids, final DBIDDataStore pi, final DoubleDistanceDataStore lambda, int minclusters) {
+ FiniteProgress progress = LOG.isVerbose() ? new FiniteProgress("Extracting clusters", ids.size(), LOG) : null;
+ D nulldist = getDistanceFunction().getDistanceFactory().nullDistance();
- D stopdist = null;
- if(minclusters < ids.size()) {
- stopdist = lambda.get(order.get(ids.size() - minclusters));
+ // Sort DBIDs by lambda. We need this for two things:
+ // a) to determine the stop distance from "minclusters" parameter
+ // b) to process arrows in decreasing / increasing order
+ ArrayModifiableDBIDs order = DBIDUtil.newArray(ids);
+ order.sort(new CompareByDoubleLambda(lambda));
+
+ // Stop distance:
+ final double stopdist = (minclusters > 0) ? lambda.doubleValue(order.get(ids.size() - minclusters)) : Double.POSITIVE_INFINITY;
+
+ // The initial pass is top-down.
+ DBIDArrayIter it = order.iter();
+ int split = (minclusters > 0) ? Math.max(ids.size() - minclusters, 0) : 0;
+ // Tie handling: decrement split.
+ if (minclusters > 0) {
+ while (split > 0) {
+ it.seek(split - 1);
+ if (stopdist <= lambda.doubleValue(it)) {
+ split--;
+ minclusters++;
+ } else {
+ break;
+ }
+ }
}
- ModifiableHierarchy<Cluster<Model>> hier = new HierarchyHashmapList<Cluster<Model>>();
- Map<DBID, Cluster<Model>> clusters = new HashMap<DBID, Cluster<Model>>();
- Map<DBID, ModifiableDBIDs> cids = new HashMap<DBID, ModifiableDBIDs>();
-
- FiniteProgress progress = logger.isVerbose() ? new FiniteProgress("Extracting clusters", ids.size(), logger) : null;
-
- for(DBIDIter it = order.iter(); it.valid(); it.advance()) {
- DBID dest = pi.get(it);
- D l = lambda.get(it);
- // logger.debugFine("DBID " + cur.toString() + " dist: " + l.toString());
- if(stopdist != null && stopdist.compareTo(l) > 0) {
- DBID cur = it.getDBID();
- ModifiableDBIDs curset = cids.remove(cur);
- ModifiableDBIDs destset = cids.get(dest);
- if(destset == null) {
- if(curset != null) {
- destset = curset;
- }
- else {
- destset = DBIDUtil.newHashSet();
- destset.add(cur);
- }
- destset.add(dest);
- cids.put(dest, destset);
- }
- else {
- if(curset != null) {
- destset.addDBIDs(curset);
- }
- else {
- destset.add(cur);
- }
+ // Extract the child clusters
+ int cnum = 0;
+ int expcnum = Math.max(0, minclusters);
+ WritableIntegerDataStore cluster_map = DataStoreUtil.makeIntegerStorage(ids, DataStoreFactory.HINT_TEMP, -1);
+ ArrayList<ModifiableDBIDs> cluster_dbids = new ArrayList<ModifiableDBIDs>(expcnum);
+ TDoubleArrayList cluster_dist = new TDoubleArrayList(expcnum);
+ ArrayModifiableDBIDs cluster_leads = DBIDUtil.newArray(expcnum);
+
+ DBIDVar succ = DBIDUtil.newVar(); // Variable for successor.
+ // Go backwards on the lower part.
+ for (it.seek(split - 1); it.valid(); it.retract()) {
+ double dist = lambda.doubleValue(it); // Distance to successor
+ pi.assignVar(it, succ); // succ = pi(it)
+ int clusterid = cluster_map.intValue(succ);
+ // Successor cluster has already been created:
+ if (clusterid >= 0) {
+ cluster_dbids.get(clusterid).add(it);
+ cluster_map.putInt(it, clusterid);
+ // Update distance to maximum encountered:
+ if (cluster_dist.get(clusterid) < dist) {
+ cluster_dist.set(clusterid, dist);
}
- curdist = l;
+ } else {
+ // Need to start a new cluster:
+ clusterid = cnum; // next cluster number.
+ ModifiableDBIDs cids = DBIDUtil.newArray();
+ // Add element and successor as initial members:
+ cids.add(succ);
+ cluster_map.putInt(succ, clusterid);
+ cids.add(it);
+ cluster_map.putInt(it, clusterid);
+ // Store new cluster.
+ cluster_dbids.add(cids);
+ cluster_leads.add(succ);
+ cluster_dist.add(dist);
+ cnum++;
}
- else {
- if(curdist == null || l.compareTo(curdist) > 0) {
- // New distance level reached. Post-process the current objects
- for(Entry<DBID, ModifiableDBIDs> ent : cids.entrySet()) {
- DBID key = ent.getKey();
- ModifiableDBIDs clusids = ent.getValue();
- // Make a new cluster
- String cname = "Cluster_" + key.toString() + "_" + curdist.toString();
- Cluster<Model> cluster = new Cluster<Model>(cname, clusids, ClusterModel.CLUSTER, hier);
- // Collect child clusters and clean up the cluster ids, keeping only
- // "new" objects.
- for(DBIDMIter iter = clusids.iter(); iter.valid(); iter.advance()) {
- Cluster<Model> chiclus = clusters.get(iter);
- if(chiclus != null) {
- hier.add(cluster, chiclus);
- clusters.remove(iter);
- iter.remove();
- }
- }
- clusters.put(key, cluster);
- }
- if(logger.isDebuggingFine()) {
- StringBuffer buf = new StringBuffer();
- buf.append("Number of clusters at depth ");
- buf.append((curdist != null ? curdist.toString() : "null"));
- buf.append(": ").append(clusters.size()).append(" ");
- buf.append("last-objects:");
- for(DBID id : clusters.keySet()) {
- buf.append(" ").append(id.toString());
- }
- logger.debugFine(buf.toString());
- }
- cids.clear();
- curdist = l;
- }
- // Add the current object to the destinations cluster
- {
- ModifiableDBIDs destset = cids.get(dest);
- if(destset == null) {
- destset = DBIDUtil.newHashSet();
- cids.put(dest, destset);
- destset.add(dest);
- }
- destset.add(it);
+
+ // Decrement counter
+ if (progress != null) {
+ progress.incrementProcessed(LOG);
+ }
+ }
+ // Build a hierarchy out of these clusters.
+ Cluster<DendrogramModel<D>> root = null;
+ ModifiableHierarchy<Cluster<DendrogramModel<D>>> hier = new HierarchyHashmapList<Cluster<DendrogramModel<D>>>();
+ ArrayList<Cluster<DendrogramModel<D>>> clusters = new ArrayList<Cluster<DendrogramModel<D>>>(ids.size() + expcnum - split);
+ // Convert initial clusters to cluster objects
+ {
+ int i = 0;
+ for (DBIDIter it2 = cluster_leads.iter(); it2.valid(); it2.advance(), i++) {
+ @SuppressWarnings("unchecked")
+ D depth = (D) new DoubleDistance(cluster_dist.get(i));
+ clusters.add(makeCluster(it2, depth, cluster_dbids.get(i), hier));
+ }
+ cluster_dist = null; // Invalidate
+ cluster_dbids = null; // Invalidate
+ }
+ // Process the upper part, bottom-up.
+ for (it.seek(split); it.valid(); it.advance()) {
+ int clusterid = cluster_map.intValue(it);
+ // The current cluster:
+ final Cluster<DendrogramModel<D>> clus;
+ if (clusterid >= 0) {
+ clus = clusters.get(clusterid);
+ } else {
+ ArrayModifiableDBIDs cids = DBIDUtil.newArray(1);
+ cids.add(it);
+ clus = makeCluster(it, nulldist, cids, hier);
+ // No need to store in clusters: cannot have another incoming pi
+ // pointer!
+ }
+ // The successor to join:
+ pi.assignVar(it, succ); // succ = pi(it)
+ if (DBIDUtil.equal(it, succ)) {
+ assert (root == null);
+ root = clus;
+ } else {
+ // Parent cluster:
+ int parentid = cluster_map.intValue(succ);
+ @SuppressWarnings("unchecked")
+ D depth = (D) new DoubleDistance(lambda.doubleValue(it));
+ // Parent cluster exists - merge as a new cluster:
+ if (parentid >= 0) {
+ Cluster<DendrogramModel<D>> pclus = makeCluster(succ, depth, DBIDUtil.EMPTYDBIDS, hier);
+ hier.add(pclus, clusters.get(parentid));
+ hier.add(pclus, clus);
+ clusters.set(parentid, pclus); // Replace existing parent cluster
+ } else {
+ // Create a new, one-element, parent cluster.
+ parentid = cnum;
+ cnum++;
+ ArrayModifiableDBIDs cids = DBIDUtil.newArray(1);
+ cids.add(succ);
+ Cluster<DendrogramModel<D>> pclus = makeCluster(succ, depth, cids, hier);
+ hier.add(pclus, clus);
+ assert (clusters.size() == parentid);
+ clusters.add(pclus); // Remember parent cluster
+ cluster_map.putInt(succ, parentid); // Reference
}
}
+
// Decrement counter
- if(progress != null) {
- progress.incrementProcessed(logger);
+ if (progress != null) {
+ progress.incrementProcessed(LOG);
}
}
- if(progress != null) {
- progress.ensureCompleted(logger);
- }
- // There should be one cluster remaining at infinite distance...
- if(clusters.size() != 1) {
- logger.warning("Single-link is expected to have a single cluster at the top level!");
- return null;
+
+ if (progress != null) {
+ progress.ensureCompleted(LOG);
}
- final Clustering<Model> clustering = new Clustering<Model>("Single-Link-Clustering", "slink-clustering");
- // FIXME: validate this is works correctly for a single-object dataset!
- for(Cluster<Model> cluster : clusters.values()) {
- clustering.addCluster(cluster);
+ // build hierarchy
+ final Clustering<DendrogramModel<D>> dendrogram = new Clustering<DendrogramModel<D>>("Single-Link-Dendrogram", "slink-dendrogram");
+ dendrogram.addCluster(root);
+
+ return dendrogram;
+ }
+
+ /**
+ * Make the cluster for the given object
+ *
+ * @param lead Leading object
+ * @param depth Linkage depth
+ * @param members Member objects
+ * @param hier Cluster hierarchy
+ * @return Cluster
+ */
+ private Cluster<DendrogramModel<D>> makeCluster(DBIDRef lead, D depth, DBIDs members, ModifiableHierarchy<Cluster<DendrogramModel<D>>> hier) {
+ final String name;
+ if (members.size() == 0) {
+ name = "merge_" + lead + "_" + depth;
+ } else if (depth.isInfiniteDistance()) {
+ assert (members.contains(lead));
+ name = "object_" + lead;
+ } else {
+ name = "cluster_" + lead + "_" + depth;
}
- return clustering;
+ Cluster<DendrogramModel<D>> cluster = new Cluster<DendrogramModel<D>>(name, members, new DendrogramModel<D>(depth), hier);
+ return cluster;
}
@Override
@@ -581,7 +717,7 @@ public class SLINK<O, D extends Distance<D>> extends AbstractDistanceBasedAlgori
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -593,7 +729,7 @@ public class SLINK<O, D extends Distance<D>> extends AbstractDistanceBasedAlgori
*
* @param <D> Distance type
*/
- private static final class CompareByLambda<D extends Distance<D>> implements Comparator<DBID> {
+ private static final class CompareByLambda<D extends Distance<D>> implements Comparator<DBIDRef> {
/**
* Lambda storage
*/
@@ -609,7 +745,7 @@ public class SLINK<O, D extends Distance<D>> extends AbstractDistanceBasedAlgori
}
@Override
- public int compare(DBID id1, DBID id2) {
+ public int compare(DBIDRef id1, DBIDRef id2) {
D k1 = lambda.get(id1);
D k2 = lambda.get(id2);
assert (k1 != null);
@@ -619,6 +755,36 @@ public class SLINK<O, D extends Distance<D>> extends AbstractDistanceBasedAlgori
}
/**
+ * Order a DBID collection by the lambda value.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ private static final class CompareByDoubleLambda implements Comparator<DBIDRef> {
+ /**
+ * Lambda storage
+ */
+ private final DoubleDistanceDataStore lambda;
+
+ /**
+ * Constructor.
+ *
+ * @param lambda Lambda storage
+ */
+ protected CompareByDoubleLambda(DoubleDistanceDataStore lambda) {
+ this.lambda = lambda;
+ }
+
+ @Override
+ public int compare(DBIDRef id1, DBIDRef id2) {
+ double k1 = lambda.doubleValue(id1);
+ double k2 = lambda.doubleValue(id2);
+ return Double.compare(k1, k2);
+ }
+ }
+
+ /**
* Parameterization class.
*
* @author Erich Schubert
@@ -626,14 +792,21 @@ public class SLINK<O, D extends Distance<D>> extends AbstractDistanceBasedAlgori
* @apiviz.exclude
*/
public static class Parameterizer<O, D extends Distance<D>> extends AbstractDistanceBasedAlgorithm.Parameterizer<O, D> {
- protected Integer minclusters = null;
+ /**
+ * The minimum number of clusters to extract
+ */
+ public static final OptionID SLINK_MINCLUSTERS_ID = new OptionID("slink.minclusters", "The maximum number of clusters to extract.");
+
+ protected int minclusters = -1;
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- IntParameter minclustersP = new IntParameter(SLINK_MINCLUSTERS_ID, new GreaterEqualConstraint(1), true);
- if(config.grab(minclustersP)) {
- minclusters = minclustersP.getValue();
+ IntParameter minclustersP = new IntParameter(SLINK_MINCLUSTERS_ID);
+ minclustersP.addConstraint(new GreaterEqualConstraint(1));
+ minclustersP.setOptional(true);
+ if (config.grab(minclustersP)) {
+ minclusters = minclustersP.intValue();
}
}
@@ -642,4 +815,4 @@ public class SLINK<O, D extends Distance<D>> extends AbstractDistanceBasedAlgori
return new SLINK<O, D>(distanceFunction, minclusters);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/SNNClustering.java b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/SNNClustering.java
index ae612b2a..f3b59c42 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/SNNClustering.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/SNNClustering.java
@@ -81,13 +81,13 @@ public class SNNClustering<O> extends AbstractAlgorithm<Clustering<Model>> imple
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(SNNClustering.class);
+ private static final Logging LOG = Logging.getLogger(SNNClustering.class);
/**
* Parameter to specify the minimum SNN density, must be an integer greater
* than 0.
*/
- public static final OptionID EPSILON_ID = OptionID.getOrCreateOptionID("snn.epsilon", "The minimum SNN density.");
+ public static final OptionID EPSILON_ID = new OptionID("snn.epsilon", "The minimum SNN density.");
/**
* Holds the value of {@link #EPSILON_ID}.
@@ -98,7 +98,7 @@ public class SNNClustering<O> extends AbstractAlgorithm<Clustering<Model>> imple
* Parameter to specify the threshold for minimum number of points in the
* epsilon-SNN-neighborhood of a point, must be an integer greater than 0.
*/
- public static final OptionID MINPTS_ID = OptionID.getOrCreateOptionID("snn.minpts", "Threshold for minimum number of points in " + "the epsilon-SNN-neighborhood of a point.");
+ public static final OptionID MINPTS_ID = new OptionID("snn.minpts", "Threshold for minimum number of points in " + "the epsilon-SNN-neighborhood of a point.");
/**
* Holds the value of {@link #MINPTS_ID}.
@@ -149,22 +149,22 @@ public class SNNClustering<O> extends AbstractAlgorithm<Clustering<Model>> imple
public Clustering<Model> run(Database database, Relation<O> relation) {
SimilarityQuery<O, IntegerDistance> snnInstance = similarityFunction.instantiate(relation);
- FiniteProgress objprog = logger.isVerbose() ? new FiniteProgress("SNNClustering", relation.size(), logger) : null;
- IndefiniteProgress clusprog = logger.isVerbose() ? new IndefiniteProgress("Number of clusters", logger) : null;
+ FiniteProgress objprog = LOG.isVerbose() ? new FiniteProgress("SNNClustering", relation.size(), LOG) : null;
+ IndefiniteProgress clusprog = LOG.isVerbose() ? new IndefiniteProgress("Number of clusters", LOG) : null;
resultList = new ArrayList<ModifiableDBIDs>();
noise = DBIDUtil.newHashSet();
processedIDs = DBIDUtil.newHashSet(relation.size());
if(relation.size() >= minpts) {
for(DBIDIter id = snnInstance.getRelation().iterDBIDs(); id.valid(); id.advance()) {
if(!processedIDs.contains(id)) {
- expandCluster(snnInstance, id.getDBID(), objprog, clusprog);
+ expandCluster(snnInstance, DBIDUtil.deref(id), objprog, clusprog);
if(processedIDs.size() == relation.size() && noise.size() == 0) {
break;
}
}
if(objprog != null && clusprog != null) {
- objprog.setProcessed(processedIDs.size(), logger);
- clusprog.setProcessed(resultList.size(), logger);
+ objprog.setProcessed(processedIDs.size(), LOG);
+ clusprog.setProcessed(resultList.size(), LOG);
}
}
}
@@ -172,15 +172,15 @@ public class SNNClustering<O> extends AbstractAlgorithm<Clustering<Model>> imple
for(DBIDIter id = snnInstance.getRelation().iterDBIDs(); id.valid(); id.advance()) {
noise.add(id);
if(objprog != null && clusprog != null) {
- objprog.setProcessed(noise.size(), logger);
- clusprog.setProcessed(resultList.size(), logger);
+ objprog.setProcessed(noise.size(), LOG);
+ clusprog.setProcessed(resultList.size(), LOG);
}
}
}
// Finish progress logging
if(objprog != null && clusprog != null) {
- objprog.ensureCompleted(logger);
- clusprog.setCompleted(logger);
+ objprog.ensureCompleted(LOG);
+ clusprog.setCompleted(LOG);
}
Clustering<Model> result = new Clustering<Model>("Shared-Nearest-Neighbor Clustering", "snn-clustering");
@@ -230,8 +230,8 @@ public class SNNClustering<O> extends AbstractAlgorithm<Clustering<Model>> imple
noise.add(startObjectID);
processedIDs.add(startObjectID);
if(objprog != null && clusprog != null) {
- objprog.setProcessed(processedIDs.size(), logger);
- clusprog.setProcessed(resultList.size(), logger);
+ objprog.setProcessed(processedIDs.size(), LOG);
+ clusprog.setProcessed(resultList.size(), LOG);
}
return;
}
@@ -255,26 +255,25 @@ public class SNNClustering<O> extends AbstractAlgorithm<Clustering<Model>> imple
if(neighborhood.size() >= minpts) {
for(DBIDIter iter = neighborhood.iter(); iter.valid(); iter.advance()) {
- DBID p = iter.getDBID();
- boolean inNoise = noise.contains(p);
- boolean unclassified = !processedIDs.contains(p);
+ boolean inNoise = noise.contains(iter);
+ boolean unclassified = !processedIDs.contains(iter);
if(inNoise || unclassified) {
if(unclassified) {
- seeds.add(p);
+ seeds.add(iter);
}
- currentCluster.add(p);
- processedIDs.add(p);
+ currentCluster.add(iter);
+ processedIDs.add(iter);
if(inNoise) {
- noise.remove(p);
+ noise.remove(iter);
}
}
}
}
if(objprog != null && clusprog != null) {
- objprog.setProcessed(processedIDs.size(), logger);
+ objprog.setProcessed(processedIDs.size(), LOG);
int numClusters = currentCluster.size() > minpts ? resultList.size() + 1 : resultList.size();
- clusprog.setProcessed(numClusters, logger);
+ clusprog.setProcessed(numClusters, LOG);
}
if(processedIDs.size() == snnInstance.getRelation().size() && noise.size() == 0) {
@@ -298,7 +297,7 @@ public class SNNClustering<O> extends AbstractAlgorithm<Clustering<Model>> imple
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -328,9 +327,10 @@ public class SNNClustering<O> extends AbstractAlgorithm<Clustering<Model>> imple
epsilon = epsilonP.getValue();
}
- IntParameter minptsP = new IntParameter(MINPTS_ID, new GreaterConstraint(0));
+ IntParameter minptsP = new IntParameter(MINPTS_ID);
+ minptsP.addConstraint(new GreaterConstraint(0));
if(config.grab(minptsP)) {
- minpts = minptsP.getValue();
+ minpts = minptsP.intValue();
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/CASH.java b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/CASH.java
index e4c6a123..1cb1eb0d 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/CASH.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/CASH.java
@@ -23,19 +23,20 @@ package de.lmu.ifi.dbs.elki.algorithm.clustering.correlation;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+import java.util.ArrayList;
import java.util.Arrays;
-import java.util.List;
import de.lmu.ifi.dbs.elki.algorithm.AbstractAlgorithm;
import de.lmu.ifi.dbs.elki.algorithm.DependencyDerivator;
import de.lmu.ifi.dbs.elki.algorithm.clustering.ClusteringAlgorithm;
import de.lmu.ifi.dbs.elki.algorithm.clustering.correlation.cash.CASHInterval;
import de.lmu.ifi.dbs.elki.algorithm.clustering.correlation.cash.CASHIntervalSplit;
+import de.lmu.ifi.dbs.elki.algorithm.clustering.correlation.cash.ParameterizationFunction;
import de.lmu.ifi.dbs.elki.data.Cluster;
import de.lmu.ifi.dbs.elki.data.Clustering;
import de.lmu.ifi.dbs.elki.data.DoubleVector;
import de.lmu.ifi.dbs.elki.data.HyperBoundingBox;
-import de.lmu.ifi.dbs.elki.data.ParameterizationFunction;
+import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.data.model.ClusterModel;
import de.lmu.ifi.dbs.elki.data.model.CorrelationAnalysisSolution;
import de.lmu.ifi.dbs.elki.data.model.LinearEquationModel;
@@ -65,16 +66,13 @@ import de.lmu.ifi.dbs.elki.math.linearalgebra.Matrix;
import de.lmu.ifi.dbs.elki.math.linearalgebra.pca.FirstNEigenPairFilter;
import de.lmu.ifi.dbs.elki.math.linearalgebra.pca.PCAFilteredRunner;
import de.lmu.ifi.dbs.elki.utilities.ClassGenericsUtil;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.Heap;
import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.IntegerPriorityObject;
import de.lmu.ifi.dbs.elki.utilities.documentation.Description;
import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
import de.lmu.ifi.dbs.elki.utilities.documentation.Title;
-import de.lmu.ifi.dbs.elki.utilities.exceptions.UnableToComplyException;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.ParameterException;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterConstraint;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.ListParameterization;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
@@ -86,10 +84,6 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
* Provides the CASH algorithm, an subspace clustering algorithm based on the
* Hough transform.
*
- * <b>Note:</b> CASH requires explicitly setting the input vector type to
- * {@link ParameterizationFunction}:
- * (in the MiniGui, set option: parser.vector-type ParameterizationFunction).
- *
* <p>
* Reference: E. Achtert, C. Böhm, J. David, P. Kröger, A. Zimek: Robust
* clustering in arbitrarily oriented subspaces. <br>
@@ -101,16 +95,18 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
* @apiviz.has CASHInterval
* @apiviz.uses ParameterizationFunction
* @apiviz.has LinearEquationModel
+ *
+ * @param <V> Vector type
*/
// todo elke hierarchy (later)
@Title("CASH: Robust clustering in arbitrarily oriented subspaces")
@Description("Subspace clustering algorithm based on the Hough transform.")
@Reference(authors = "E. Achtert, C. Böhm, J. David, P. Kröger, A. Zimek", title = "Robust clustering in arbitraily oriented subspaces", booktitle = "Proc. 8th SIAM Int. Conf. on Data Mining (SDM'08), Atlanta, GA, 2008", url = "http://www.siam.org/proceedings/datamining/2008/dm08_69_AchtertBoehmDavidKroegerZimek.pdf")
-public class CASH extends AbstractAlgorithm<Clustering<Model>> implements ClusteringAlgorithm<Clustering<Model>> {
+public class CASH<V extends NumberVector<?>> extends AbstractAlgorithm<Clustering<Model>> implements ClusteringAlgorithm<Clustering<Model>> {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(CASH.class);
+ private static final Logging LOG = Logging.getLogger(CASH.class);
/**
* Parameter to specify the threshold for minimum number of points in a
@@ -119,7 +115,7 @@ public class CASH extends AbstractAlgorithm<Clustering<Model>> implements Cluste
* Key: {@code -cash.minpts}
* </p>
*/
- public static final OptionID MINPTS_ID = OptionID.getOrCreateOptionID("cash.minpts", "Threshold for minimum number of points in a cluster.");
+ public static final OptionID MINPTS_ID = new OptionID("cash.minpts", "Threshold for minimum number of points in a cluster.");
/**
* Parameter to specify the maximum level for splitting the hypercube, must be
@@ -128,7 +124,7 @@ public class CASH extends AbstractAlgorithm<Clustering<Model>> implements Cluste
* Key: {@code -cash.maxlevel}
* </p>
*/
- public static final OptionID MAXLEVEL_ID = OptionID.getOrCreateOptionID("cash.maxlevel", "The maximum level for splitting the hypercube.");
+ public static final OptionID MAXLEVEL_ID = new OptionID("cash.maxlevel", "The maximum level for splitting the hypercube.");
/**
* Parameter to specify the minimum dimensionality of the subspaces to be
@@ -140,7 +136,7 @@ public class CASH extends AbstractAlgorithm<Clustering<Model>> implements Cluste
* Key: {@code -cash.mindim}
* </p>
*/
- public static final OptionID MINDIM_ID = OptionID.getOrCreateOptionID("cash.mindim", "The minimum dimensionality of the subspaces to be found.");
+ public static final OptionID MINDIM_ID = new OptionID("cash.mindim", "The minimum dimensionality of the subspaces to be found.");
/**
* Parameter to specify the maximum jitter for distance values, must be a
@@ -149,7 +145,7 @@ public class CASH extends AbstractAlgorithm<Clustering<Model>> implements Cluste
* Key: {@code -cash.jitter}
* </p>
*/
- public static final OptionID JITTER_ID = OptionID.getOrCreateOptionID("cash.jitter", "The maximum jitter for distance values.");
+ public static final OptionID JITTER_ID = new OptionID("cash.jitter", "The maximum jitter for distance values.");
/**
* Flag to indicate that an adjustment of the applied heuristic for choosing
@@ -158,7 +154,7 @@ public class CASH extends AbstractAlgorithm<Clustering<Model>> implements Cluste
* Key: {@code -cash.adjust}
* </p>
*/
- public static final OptionID ADJUST_ID = OptionID.getOrCreateOptionID("cash.adjust", "Flag to indicate that an adjustment of the applied heuristic for choosing an interval " + "is performed after an interval is selected.");
+ public static final OptionID ADJUST_ID = new OptionID("cash.adjust", "Flag to indicate that an adjustment of the applied heuristic for choosing an interval " + "is performed after an interval is selected.");
/**
* Holds the value of {@link #MINPTS_ID}.
@@ -196,7 +192,7 @@ public class CASH extends AbstractAlgorithm<Clustering<Model>> implements Cluste
private ModifiableDBIDs processedIDs;
/**
- * The entire database
+ * The entire relation.
*/
private Relation<ParameterizationFunction> fulldatabase;
@@ -222,52 +218,61 @@ public class CASH extends AbstractAlgorithm<Clustering<Model>> implements Cluste
* Run CASH on the relation.
*
* @param database Database
- * @param relation Relation
+ * @param vrel Relation
* @return Clustering result
*/
- public Clustering<Model> run(Database database, Relation<ParameterizationFunction> relation) {
- this.fulldatabase = relation;
- if(logger.isVerbose()) {
- StringBuffer msg = new StringBuffer();
- msg.append("DB size: ").append(relation.size());
+ public Clustering<Model> run(Database database, Relation<V> vrel) {
+ this.fulldatabase = preprocess(database, vrel);
+ if (LOG.isVerbose()) {
+ StringBuilder msg = new StringBuilder();
+ msg.append("DB size: ").append(fulldatabase.size());
msg.append("\nmin Dim: ").append(minDim);
- logger.verbose(msg.toString());
+ LOG.verbose(msg.toString());
}
- try {
- processedIDs = DBIDUtil.newHashSet(relation.size());
- noiseDim = DatabaseUtil.dimensionality(relation);
+ processedIDs = DBIDUtil.newHashSet(fulldatabase.size());
+ noiseDim = dimensionality(fulldatabase);
- FiniteProgress progress = logger.isVerbose() ? new FiniteProgress("CASH Clustering", relation.size(), logger) : null;
- Clustering<Model> result = doRun(relation, progress);
- if(progress != null) {
- progress.ensureCompleted(logger);
- }
+ FiniteProgress progress = LOG.isVerbose() ? new FiniteProgress("CASH Clustering", fulldatabase.size(), LOG) : null;
+ Clustering<Model> result = doRun(fulldatabase, progress);
+ if (progress != null) {
+ progress.ensureCompleted(LOG);
+ }
- if(logger.isVerbose()) {
- StringBuffer msg = new StringBuffer();
- for(Cluster<Model> c : result.getAllClusters()) {
- if(c.getModel() instanceof LinearEquationModel) {
- LinearEquationModel s = (LinearEquationModel) c.getModel();
- msg.append("\n Cluster: Dim: " + s.getLes().subspacedim() + " size: " + c.size());
- }
- else {
- msg.append("\n Cluster: " + c.getModel().getClass().getName() + " size: " + c.size());
- }
+ if (LOG.isVerbose()) {
+ StringBuilder msg = new StringBuilder();
+ for (Cluster<Model> c : result.getAllClusters()) {
+ if (c.getModel() instanceof LinearEquationModel) {
+ LinearEquationModel s = (LinearEquationModel) c.getModel();
+ msg.append("\n Cluster: Dim: " + s.getLes().subspacedim() + " size: " + c.size());
+ } else {
+ msg.append("\n Cluster: " + c.getModel().getClass().getName() + " size: " + c.size());
}
- logger.verbose(msg.toString());
}
- return result;
- }
- catch(UnableToComplyException e) {
- throw new IllegalStateException(e);
+ LOG.verbose(msg.toString());
}
- catch(ParameterException e) {
- throw new IllegalStateException(e);
- }
- catch(NonNumericFeaturesException e) {
- throw new IllegalStateException(e);
+ return result;
+ }
+
+ /**
+ * Preprocess the dataset, precomputing the parameterization functions.
+ *
+ * @param db Database
+ * @param vrel Vector relation
+ * @return Preprocessed relation
+ */
+ private Relation<ParameterizationFunction> preprocess(Database db, Relation<V> vrel) {
+ DBIDs ids = vrel.getDBIDs();
+ SimpleTypeInformation<ParameterizationFunction> type = new SimpleTypeInformation<ParameterizationFunction>(ParameterizationFunction.class);
+ MaterializedRelation<ParameterizationFunction> prep = new MaterializedRelation<ParameterizationFunction>(db, type, ids);
+
+ // Project
+ for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
+ ParameterizationFunction f = new ParameterizationFunction(vrel.get(iter));
+ prep.set(iter, f);
}
+
+ return prep;
}
/**
@@ -277,69 +282,62 @@ public class CASH extends AbstractAlgorithm<Clustering<Model>> implements Cluste
* @param relation the Relation to run the CASH algorithm on
* @param progress the progress object for verbose messages
* @return a mapping of subspace dimensionalities to clusters
- * @throws UnableToComplyException if an error according to the database
- * occurs
- * @throws ParameterException if the parameter setting is wrong
- * @throws NonNumericFeaturesException if non numeric feature vectors are used
*/
- private Clustering<Model> doRun(Relation<ParameterizationFunction> relation, FiniteProgress progress) throws UnableToComplyException, ParameterException, NonNumericFeaturesException {
+ private Clustering<Model> doRun(Relation<ParameterizationFunction> relation, FiniteProgress progress) {
Clustering<Model> res = new Clustering<Model>("CASH clustering", "cash-clustering");
- final int dim = DatabaseUtil.dimensionality(relation);
+ final int dim = dimensionality(relation);
// init heap
Heap<IntegerPriorityObject<CASHInterval>> heap = new Heap<IntegerPriorityObject<CASHInterval>>();
ModifiableDBIDs noiseIDs = DBIDUtil.newHashSet(relation.getDBIDs());
initHeap(heap, relation, dim, noiseIDs);
- if(logger.isDebugging()) {
- StringBuffer msg = new StringBuffer();
+ if (LOG.isDebugging()) {
+ StringBuilder msg = new StringBuilder();
msg.append("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
msg.append("\nXXXX dim ").append(dim);
msg.append("\nXXXX database.size ").append(relation.size());
msg.append("\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
- logger.debugFine(msg.toString());
- }
- else if(logger.isVerbose()) {
- StringBuffer msg = new StringBuffer();
+ LOG.debugFine(msg.toString());
+ } else if (LOG.isVerbose()) {
+ StringBuilder msg = new StringBuilder();
msg.append("XXXX dim ").append(dim).append(" database.size ").append(relation.size());
- logger.verbose(msg.toString());
+ LOG.verbose(msg.toString());
}
// get the ''best'' d-dimensional intervals at max level
- while(!heap.isEmpty()) {
+ while (!heap.isEmpty()) {
CASHInterval interval = determineNextIntervalAtMaxLevel(heap);
- if(logger.isDebugging()) {
- logger.debugFine("next interval in dim " + dim + ": " + interval);
- }
- else if(logger.isVerbose()) {
- logger.verbose("next interval in dim " + dim + ": " + interval);
+ if (LOG.isDebugging()) {
+ LOG.debugFine("next interval in dim " + dim + ": " + interval);
+ } else if (LOG.isVerbose()) {
+ LOG.verbose("next interval in dim " + dim + ": " + interval);
}
// only noise left
- if(interval == null) {
+ if (interval == null) {
break;
}
// do a dim-1 dimensional run
ModifiableDBIDs clusterIDs = DBIDUtil.newHashSet();
- if(dim > minDim + 1) {
+ if (dim > minDim + 1) {
ModifiableDBIDs ids;
Matrix basis_dim_minus_1;
- if(adjust) {
+ if (adjust) {
ids = DBIDUtil.newHashSet();
basis_dim_minus_1 = runDerivator(relation, dim, interval, ids);
- }
- else {
+ } else {
ids = interval.getIDs();
basis_dim_minus_1 = determineBasis(SpatialUtil.centroid(interval));
}
- if(ids.size() != 0) {
+ if (ids.size() != 0) {
MaterializedRelation<ParameterizationFunction> db = buildDB(dim, basis_dim_minus_1, ids, relation);
// add result of dim-1 to this result
Clustering<Model> res_dim_minus_1 = doRun(db, progress);
- for(Cluster<Model> cluster : res_dim_minus_1.getAllClusters()) {
+ for (Cluster<Model> cluster : res_dim_minus_1.getAllClusters()) {
res.addCluster(cluster);
noiseIDs.removeDBIDs(cluster.getIDs());
clusterIDs.addDBIDs(cluster.getIDs());
@@ -358,28 +356,31 @@ public class CASH extends AbstractAlgorithm<Clustering<Model>> implements Cluste
}
// Rebuild heap
- List<IntegerPriorityObject<CASHInterval>> heapVector = heap.toSortedArrayList();
- for(IntegerPriorityObject<CASHInterval> pair : heapVector) {
+ ArrayList<IntegerPriorityObject<CASHInterval>> heapVector = new ArrayList<IntegerPriorityObject<CASHInterval>>(heap.size());
+ for (IntegerPriorityObject<CASHInterval> obj : heap) {
+ heapVector.add(obj);
+ }
+ heap.clear();
+ for (IntegerPriorityObject<CASHInterval> pair : heapVector) {
CASHInterval currentInterval = pair.getObject();
currentInterval.removeIDs(clusterIDs);
- if(currentInterval.getIDs().size() >= minPts) {
+ if (currentInterval.getIDs().size() >= minPts) {
heap.add(new IntegerPriorityObject<CASHInterval>(currentInterval.priority(), currentInterval));
}
}
- if(progress != null) {
- progress.setProcessed(processedIDs.size(), logger);
+ if (progress != null) {
+ progress.setProcessed(processedIDs.size(), LOG);
}
}
// put noise to clusters
- if(!noiseIDs.isEmpty()) {
- if(dim == noiseDim) {
+ if (!noiseIDs.isEmpty()) {
+ if (dim == noiseDim) {
Cluster<Model> c = new Cluster<Model>(noiseIDs, true, ClusterModel.CLUSTER);
res.addCluster(c);
processedIDs.addDBIDs(noiseIDs);
- }
- else if(noiseIDs.size() >= minPts) {
+ } else if (noiseIDs.size() >= minPts) {
LinearEquationSystem les = runDerivator(fulldatabase, dim - 1, noiseIDs);
Cluster<Model> c = new Cluster<Model>(noiseIDs, true, new LinearEquationModel(les));
res.addCluster(c);
@@ -387,29 +388,38 @@ public class CASH extends AbstractAlgorithm<Clustering<Model>> implements Cluste
}
}
- if(logger.isDebugging()) {
- StringBuffer msg = new StringBuffer();
+ if (LOG.isDebugging()) {
+ StringBuilder msg = new StringBuilder();
msg.append("noise fuer dim ").append(dim).append(": ").append(noiseIDs.size());
- for(Cluster<Model> c : res.getAllClusters()) {
- if(c.getModel() instanceof LinearEquationModel) {
+ for (Cluster<Model> c : res.getAllClusters()) {
+ if (c.getModel() instanceof LinearEquationModel) {
LinearEquationModel s = (LinearEquationModel) c.getModel();
msg.append("\n Cluster: Dim: " + s.getLes().subspacedim() + " size: " + c.size());
- }
- else {
+ } else {
msg.append("\n Cluster: " + c.getModel().getClass().getName() + " size: " + c.size());
}
}
- logger.debugFine(msg.toString());
+ LOG.debugFine(msg.toString());
}
- if(progress != null) {
- progress.setProcessed(processedIDs.size(), logger);
+ if (progress != null) {
+ progress.setProcessed(processedIDs.size(), LOG);
}
return res;
}
/**
+ * Get the dimensionality of a vector field.
+ *
+ * @param relation Relation
+ * @return Dimensionality
+ */
+ private static int dimensionality(Relation<ParameterizationFunction> relation) {
+ return relation.get(relation.iterDBIDs()).getDimensionality();
+ }
+
+ /**
* Initializes the heap with the root intervals.
*
* @param heap the heap to be initialized
@@ -431,21 +441,20 @@ public class CASH extends AbstractAlgorithm<Clustering<Model>> implements Cluste
double[] d_mins = new double[numDIntervals];
double[] d_maxs = new double[numDIntervals];
- if(logger.isDebugging()) {
- StringBuffer msg = new StringBuffer();
+ if (LOG.isDebugging()) {
+ StringBuilder msg = new StringBuilder();
msg.append("d_min ").append(d_min);
msg.append("\nd_max ").append(d_max);
msg.append("\nnumDIntervals ").append(numDIntervals);
msg.append("\ndIntervalSize ").append(dIntervalSize);
- logger.debugFine(msg.toString());
- }
- else if(logger.isVerbose()) {
- StringBuffer msg = new StringBuffer();
+ LOG.debugFine(msg.toString());
+ } else if (LOG.isVerbose()) {
+ StringBuilder msg = new StringBuilder();
msg.append("d_min ").append(d_min);
msg.append("\nd_max ").append(d_max);
msg.append("\nnumDIntervals ").append(numDIntervals);
msg.append("\ndIntervalSize ").append(dIntervalSize);
- logger.verbose(msg.toString());
+ LOG.verbose(msg.toString());
}
// alpha intervals
@@ -453,33 +462,31 @@ public class CASH extends AbstractAlgorithm<Clustering<Model>> implements Cluste
double[] alphaMax = new double[dim - 1];
Arrays.fill(alphaMax, Math.PI);
- for(int i = 0; i < numDIntervals; i++) {
- if(i == 0) {
+ for (int i = 0; i < numDIntervals; i++) {
+ if (i == 0) {
d_mins[i] = d_min;
- }
- else {
+ } else {
d_mins[i] = d_maxs[i - 1];
}
- if(i < numDIntervals - 1) {
+ if (i < numDIntervals - 1) {
d_maxs[i] = d_mins[i] + dIntervalSize;
- }
- else {
+ } else {
d_maxs[i] = d_max - d_mins[i];
}
HyperBoundingBox alphaInterval = new HyperBoundingBox(alphaMin, alphaMax);
ModifiableDBIDs intervalIDs = split.determineIDs(ids, alphaInterval, d_mins[i], d_maxs[i]);
- if(intervalIDs != null && intervalIDs.size() >= minPts) {
- CASHInterval rootInterval = new CASHInterval(alphaMin, alphaMax, split, intervalIDs, 0, 0, d_mins[i], d_maxs[i]);
+ if (intervalIDs != null && intervalIDs.size() >= minPts) {
+ CASHInterval rootInterval = new CASHInterval(alphaMin, alphaMax, split, intervalIDs, -1, 0, d_mins[i], d_maxs[i]);
heap.add(new IntegerPriorityObject<CASHInterval>(rootInterval.priority(), rootInterval));
}
}
- if(logger.isDebuggingFiner()) {
- StringBuffer msg = new StringBuffer();
+ if (LOG.isDebuggingFiner()) {
+ StringBuilder msg = new StringBuilder();
msg.append("heap.size ").append(heap.size());
- logger.debugFiner(msg.toString());
+ LOG.debugFiner(msg.toString());
}
}
@@ -493,23 +500,21 @@ public class CASH extends AbstractAlgorithm<Clustering<Model>> implements Cluste
* @param relation the database storing the parameterization functions
* @return a dim-1 dimensional database where the objects are projected into
* the specified subspace
- * @throws UnableToComplyException if an error according to the database
- * occurs
*/
- private MaterializedRelation<ParameterizationFunction> buildDB(int dim, Matrix basis, DBIDs ids, Relation<ParameterizationFunction> relation) throws UnableToComplyException {
+ private MaterializedRelation<ParameterizationFunction> buildDB(int dim, Matrix basis, DBIDs ids, Relation<ParameterizationFunction> relation) {
ProxyDatabase proxy = new ProxyDatabase(ids);
- VectorFieldTypeInformation<ParameterizationFunction> type = VectorFieldTypeInformation.get(ParameterizationFunction.class, basis.getColumnDimensionality());
+ SimpleTypeInformation<ParameterizationFunction> type = new SimpleTypeInformation<ParameterizationFunction>(ParameterizationFunction.class);
MaterializedRelation<ParameterizationFunction> prep = new MaterializedRelation<ParameterizationFunction>(proxy, type, ids);
proxy.addRelation(prep);
-
+
// Project
- for(DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
+ for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
ParameterizationFunction f = project(basis, relation.get(iter));
prep.set(iter, f);
}
- if(logger.isDebugging()) {
- logger.debugFine("db fuer dim " + (dim - 1) + ": " + ids.size());
+ if (LOG.isDebugging()) {
+ LOG.debugFine("db fuer dim " + (dim - 1) + ": " + ids.size());
}
return prep;
@@ -527,7 +532,7 @@ public class CASH extends AbstractAlgorithm<Clustering<Model>> implements Cluste
// Matrix m = new Matrix(new
// double[][]{f.getPointCoordinates()}).times(basis);
Matrix m = f.getColumnVector().transposeTimes(basis);
- ParameterizationFunction f_t = new ParameterizationFunction(m.getColumnPackedCopy());
+ ParameterizationFunction f_t = new ParameterizationFunction(new DoubleVector(m.getColumnPackedCopy()));
return f_t;
}
@@ -540,7 +545,7 @@ public class CASH extends AbstractAlgorithm<Clustering<Model>> implements Cluste
*/
private Matrix determineBasis(double[] alpha) {
double[] nn = new double[alpha.length + 1];
- for(int i = 0; i < nn.length; i++) {
+ for (int i = 0; i < nn.length; i++) {
double alpha_i = i == alpha.length ? 0 : alpha[i];
nn[i] = sinusProduct(0, i, alpha) * StrictMath.cos(alpha_i);
}
@@ -560,7 +565,7 @@ public class CASH extends AbstractAlgorithm<Clustering<Model>> implements Cluste
*/
private double sinusProduct(int start, int end, double[] alpha) {
double result = 1;
- for(int j = start; j < end; j++) {
+ for (int j = start; j < end; j++) {
result *= StrictMath.sin(alpha[j]);
}
return result;
@@ -576,8 +581,8 @@ public class CASH extends AbstractAlgorithm<Clustering<Model>> implements Cluste
private CASHInterval determineNextIntervalAtMaxLevel(Heap<IntegerPriorityObject<CASHInterval>> heap) {
CASHInterval next = doDetermineNextIntervalAtMaxLevel(heap);
// noise path was chosen
- while(next == null) {
- if(heap.isEmpty()) {
+ while (next == null) {
+ if (heap.isEmpty()) {
return null;
}
next = doDetermineNextIntervalAtMaxLevel(heap);
@@ -596,48 +601,45 @@ public class CASH extends AbstractAlgorithm<Clustering<Model>> implements Cluste
private CASHInterval doDetermineNextIntervalAtMaxLevel(Heap<IntegerPriorityObject<CASHInterval>> heap) {
CASHInterval interval = heap.poll().getObject();
int dim = interval.getDimensionality();
- while(true) {
+ while (true) {
// max level is reached
- if(interval.getLevel() >= maxLevel && interval.getMaxSplitDimension() == dim) {
+ if (interval.getLevel() >= maxLevel && interval.getMaxSplitDimension() == (dim - 1)) {
return interval;
}
- if(heap.size() % 10000 == 0 && logger.isVerbose()) {
- logger.verbose("heap size " + heap.size());
+ if (heap.size() % 10000 == 0 && LOG.isVerbose()) {
+ LOG.verbose("heap size " + heap.size());
}
- if(heap.size() >= 40000) {
- logger.warning("Heap size > 40.000!!!");
+ if (heap.size() >= 40000) {
+ LOG.warning("Heap size > 40.000!!!");
heap.clear();
return null;
}
- if(logger.isDebuggingFiner()) {
- logger.debugFiner("split " + interval.toString() + " " + interval.getLevel() + "-" + interval.getMaxSplitDimension());
+ if (LOG.isDebuggingFiner()) {
+ LOG.debugFiner("split " + interval.toString() + " " + interval.getLevel() + "-" + interval.getMaxSplitDimension());
}
interval.split();
// noise
- if(!interval.hasChildren()) {
+ if (!interval.hasChildren()) {
return null;
}
CASHInterval bestInterval;
- if(interval.getLeftChild() != null && interval.getRightChild() != null) {
+ if (interval.getLeftChild() != null && interval.getRightChild() != null) {
int comp = interval.getLeftChild().compareTo(interval.getRightChild());
- if(comp < 0) {
+ if (comp < 0) {
bestInterval = interval.getRightChild();
heap.add(new IntegerPriorityObject<CASHInterval>(interval.getLeftChild().priority(), interval.getLeftChild()));
- }
- else {
+ } else {
bestInterval = interval.getLeftChild();
heap.add(new IntegerPriorityObject<CASHInterval>(interval.getRightChild().priority(), interval.getRightChild()));
}
- }
- else if(interval.getLeftChild() == null) {
+ } else if (interval.getLeftChild() == null) {
bestInterval = interval.getRightChild();
- }
- else {
+ } else {
bestInterval = interval.getLeftChild();
}
@@ -662,7 +664,7 @@ public class CASH extends AbstractAlgorithm<Clustering<Model>> implements Cluste
double d_min = Double.POSITIVE_INFINITY;
double d_max = Double.NEGATIVE_INFINITY;
- for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
+ for (DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
ParameterizationFunction f = relation.get(iditer);
HyperBoundingBox minMax = f.determineAlphaMinMax(box);
double f_min = f.function(SpatialUtil.getMin(minMax));
@@ -684,11 +686,8 @@ public class CASH extends AbstractAlgorithm<Clustering<Model>> implements Cluste
* @param dim the dimensionality of the database
* @param ids an empty set to assign the ids
* @return a basis of the found subspace
- * @throws UnableToComplyException if an error according to the database
- * occurs
- * @throws ParameterException if the parameter setting is wrong
*/
- private Matrix runDerivator(Relation<ParameterizationFunction> relation, int dim, CASHInterval interval, ModifiableDBIDs ids) throws UnableToComplyException, ParameterException {
+ private Matrix runDerivator(Relation<ParameterizationFunction> relation, int dim, CASHInterval interval, ModifiableDBIDs ids) {
// build database for derivator
Database derivatorDB = buildDerivatorDB(relation, interval);
@@ -705,14 +704,14 @@ public class CASH extends AbstractAlgorithm<Clustering<Model>> implements Cluste
Matrix weightMatrix = model.getSimilarityMatrix();
DoubleVector centroid = new DoubleVector(model.getCentroid());
DistanceQuery<DoubleVector, DoubleDistance> df = QueryUtil.getDistanceQuery(derivatorDB, new WeightedDistanceFunction(weightMatrix));
- DoubleDistance eps = df.getDistanceFactory().parseString("0.25");
+ DoubleDistance eps = new DoubleDistance(0.25);
ids.addDBIDs(interval.getIDs());
// Search for nearby vectors in original database
- for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
+ for (DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
DoubleVector v = new DoubleVector(relation.get(iditer).getColumnVector().getArrayRef());
DoubleDistance d = df.distance(v, centroid);
- if(d.compareTo(eps) < 0) {
+ if (d.compareTo(eps) < 0) {
ids.add(iditer);
}
}
@@ -729,25 +728,23 @@ public class CASH extends AbstractAlgorithm<Clustering<Model>> implements Cluste
* @param interval the interval to build the database from
* @return a database for the derivator consisting of the ids in the specified
* interval
- * @throws UnableToComplyException if an error according to the database
- * occurs
*/
- private Database buildDerivatorDB(Relation<ParameterizationFunction> relation, CASHInterval interval) throws UnableToComplyException {
+ private Database buildDerivatorDB(Relation<ParameterizationFunction> relation, CASHInterval interval) {
DBIDs ids = interval.getIDs();
ProxyDatabase proxy = new ProxyDatabase(ids);
- int dim = DatabaseUtil.dimensionality(relation);
- SimpleTypeInformation<DoubleVector> type = new VectorFieldTypeInformation<DoubleVector>(DoubleVector.class, dim, new DoubleVector(new double[dim]));
+ int dim = dimensionality(relation);
+ SimpleTypeInformation<DoubleVector> type = new VectorFieldTypeInformation<DoubleVector>(DoubleVector.FACTORY, dim);
MaterializedRelation<DoubleVector> prep = new MaterializedRelation<DoubleVector>(proxy, type, ids);
proxy.addRelation(prep);
// Project
- for(DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
+ for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
DoubleVector v = new DoubleVector(relation.get(iter).getColumnVector().getArrayRef());
prep.set(iter, v);
}
- if(logger.isDebugging()) {
- logger.debugFine("db fuer derivator : " + prep.size());
+ if (LOG.isDebugging()) {
+ LOG.debugFine("db fuer derivator : " + prep.size());
}
return proxy;
@@ -778,11 +775,7 @@ public class CASH extends AbstractAlgorithm<Clustering<Model>> implements Cluste
CorrelationAnalysisSolution<DoubleVector> model = derivator.run(derivatorDB);
LinearEquationSystem les = model.getNormalizedLinearEquationSystem(null);
return les;
- }
- catch(UnableToComplyException e) {
- throw new IllegalStateException("Initialization of the database for the derivator failed: " + e);
- }
- catch(NonNumericFeaturesException e) {
+ } catch (NonNumericFeaturesException e) {
throw new IllegalStateException("Error during normalization" + e);
}
}
@@ -795,18 +788,16 @@ public class CASH extends AbstractAlgorithm<Clustering<Model>> implements Cluste
* @param ids the ids to build the database from
* @return a database for the derivator consisting of the ids in the specified
* interval
- * @throws UnableToComplyException if initialization of the database is not
- * possible
*/
- private Database buildDerivatorDB(Relation<ParameterizationFunction> relation, DBIDs ids) throws UnableToComplyException {
+ private Database buildDerivatorDB(Relation<ParameterizationFunction> relation, DBIDs ids) {
ProxyDatabase proxy = new ProxyDatabase(ids);
- int dim = DatabaseUtil.dimensionality(relation);
- SimpleTypeInformation<DoubleVector> type = new VectorFieldTypeInformation<DoubleVector>(DoubleVector.class, dim, new DoubleVector(new double[dim]));
+ int dim = dimensionality(relation);
+ SimpleTypeInformation<DoubleVector> type = new VectorFieldTypeInformation<DoubleVector>(DoubleVector.FACTORY, dim);
MaterializedRelation<DoubleVector> prep = new MaterializedRelation<DoubleVector>(proxy, type, ids);
proxy.addRelation(prep);
// Project
- for(DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
+ for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
DoubleVector v = new DoubleVector(relation.get(iter).getColumnVector().getArrayRef());
prep.set(iter, v);
}
@@ -816,12 +807,12 @@ public class CASH extends AbstractAlgorithm<Clustering<Model>> implements Cluste
@Override
public TypeInformation[] getInputTypeRestriction() {
- return TypeUtil.array(VectorFieldTypeInformation.get(ParameterizationFunction.class));
+ return TypeUtil.array(TypeUtil.NUMBER_VECTOR_FIELD);
}
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -845,31 +836,35 @@ public class CASH extends AbstractAlgorithm<Clustering<Model>> implements Cluste
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- IntParameter minptsP = new IntParameter(MINPTS_ID, new GreaterConstraint(0));
- if(config.grab(minptsP)) {
+ IntParameter minptsP = new IntParameter(MINPTS_ID);
+ minptsP.addConstraint(new GreaterConstraint(0));
+ if (config.grab(minptsP)) {
minpts = minptsP.getValue();
}
- IntParameter maxlevelP = new IntParameter(MAXLEVEL_ID, new GreaterConstraint(0));
- if(config.grab(maxlevelP)) {
+ IntParameter maxlevelP = new IntParameter(MAXLEVEL_ID);
+ maxlevelP.addConstraint(new GreaterConstraint(0));
+ if (config.grab(maxlevelP)) {
maxlevel = maxlevelP.getValue();
}
- IntParameter mindimP = new IntParameter(MINDIM_ID, new GreaterConstraint(0), 1);
- if(config.grab(mindimP)) {
+ IntParameter mindimP = new IntParameter(MINDIM_ID, 1);
+ mindimP.addConstraint(new GreaterConstraint(0));
+ if (config.grab(mindimP)) {
mindim = mindimP.getValue();
}
- DoubleParameter jitterP = new DoubleParameter(JITTER_ID, new GreaterConstraint(0));
- if(config.grab(jitterP)) {
+ DoubleParameter jitterP = new DoubleParameter(JITTER_ID);
+ jitterP.addConstraint(new GreaterConstraint(0));
+ if (config.grab(jitterP)) {
jitter = jitterP.getValue();
}
Flag adjustF = new Flag(ADJUST_ID);
- if(config.grab(adjustF)) {
+ if (config.grab(adjustF)) {
adjust = adjustF.getValue();
}
}
@Override
- protected CASH makeInstance() {
- return new CASH(minpts, maxlevel, mindim, jitter, adjust);
+ protected CASH<NumberVector<?>> makeInstance() {
+ return new CASH<NumberVector<?>>(minpts, maxlevel, mindim, jitter, adjust);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/COPAC.java b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/COPAC.java
index 1d41d37e..ac50559e 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/COPAC.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/COPAC.java
@@ -40,13 +40,13 @@ import de.lmu.ifi.dbs.elki.data.model.Model;
import de.lmu.ifi.dbs.elki.data.type.TypeInformation;
import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
import de.lmu.ifi.dbs.elki.database.ProxyDatabase;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.ids.ModifiableDBIDs;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.distance.distancefunction.FilteredLocalPCABasedDistanceFunction;
import de.lmu.ifi.dbs.elki.distance.distancefunction.IndexBasedDistanceFunction;
import de.lmu.ifi.dbs.elki.distance.distancefunction.LocallyWeightedDistanceFunction;
@@ -56,7 +56,6 @@ import de.lmu.ifi.dbs.elki.index.preprocessed.LocalProjectionIndex;
import de.lmu.ifi.dbs.elki.index.preprocessed.LocalProjectionIndex.Factory;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.logging.progress.FiniteProgress;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.utilities.documentation.Description;
import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
import de.lmu.ifi.dbs.elki.utilities.documentation.Title;
@@ -92,11 +91,11 @@ import de.lmu.ifi.dbs.elki.utilities.pairs.Pair;
@Title("COPAC: COrrelation PArtition Clustering")
@Description("Partitions a database according to the correlation dimension of its objects and performs " + "a clustering algorithm over the partitions.")
@Reference(authors = "E. Achtert, C. Böhm, H.-P. Kriegel, P. Kröger P., A. Zimek", title = "Robust, Complete, and Efficient Correlation Clustering", booktitle = "Proc. 7th SIAM International Conference on Data Mining (SDM'07), Minneapolis, MN, 2007", url = "http://www.siam.org/proceedings/datamining/2007/dm07_037achtert.pdf")
-public class COPAC<V extends NumberVector<V, ?>, D extends Distance<D>> extends AbstractAlgorithm<Clustering<Model>> implements ClusteringAlgorithm<Clustering<Model>> {
+public class COPAC<V extends NumberVector<?>, D extends Distance<D>> extends AbstractAlgorithm<Clustering<Model>> implements ClusteringAlgorithm<Clustering<Model>> {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(COPAC.class);
+ private static final Logging LOG = Logging.getLogger(COPAC.class);
/**
* Parameter to specify the local PCA preprocessor to derive partition
@@ -106,7 +105,7 @@ public class COPAC<V extends NumberVector<V, ?>, D extends Distance<D>> extends
* Key: {@code -copac.preprocessor}
* </p>
*/
- public static final OptionID PREPROCESSOR_ID = OptionID.getOrCreateOptionID("copac.preprocessor", "Local PCA Preprocessor to derive partition criterion.");
+ public static final OptionID PREPROCESSOR_ID = new OptionID("copac.preprocessor", "Local PCA Preprocessor to derive partition criterion.");
/**
* Parameter to specify the distance function to use inside the partitions
@@ -120,7 +119,7 @@ public class COPAC<V extends NumberVector<V, ?>, D extends Distance<D>> extends
* Key: {@code -copac.partitionDistance}
* </p>
*/
- public static final OptionID PARTITION_DISTANCE_ID = OptionID.getOrCreateOptionID("copac.partitionDistance", "Distance to use for the inner algorithms.");
+ public static final OptionID PARTITION_DISTANCE_ID = new OptionID("copac.partitionDistance", "Distance to use for the inner algorithms.");
/**
* Parameter to specify the clustering algorithm to apply to each partition,
@@ -130,7 +129,7 @@ public class COPAC<V extends NumberVector<V, ?>, D extends Distance<D>> extends
* Key: {@code -copac.partitionAlgorithm}
* </p>
*/
- public static final OptionID PARTITION_ALGORITHM_ID = OptionID.getOrCreateOptionID("copac.partitionAlgorithm", "Clustering algorithm to apply to each partition.");
+ public static final OptionID PARTITION_ALGORITHM_ID = new OptionID("copac.partitionAlgorithm", "Clustering algorithm to apply to each partition.");
/**
* Holds the instance of the preprocessed distance function
@@ -178,8 +177,8 @@ public class COPAC<V extends NumberVector<V, ?>, D extends Distance<D>> extends
*/
@SuppressWarnings("unchecked")
public Clustering<Model> run(Relation<V> relation) {
- if(logger.isVerbose()) {
- logger.verbose("Running COPAC on db size = " + relation.size() + " with dimensionality = " + DatabaseUtil.dimensionality(relation));
+ if(LOG.isVerbose()) {
+ LOG.verbose("Running COPAC on db size = " + relation.size() + " with dimensionality = " + RelationUtil.dimensionality(relation));
}
partitionDistanceQuery = (FilteredLocalPCABasedDistanceFunction.Instance<V, LocalProjectionIndex<V, ?>, D>) partitionDistanceFunction.instantiate(relation);
@@ -187,7 +186,7 @@ public class COPAC<V extends NumberVector<V, ?>, D extends Distance<D>> extends
// partitioning
Map<Integer, ModifiableDBIDs> partitionMap = new HashMap<Integer, ModifiableDBIDs>();
- FiniteProgress partitionProgress = logger.isVerbose() ? new FiniteProgress("Partitioning", relation.size(), logger) : null;
+ FiniteProgress partitionProgress = LOG.isVerbose() ? new FiniteProgress("Partitioning", relation.size(), LOG) : null;
int processed = 1;
for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
@@ -199,17 +198,17 @@ public class COPAC<V extends NumberVector<V, ?>, D extends Distance<D>> extends
partitionMap.get(corrdim).add(iditer);
if(partitionProgress != null) {
- partitionProgress.setProcessed(processed++, logger);
+ partitionProgress.setProcessed(processed++, LOG);
}
}
if(partitionProgress != null) {
- partitionProgress.ensureCompleted(logger);
+ partitionProgress.ensureCompleted(LOG);
}
- if(logger.isVerbose()) {
+ if(LOG.isVerbose()) {
for(Integer corrDim : partitionMap.keySet()) {
ModifiableDBIDs list = partitionMap.get(corrDim);
- logger.verbose("Partition [corrDim = " + corrDim + "]: " + list.size() + " objects.");
+ LOG.verbose("Partition [corrDim = " + corrDim + "]: " + list.size() + " objects.");
}
}
@@ -236,7 +235,7 @@ public class COPAC<V extends NumberVector<V, ?>, D extends Distance<D>> extends
// TODO: use an extra finite progress for the partitions?
for(Entry<Integer, DBIDs> pair : partitionMap.entrySet()) {
// noise partition
- if(pair.getKey() == DatabaseUtil.dimensionality(relation)) {
+ if(pair.getKey() == RelationUtil.dimensionality(relation)) {
// Make a Noise cluster
result.addCluster(new Cluster<Model>(pair.getValue(), true, ClusterModel.CLUSTER));
}
@@ -245,8 +244,8 @@ public class COPAC<V extends NumberVector<V, ?>, D extends Distance<D>> extends
ProxyDatabase proxy = new ProxyDatabase(partids, relation);
ClusteringAlgorithm<Clustering<Model>> partitionAlgorithm = getPartitionAlgorithm(query);
- if(logger.isVerbose()) {
- logger.verbose("Running " + partitionAlgorithm.getClass().getName() + " on partition [corrDim = " + pair.getKey() + "]...");
+ if(LOG.isVerbose()) {
+ LOG.verbose("Running " + partitionAlgorithm.getClass().getName() + " on partition [corrDim = " + pair.getKey() + "]...");
}
Clustering<Model> p = partitionAlgorithm.run(proxy);
// Re-Wrap resulting Clusters as DimensionModel clusters.
@@ -295,7 +294,7 @@ public class COPAC<V extends NumberVector<V, ?>, D extends Distance<D>> extends
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -305,7 +304,7 @@ public class COPAC<V extends NumberVector<V, ?>, D extends Distance<D>> extends
*
* @apiviz.exclude
*/
- public static class Parameterizer<V extends NumberVector<V, ?>, D extends Distance<D>> extends AbstractParameterizer {
+ public static class Parameterizer<V extends NumberVector<?>, D extends Distance<D>> extends AbstractParameterizer {
protected LocalProjectionIndex.Factory<V, ?> indexI = null;
protected FilteredLocalPCABasedDistanceFunction<V, ?, D> pdistI = null;
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/ERiC.java b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/ERiC.java
index b57a6e29..7e7314b4 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/ERiC.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/ERiC.java
@@ -44,6 +44,7 @@ import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.ids.ModifiableDBIDs;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
import de.lmu.ifi.dbs.elki.distance.distancefunction.ProxyDistanceFunction;
import de.lmu.ifi.dbs.elki.distance.distancefunction.correlation.ERiCDistanceFunction;
@@ -52,11 +53,11 @@ import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
import de.lmu.ifi.dbs.elki.distance.distancevalue.IntegerDistance;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.logging.progress.StepProgress;
+import de.lmu.ifi.dbs.elki.math.linearalgebra.Centroid;
import de.lmu.ifi.dbs.elki.math.linearalgebra.pca.FirstNEigenPairFilter;
import de.lmu.ifi.dbs.elki.math.linearalgebra.pca.PCAFilteredResult;
import de.lmu.ifi.dbs.elki.math.linearalgebra.pca.PCAFilteredRunner;
import de.lmu.ifi.dbs.elki.utilities.ClassGenericsUtil;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.utilities.documentation.Description;
import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
import de.lmu.ifi.dbs.elki.utilities.documentation.Title;
@@ -78,11 +79,11 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameteriz
*
* @author Elke Achtert
*
- * @apiviz.uses COPAC
- * @apiviz.uses DBSCAN
- * @apiviz.uses ERiCDistanceFunction
- * @apiviz.uses FirstNEigenPairFilter
- * @apiviz.uses PCAFilteredRunner
+ * @apiviz.composedOf COPAC
+ * @apiviz.composedOf DBSCAN
+ * @apiviz.composedOf ERiCDistanceFunction
+ * @apiviz.composedOf FirstNEigenPairFilter
+ * @apiviz.composedOf PCAFilteredRunner
* @apiviz.has CorrelationModel
*
* @param <V> the type of NumberVector handled by this Algorithm
@@ -91,11 +92,11 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameteriz
@Title("ERiC: Exploring Relationships among Correlation Clusters")
@Description("Performs the DBSCAN algorithm on the data using a special distance function taking into account correlations among attributes and builds " + "a hierarchy that allows multiple inheritance from the correlation clustering result.")
@Reference(authors = "E. Achtert, C. Böhm, H.-P. Kriegel, P. Kröger, and A. Zimek", title = "On Exploring Complex Relationships of Correlation Clusters", booktitle = "Proc. 19th International Conference on Scientific and Statistical Database Management (SSDBM 2007), Banff, Canada, 2007", url = "http://dx.doi.org/10.1109/SSDBM.2007.21")
-public class ERiC<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Clustering<CorrelationModel<V>>> implements ClusteringAlgorithm<Clustering<CorrelationModel<V>>> {
+public class ERiC<V extends NumberVector<?>> extends AbstractAlgorithm<Clustering<CorrelationModel<V>>> implements ClusteringAlgorithm<Clustering<CorrelationModel<V>>> {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(ERiC.class);
+ private static final Logging LOG = Logging.getLogger(ERiC.class);
/**
* The COPAC clustering algorithm.
@@ -119,13 +120,13 @@ public class ERiC<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Cluste
* @return Clustering result
*/
public Clustering<CorrelationModel<V>> run(Relation<V> relation) {
- final int dimensionality = DatabaseUtil.dimensionality(relation);
+ final int dimensionality = RelationUtil.dimensionality(relation);
- StepProgress stepprog = logger.isVerbose() ? new StepProgress(3) : null;
+ StepProgress stepprog = LOG.isVerbose() ? new StepProgress(3) : null;
// run COPAC
if(stepprog != null) {
- stepprog.beginStep(1, "Preprocessing local correlation dimensionalities and partitioning data", logger);
+ stepprog.beginStep(1, "Preprocessing local correlation dimensionalities and partitioning data", LOG);
}
Clustering<Model> copacResult = copacAlgorithm.run(relation);
@@ -133,11 +134,11 @@ public class ERiC<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Cluste
// extract correlation clusters
if(stepprog != null) {
- stepprog.beginStep(2, "Extract correlation clusters", logger);
+ stepprog.beginStep(2, "Extract correlation clusters", LOG);
}
SortedMap<Integer, List<Cluster<CorrelationModel<V>>>> clusterMap = extractCorrelationClusters(copacResult, relation, dimensionality);
- if(logger.isDebugging()) {
- StringBuffer msg = new StringBuffer("Step 2: Extract correlation clusters...");
+ if(LOG.isDebugging()) {
+ StringBuilder msg = new StringBuilder("Step 2: Extract correlation clusters...");
for(Integer corrDim : clusterMap.keySet()) {
List<Cluster<CorrelationModel<V>>> correlationClusters = clusterMap.get(corrDim);
msg.append("\n\ncorrDim ").append(corrDim);
@@ -149,23 +150,23 @@ public class ERiC<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Cluste
// " ids " + cluster.getIDs().size());
}
}
- logger.debugFine(msg.toString());
+ LOG.debugFine(msg.toString());
}
- if(logger.isVerbose()) {
+ if(LOG.isVerbose()) {
int clusters = 0;
for(List<Cluster<CorrelationModel<V>>> correlationClusters : clusterMap.values()) {
clusters += correlationClusters.size();
}
- logger.verbose(clusters + " clusters extracted.");
+ LOG.verbose(clusters + " clusters extracted.");
}
// build hierarchy
if(stepprog != null) {
- stepprog.beginStep(3, "Building hierarchy", logger);
+ stepprog.beginStep(3, "Building hierarchy", LOG);
}
buildHierarchy(clusterMap, query);
- if(logger.isDebugging()) {
- StringBuffer msg = new StringBuffer("Step 3: Build hierarchy");
+ if(LOG.isDebugging()) {
+ StringBuilder msg = new StringBuilder("Step 3: Build hierarchy");
for(Integer corrDim : clusterMap.keySet()) {
List<Cluster<CorrelationModel<V>>> correlationClusters = clusterMap.get(corrDim);
for(Cluster<CorrelationModel<V>> cluster : correlationClusters) {
@@ -179,10 +180,10 @@ public class ERiC<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Cluste
}
}
}
- logger.debugFine(msg.toString());
+ LOG.debugFine(msg.toString());
}
if(stepprog != null) {
- stepprog.setCompleted(logger);
+ stepprog.setCompleted(LOG);
}
Clustering<CorrelationModel<V>> result = new Clustering<CorrelationModel<V>>("ERiC clustering", "eric-clustering");
@@ -221,7 +222,7 @@ public class ERiC<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Cluste
Class<PCAFilteredRunner<V>> cls = ClassGenericsUtil.uglyCastIntoSubclass(PCAFilteredRunner.class);
PCAFilteredRunner<V> pca = parameters.tryInstantiate(cls);
for(ParameterException e : parameters.getErrors()) {
- logger.warning("Error in internal parameterization: " + e.getMessage());
+ LOG.warning("Error in internal parameterization: " + e.getMessage());
}
// get cluster list for this dimension.
@@ -233,7 +234,7 @@ public class ERiC<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Cluste
PCAFilteredResult pcares = pca.processIds(group, database);
- V centroid = DatabaseUtil.centroid(database, group);
+ V centroid = Centroid.make(database, group).toVector(database);
Cluster<CorrelationModel<V>> correlationCluster = new Cluster<CorrelationModel<V>>("[" + correlationDimension + "_" + correlationClusters.size() + "]", group, new CorrelationModel<V>(pcares, centroid), new ArrayList<Cluster<CorrelationModel<V>>>(), new ArrayList<Cluster<CorrelationModel<V>>>());
correlationClusters.add(correlationCluster);
}
@@ -264,11 +265,11 @@ public class ERiC<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Cluste
Class<PCAFilteredRunner<V>> cls = ClassGenericsUtil.uglyCastIntoSubclass(PCAFilteredRunner.class);
PCAFilteredRunner<V> pca = parameters.tryInstantiate(cls);
for(ParameterException e : parameters.getErrors()) {
- logger.warning("Error in internal parameterization: " + e.getMessage());
+ LOG.warning("Error in internal parameterization: " + e.getMessage());
}
PCAFilteredResult pcares = pca.processIds(noise.getIDs(), database);
- V centroid = DatabaseUtil.centroid(database, noise.getIDs());
+ V centroid = Centroid.make(database, noise.getIDs()).toVector(database);
Cluster<CorrelationModel<V>> correlationCluster = new Cluster<CorrelationModel<V>>("[noise]", noise.getIDs(), new CorrelationModel<V>(pcares, centroid), new ArrayList<Cluster<CorrelationModel<V>>>(), new ArrayList<Cluster<CorrelationModel<V>>>());
correlationClusters.add(correlationCluster);
}
@@ -292,7 +293,7 @@ public class ERiC<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Cluste
}
private void buildHierarchy(SortedMap<Integer, List<Cluster<CorrelationModel<V>>>> clusterMap, DistanceQuery<V, IntegerDistance> query) {
- StringBuffer msg = new StringBuffer();
+ StringBuilder msg = new StringBuilder();
DBSCAN<V, DoubleDistance> dbscan = ClassGenericsUtil.castWithGenericsOrNull(DBSCAN.class, copacAlgorithm.getPartitionAlgorithm(query));
if(dbscan == null) {
@@ -310,7 +311,7 @@ public class ERiC<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Cluste
for(Integer childCorrDim : clusterMap.keySet()) {
List<Cluster<CorrelationModel<V>>> children = clusterMap.get(childCorrDim);
SortedMap<Integer, List<Cluster<CorrelationModel<V>>>> parentMap = clusterMap.tailMap(childCorrDim + 1);
- if(logger.isDebugging()) {
+ if(LOG.isDebugging()) {
msg.append("\ncorrdim ").append(childCorrDim);
msg.append("\nparents ").append(parentMap.keySet());
}
@@ -323,8 +324,8 @@ public class ERiC<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Cluste
if(subspaceDim_parent == lambda_max && child.getParents().isEmpty()) {
parent.getChildren().add(child);
child.getParents().add(parent);
- if(logger.isDebugging()) {
- msg.append("\n").append(parent).append(" is parent of ").append(child);
+ if(LOG.isDebugging()) {
+ msg.append('\n').append(parent).append(" is parent of ").append(child);
}
}
else {
@@ -332,8 +333,8 @@ public class ERiC<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Cluste
if(!dist.bitValue() && (child.getParents().isEmpty() || !isParent(distanceFunction, parent, child.getParents()))) {
parent.getChildren().add(child);
child.getParents().add(parent);
- if(logger.isDebugging()) {
- msg.append("\n").append(parent).append(" is parent of ").append(child);
+ if(LOG.isDebugging()) {
+ msg.append('\n').append(parent).append(" is parent of ").append(child);
}
}
}
@@ -341,8 +342,8 @@ public class ERiC<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Cluste
}
}
}
- if(logger.isDebugging()) {
- logger.debugFine(msg.toString());
+ if(LOG.isDebugging()) {
+ LOG.debugFine(msg.toString());
}
}
@@ -360,7 +361,7 @@ public class ERiC<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Cluste
*/
private boolean isParent(ERiCDistanceFunction distanceFunction, Cluster<CorrelationModel<V>> parent, List<Cluster<CorrelationModel<V>>> children) {
- StringBuffer msg = new StringBuffer();
+ StringBuilder msg = new StringBuilder();
for(Cluster<CorrelationModel<V>> child : children) {
if(parent.getModel().getPCAResult().getCorrelationDimension() == child.getModel().getPCAResult().getCorrelationDimension()) {
@@ -368,19 +369,19 @@ public class ERiC<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Cluste
}
BitDistance dist = distanceFunction.distance(parent.getModel().getCentroid(), child.getModel().getCentroid(), parent.getModel().getPCAResult(), child.getModel().getPCAResult());
- if(logger.isDebugging()) {
+ if(LOG.isDebugging()) {
msg.append("\ndist(").append(child).append(" - ").append(parent).append(") = ").append(dist);
}
if(!dist.bitValue()) {
- if(logger.isDebugging()) {
- logger.debugFine(msg.toString());
+ if(LOG.isDebugging()) {
+ LOG.debugFine(msg.toString());
}
return true;
}
}
- if(logger.isDebugging()) {
- logger.debugFine(msg.toString());
+ if(LOG.isDebugging()) {
+ LOG.debugFine(msg.toString());
}
return false;
}
@@ -392,7 +393,7 @@ public class ERiC<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Cluste
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -402,7 +403,7 @@ public class ERiC<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Cluste
*
* @apiviz.exclude
*/
- public static class Parameterizer<V extends NumberVector<V, ?>> extends AbstractParameterizer {
+ public static class Parameterizer<V extends NumberVector<?>> extends AbstractParameterizer {
/**
* The COPAC instance to use
*/
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/FourC.java b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/FourC.java
index 98761962..f56342e0 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/FourC.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/FourC.java
@@ -57,11 +57,11 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameteriz
@Title("4C: Computing Correlation Connected Clusters")
@Description("4C identifies local subgroups of data objects sharing a uniform correlation. " + "The algorithm is based on a combination of PCA and density-based clustering (DBSCAN).")
@Reference(authors = "C. Böhm, K. Kailing, P. Kröger, A. Zimek", title = "Computing Clusters of Correlation Connected Objects", booktitle = "Proc. ACM SIGMOD Int. Conf. on Management of Data, Paris, France, 2004, 455-466", url = "http://dx.doi.org/10.1145/1007568.1007620")
-public class FourC<V extends NumberVector<V, ?>> extends AbstractProjectedDBSCAN<Clustering<Model>, V> {
+public class FourC<V extends NumberVector<?>> extends AbstractProjectedDBSCAN<Clustering<Model>, V> {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(FourC.class);
+ private static final Logging LOG = Logging.getLogger(FourC.class);
/**
* Constructor.
@@ -92,7 +92,7 @@ public class FourC<V extends NumberVector<V, ?>> extends AbstractProjectedDBSCAN
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -102,7 +102,7 @@ public class FourC<V extends NumberVector<V, ?>> extends AbstractProjectedDBSCAN
*
* @apiviz.exclude
*/
- public static class Parameterizer<O extends NumberVector<O, ?>> extends AbstractProjectedDBSCAN.Parameterizer<O, DoubleDistance> {
+ public static class Parameterizer<O extends NumberVector<?>> extends AbstractProjectedDBSCAN.Parameterizer<O, DoubleDistance> {
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/HiCO.java b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/HiCO.java
index 1065682c..759e8f59 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/HiCO.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/HiCO.java
@@ -1,26 +1,27 @@
package de.lmu.ifi.dbs.elki.algorithm.clustering.correlation;
-/*
-This file is part of ELKI:
-Environment for Developing KDD-Applications Supported by Index-Structures
-
-Copyright (C) 2012
-Ludwig-Maximilians-Universität München
-Lehr- und Forschungseinheit für Datenbanksysteme
-ELKI Development Team
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
import de.lmu.ifi.dbs.elki.algorithm.clustering.OPTICS;
import de.lmu.ifi.dbs.elki.data.NumberVector;
@@ -37,7 +38,7 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterConstraint;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterEqualConstraint;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.IntervalConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.LessConstraint;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.ChainedParameterization;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.ListParameterization;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
@@ -64,11 +65,11 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
@Title("Mining Hierarchies of Correlation Clusters")
@Description("Algorithm for detecting hierarchies of correlation clusters.")
@Reference(authors = "E. Achtert, C. Böhm, P. Kröger, A. Zimek", title = "Mining Hierarchies of Correlation Clusterse", booktitle = "Proc. Int. Conf. on Scientific and Statistical Database Management (SSDBM'06), Vienna, Austria, 2006", url = "http://dx.doi.org/10.1109/SSDBM.2006.35")
-public class HiCO<V extends NumberVector<V, ?>> extends OPTICS<V, PCACorrelationDistance> {
+public class HiCO<V extends NumberVector<?>> extends OPTICS<V, PCACorrelationDistance> {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(HiCO.class);
+ private static final Logging LOG = Logging.getLogger(HiCO.class);
/**
* Parameter to specify the smoothing factor, must be an integer greater than
@@ -79,7 +80,7 @@ public class HiCO<V extends NumberVector<V, ?>> extends OPTICS<V, PCACorrelation
* Key: {@code -hico.mu}
* </p>
*/
- public static final OptionID MU_ID = OptionID.getOrCreateOptionID("hico.mu", "Specifies the smoothing factor. The mu-nearest neighbor is used to compute the correlation reachability of an object.");
+ public static final OptionID MU_ID = new OptionID("hico.mu", "Specifies the smoothing factor. The mu-nearest neighbor is used to compute the correlation reachability of an object.");
/**
* Optional parameter to specify the number of nearest neighbors considered in
@@ -92,7 +93,7 @@ public class HiCO<V extends NumberVector<V, ?>> extends OPTICS<V, PCACorrelation
* Default value: {@link #MU_ID}
* </p>
*/
- public static final OptionID K_ID = OptionID.getOrCreateOptionID("hico.k", "Optional parameter to specify the number of nearest neighbors considered in the PCA. If this parameter is not set, k is set to the value of parameter mu.");
+ public static final OptionID K_ID = new OptionID("hico.k", "Optional parameter to specify the number of nearest neighbors considered in the PCA. If this parameter is not set, k is set to the value of parameter mu.");
/**
* Parameter to specify the threshold of a distance between a vector q and a
@@ -105,7 +106,7 @@ public class HiCO<V extends NumberVector<V, ?>> extends OPTICS<V, PCACorrelation
* Key: {@code -hico.delta}
* </p>
*/
- public static final OptionID DELTA_ID = OptionID.getOrCreateOptionID("hico.delta", "Threshold of a distance between a vector q and a given space that indicates that " + "q adds a new dimension to the space.");
+ public static final OptionID DELTA_ID = new OptionID("hico.delta", "Threshold of a distance between a vector q and a given space that indicates that " + "q adds a new dimension to the space.");
/**
* The threshold for 'strong' eigenvectors: the 'strong' eigenvectors explain
@@ -117,7 +118,7 @@ public class HiCO<V extends NumberVector<V, ?>> extends OPTICS<V, PCACorrelation
* Key: {@code -hico.alpha}
* </p>
*/
- public static final OptionID ALPHA_ID = OptionID.getOrCreateOptionID("hico.alpha", "The threshold for 'strong' eigenvectors: the 'strong' eigenvectors explain a portion of at least alpha of the total variance.");
+ public static final OptionID ALPHA_ID = new OptionID("hico.alpha", "The threshold for 'strong' eigenvectors: the 'strong' eigenvectors explain a portion of at least alpha of the total variance.");
/**
* The default value for {@link #DELTA_ID}.
@@ -131,7 +132,7 @@ public class HiCO<V extends NumberVector<V, ?>> extends OPTICS<V, PCACorrelation
/**
* Constructor.
- *
+ *
* @param distanceFunction Distance function
* @param mu Mu parameter
*/
@@ -141,31 +142,34 @@ public class HiCO<V extends NumberVector<V, ?>> extends OPTICS<V, PCACorrelation
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
- * Parameterization class.
- *
- * @author Erich Schubert
- *
- * @apiviz.exclude
+ * Parameterization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
*/
- public static class Parameterizer<V extends NumberVector<V, ?>> extends AbstractParameterizer {
+ public static class Parameterizer<V extends NumberVector<?>> extends AbstractParameterizer {
int mu = -1;
-
+
PCABasedCorrelationDistanceFunction distance;
-
+
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- IntParameter muP = new IntParameter(MU_ID, new GreaterConstraint(0));
+ IntParameter muP = new IntParameter(MU_ID);
+ muP.addConstraint(new GreaterConstraint(0));
if (config.grab(muP)) {
mu = muP.getValue();
}
- IntParameter kP = new IntParameter(K_ID, new GreaterConstraint(0), true);
+ IntParameter kP = new IntParameter(K_ID);
+ kP.addConstraint(new GreaterConstraint(0));
+ kP.setOptional(true);
final int k;
if (config.grab(kP)) {
k = kP.getValue();
@@ -173,16 +177,19 @@ public class HiCO<V extends NumberVector<V, ?>> extends OPTICS<V, PCACorrelation
k = mu;
}
- DoubleParameter deltaP = new DoubleParameter(DELTA_ID, new GreaterEqualConstraint(0), DEFAULT_DELTA);
+ DoubleParameter deltaP = new DoubleParameter(DELTA_ID, DEFAULT_DELTA);
+ deltaP.addConstraint(new GreaterEqualConstraint(0));
double delta = DEFAULT_DELTA;
if (config.grab(deltaP)) {
- delta = deltaP.getValue();
+ delta = deltaP.doubleValue();
}
- DoubleParameter alphaP = new DoubleParameter(ALPHA_ID, new IntervalConstraint(0.0, IntervalConstraint.IntervalBoundary.OPEN, 1.0, IntervalConstraint.IntervalBoundary.OPEN), DEFAULT_ALPHA);
+ DoubleParameter alphaP = new DoubleParameter(ALPHA_ID, DEFAULT_ALPHA);
+ alphaP.addConstraint(new GreaterConstraint(0.0));
+ alphaP.addConstraint(new LessConstraint(1.0));
double alpha = DEFAULT_ALPHA;
if (config.grab(alphaP)) {
- alpha = alphaP.getValue();
+ alpha = alphaP.doubleValue();
}
// Configure Distance function
@@ -203,4 +210,4 @@ public class HiCO<V extends NumberVector<V, ?>> extends OPTICS<V, PCACorrelation
return new HiCO<V>(distance, mu);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/LMCLUS.java b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/LMCLUS.java
index b8942de8..fdea8b35 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/LMCLUS.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/LMCLUS.java
@@ -1,4 +1,5 @@
package de.lmu.ifi.dbs.elki.algorithm.clustering.correlation;
+
/*
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
@@ -23,7 +24,6 @@ package de.lmu.ifi.dbs.elki.algorithm.clustering.correlation;
*/
import java.util.ArrayList;
-import java.util.Iterator;
import java.util.List;
import java.util.Random;
@@ -40,24 +40,26 @@ import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.ids.ModifiableDBIDs;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.logging.progress.FiniteProgress;
import de.lmu.ifi.dbs.elki.logging.progress.IndefiniteProgress;
import de.lmu.ifi.dbs.elki.math.MeanVariance;
-import de.lmu.ifi.dbs.elki.math.histograms.FlexiHistogram;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Matrix;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
+import de.lmu.ifi.dbs.elki.utilities.RandomFactory;
+import de.lmu.ifi.dbs.elki.utilities.datastructures.histogram.DoubleDynamicHistogram;
+import de.lmu.ifi.dbs.elki.utilities.datastructures.histogram.DoubleHistogram;
+import de.lmu.ifi.dbs.elki.utilities.datastructures.histogram.DoubleStaticHistogram.Iter;
import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
import de.lmu.ifi.dbs.elki.utilities.exceptions.AbortException;
-import de.lmu.ifi.dbs.elki.utilities.exceptions.UnableToComplyException;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterEqualConstraint;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.DoubleParameter;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
-import de.lmu.ifi.dbs.elki.utilities.pairs.DoubleObjPair;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.RandomParameter;
/**
* Linear manifold clustering in high dimensional spaces by stochastic search.
@@ -83,17 +85,17 @@ public class LMCLUS extends AbstractAlgorithm<Clustering<Model>> {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(LMCLUS.class);
+ private static final Logging LOG = Logging.getLogger(LMCLUS.class);
/**
* Epsilon
*/
- private final static double NOT_FROM_ONE_CLUSTER_PROBABILITY = 0.2;
+ private static final double NOT_FROM_ONE_CLUSTER_PROBABILITY = 0.2;
/**
* Histogram resolution
*/
- private final static int BINS = 50;
+ private static final int BINS = 50;
/**
* The current threshold value calculated by the findSeperation Method.
@@ -114,6 +116,11 @@ public class LMCLUS extends AbstractAlgorithm<Clustering<Model>> {
* Number of sampling rounds to find a good split
*/
private final int samplingLevel;
+
+ /**
+ * Random factory
+ */
+ private final RandomFactory rnd;
/**
* Constructor.
@@ -122,13 +129,15 @@ public class LMCLUS extends AbstractAlgorithm<Clustering<Model>> {
* @param minsize Minimum cluster size
* @param samplingLevel Sampling level
* @param sensitivityThreshold Threshold
+ * @param rnd Random factory
*/
- public LMCLUS(int maxdim, int minsize, int samplingLevel, double sensitivityThreshold) {
+ public LMCLUS(int maxdim, int minsize, int samplingLevel, double sensitivityThreshold, RandomFactory rnd) {
super();
this.maxLMDim = maxdim;
this.minsize = minsize;
this.samplingLevel = samplingLevel;
this.sensitivityThreshold = sensitivityThreshold;
+ this.rnd = rnd;
}
/**
@@ -148,40 +157,40 @@ public class LMCLUS extends AbstractAlgorithm<Clustering<Model>> {
* @param database The database to operate on
* @param relation Relation
* @return Clustering result
- * @throws de.lmu.ifi.dbs.elki.utilities.UnableToComplyException
*/
- public Clustering<Model> run(Database database, Relation<NumberVector<?, ?>> relation) throws UnableToComplyException {
+ public Clustering<Model> run(Database database, Relation<NumberVector<?>> relation) {
Clustering<Model> ret = new Clustering<Model>("LMCLUS Clustering", "lmclus-clustering");
- FiniteProgress progress = logger.isVerbose() ? new FiniteProgress("Clustered objects", relation.size(), logger) : null;
- IndefiniteProgress cprogress = logger.isVerbose() ? new IndefiniteProgress("Clusters found", logger) : null;
+ FiniteProgress progress = LOG.isVerbose() ? new FiniteProgress("Clustered objects", relation.size(), LOG) : null;
+ IndefiniteProgress cprogress = LOG.isVerbose() ? new IndefiniteProgress("Clusters found", LOG) : null;
ModifiableDBIDs unclustered = DBIDUtil.newHashSet(relation.getDBIDs());
+ Random r = rnd.getRandom();
- final int maxdim = Math.min(maxLMDim, DatabaseUtil.dimensionality(relation));
+ final int maxdim = Math.min(maxLMDim, RelationUtil.dimensionality(relation));
int cnum = 0;
- while(unclustered.size() > minsize) {
+ while (unclustered.size() > minsize) {
DBIDs current = unclustered;
int lmDim = 1;
- for(int k = 1; k <= maxdim; k++) {
+ for (int k = 1; k <= maxdim; k++) {
// Implementation note: this while loop is from the original publication
// and the published LMCLUS source code. It doesn't make sense to me -
// it is lacking a stop criterion other than "cluster is too small" and
// "cluster is inseparable"! Additionally, there is good criterion for
// stopping at the appropriate dimensionality either.
- while(true) {
- Separation separation = findSeparation(relation, current, k);
+ while (true) {
+ Separation separation = findSeparation(relation, current, k, r);
// logger.verbose("k: " + k + " goodness: " + separation.goodness +
// " threshold: " + separation.threshold);
- if(separation.goodness <= sensitivityThreshold) {
+ if (separation.goodness <= sensitivityThreshold) {
break;
}
ModifiableDBIDs subset = DBIDUtil.newArray(current.size());
- for(DBIDIter iter = current.iter(); iter.valid(); iter.advance()) {
- if(deviation(relation.get(iter).getColumnVector().minusEquals(separation.originV), separation.basis) < separation.threshold) {
+ for (DBIDIter iter = current.iter(); iter.valid(); iter.advance()) {
+ if (deviation(relation.get(iter).getColumnVector().minusEquals(separation.originV), separation.basis) < separation.threshold) {
subset.add(iter);
}
}
// logger.verbose("size:"+subset.size());
- if(subset.size() < minsize) {
+ if (subset.size() < minsize) {
break;
}
current = subset;
@@ -190,7 +199,7 @@ public class LMCLUS extends AbstractAlgorithm<Clustering<Model>> {
}
}
// No more clusters found
- if(current.size() < minsize || current == unclustered) {
+ if (current.size() < minsize || current == unclustered) {
break;
}
// New cluster found
@@ -201,23 +210,23 @@ public class LMCLUS extends AbstractAlgorithm<Clustering<Model>> {
ret.addCluster(cluster);
// Remove from main working set.
unclustered.removeDBIDs(current);
- if(progress != null) {
- progress.setProcessed(relation.size() - unclustered.size(), logger);
+ if (progress != null) {
+ progress.setProcessed(relation.size() - unclustered.size(), LOG);
}
- if(cprogress != null) {
- cprogress.setProcessed(cnum, logger);
+ if (cprogress != null) {
+ cprogress.setProcessed(cnum, LOG);
}
}
// Remaining objects are noise
- if(unclustered.size() > 0) {
+ if (unclustered.size() > 0) {
ret.addCluster(new Cluster<Model>(unclustered, true));
}
- if(progress != null) {
- progress.setProcessed(relation.size(), logger);
- progress.ensureCompleted(logger);
+ if (progress != null) {
+ progress.setProcessed(relation.size(), LOG);
+ progress.ensureCompleted(LOG);
}
- if(cprogress != null) {
- cprogress.setCompleted(logger);
+ if (cprogress != null) {
+ cprogress.setCompleted(LOG);
}
return ret;
}
@@ -251,19 +260,19 @@ public class LMCLUS extends AbstractAlgorithm<Clustering<Model>> {
* @param relation The vector relation
* @param currentids Current DBIDs
* @param dimension the dimension of the linear manifold to sample.
+ * @param r Random generator
* @return the overall goodness of the separation. The values origin basis and
* threshold are returned indirectly over class variables.
*/
- private Separation findSeparation(Relation<NumberVector<?, ?>> relation, DBIDs currentids, int dimension) {
+ private Separation findSeparation(Relation<NumberVector<?>> relation, DBIDs currentids, int dimension, Random r) {
Separation separation = new Separation();
// determine the number of samples needed, to secure that with a specific
// probability
// in at least on sample every sampled point is from the same cluster.
int samples = (int) Math.min(Math.log(NOT_FROM_ONE_CLUSTER_PROBABILITY) / (Math.log(1 - Math.pow((1.0d / samplingLevel), dimension))), (double) currentids.size());
// System.out.println("Number of samples: " + samples);
- Random r = new Random();
int remaining_retries = 100;
- for(int i = 1; i <= samples; i++) {
+ for (int i = 1; i <= samples; i++) {
DBIDs sample = DBIDUtil.randomSample(currentids, dimension + 1, r.nextLong());
final DBIDIter iter = sample.iter();
// Use first as origin
@@ -273,36 +282,36 @@ public class LMCLUS extends AbstractAlgorithm<Clustering<Model>> {
Matrix basis;
{
List<Vector> vectors = new ArrayList<Vector>(sample.size() - 1);
- for(;iter.valid(); iter.advance()) {
+ for (; iter.valid(); iter.advance()) {
Vector vec = relation.get(iter).getColumnVector();
vectors.add(vec.minusEquals(originV));
}
// generate orthogonal basis
basis = generateOrthonormalBasis(vectors);
- if(basis == null) {
+ if (basis == null) {
// new sample has to be taken.
i--;
remaining_retries--;
- if(remaining_retries < 0) {
+ if (remaining_retries < 0) {
throw new AbortException("Too many retries in sampling, and always a linear dependant data set.");
}
continue;
}
}
// Generate and fill a histogram.
- FlexiHistogram<Double, Double> histogram = FlexiHistogram.DoubleSumHistogram(BINS);
+ DoubleDynamicHistogram histogram = new DoubleDynamicHistogram(BINS);
double w = 1.0 / currentids.size();
- for(DBIDIter iter2 = currentids.iter(); iter2.valid(); iter2.advance()) {
+ for (DBIDIter iter2 = currentids.iter(); iter2.valid(); iter2.advance()) {
// Skip sampled points
- if(sample.contains(iter2)) {
+ if (sample.contains(iter2)) {
continue;
}
Vector vec = relation.get(iter2).getColumnVector().minusEquals(originV);
final double distance = deviation(vec, basis);
- histogram.aggregate(distance, w);
+ histogram.increment(distance, w);
}
double[] th = findAndEvaluateThreshold(histogram); // evaluate threshold
- if(th[1] > separation.goodness) {
+ if (th[1] > separation.goodness) {
separation.goodness = th[1];
separation.threshold = th[0];
separation.originV = originV;
@@ -332,17 +341,17 @@ public class LMCLUS extends AbstractAlgorithm<Clustering<Model>> {
first = first.times(1.0 / first.euclideanLength());
Matrix ret = new Matrix(first.getDimensionality(), vectors.size());
ret.setCol(0, first);
- for(int i = 1; i < vectors.size(); i++) {
+ for (int i = 1; i < vectors.size(); i++) {
// System.out.println("Matrix:" + ret);
Vector v_i = vectors.get(i);
Vector u_i = v_i.copy();
// System.out.println("Vector " + i + ":" + partialSol);
- for(int j = 0; j < i; j++) {
+ for (int j = 0; j < i; j++) {
Vector v_j = ret.getCol(j);
double f = v_i.transposeTimes(v_j) / v_j.transposeTimes(v_j);
- if(Double.isNaN(f)) {
- if(logger.isDebuggingFine()) {
- logger.debugFine("Zero vector encountered? " + v_j);
+ if (Double.isNaN(f)) {
+ if (LOG.isDebuggingFine()) {
+ LOG.debugFine("Zero vector encountered? " + v_j);
}
return null;
}
@@ -350,9 +359,9 @@ public class LMCLUS extends AbstractAlgorithm<Clustering<Model>> {
}
// check if the vectors weren't independent
final double len_u_i = u_i.euclideanLength();
- if(len_u_i == 0.0) {
- if(logger.isDebuggingFine()) {
- logger.debugFine("Points not independent - no orthonormalization.");
+ if (len_u_i == 0.0) {
+ if (LOG.isDebuggingFine()) {
+ LOG.debugFine("Points not independent - no orthonormalization.");
}
return null;
}
@@ -369,7 +378,7 @@ public class LMCLUS extends AbstractAlgorithm<Clustering<Model>> {
* @param histogram Histogram to evaluate
* @return Position and goodness
*/
- private double[] findAndEvaluateThreshold(FlexiHistogram<Double, Double> histogram) {
+ private double[] findAndEvaluateThreshold(DoubleDynamicHistogram histogram) {
int n = histogram.getNumBins();
double[] p1 = new double[n];
double[] p2 = new double[n];
@@ -381,11 +390,10 @@ public class LMCLUS extends AbstractAlgorithm<Clustering<Model>> {
// Forward pass
{
MeanVariance mv = new MeanVariance();
- Iterator<DoubleObjPair<Double>> forward = histogram.iterator();
- for(int i = 0; forward.hasNext(); i++) {
- DoubleObjPair<Double> pair = forward.next();
- p1[i] = pair.second + ((i > 0) ? p1[i - 1] : 0);
- mv.put(i, pair.second);
+ DoubleHistogram.Iter forward = histogram.iter();
+ for (int i = 0; forward.valid(); i++, forward.advance()) {
+ p1[i] = forward.getValue() + ((i > 0) ? p1[i - 1] : 0);
+ mv.put(i, forward.getValue());
mu1[i] = mv.getMean();
sigma1[i] = mv.getNaiveStddev();
}
@@ -393,17 +401,18 @@ public class LMCLUS extends AbstractAlgorithm<Clustering<Model>> {
// Backwards pass
{
MeanVariance mv = new MeanVariance();
- Iterator<DoubleObjPair<Double>> backwards = histogram.reverseIterator();
- for(int j = n - 1; backwards.hasNext(); j--) {
- DoubleObjPair<Double> pair = backwards.next();
- p2[j] = pair.second + ((j + 1 < n) ? p2[j + 1] : 0);
- mv.put(j, pair.second);
+ DoubleHistogram.Iter backwards = histogram.iter();
+ backwards.seek(histogram.getNumBins() - 1); // Seek to last
+
+ for (int j = n - 1; backwards.valid(); j--, backwards.retract()) {
+ p2[j] = backwards.getValue() + ((j + 1 < n) ? p2[j + 1] : 0);
+ mv.put(j, backwards.getValue());
mu2[j] = mv.getMean();
sigma2[j] = mv.getNaiveStddev();
}
}
- for(int i = 0; i < n; i++) {
+ for (int i = 0; i < n; i++) {
jt[i] = 1.0 + 2 * (p1[i] * (Math.log(sigma1[i]) - Math.log(p1[i])) + p2[i] * (Math.log(sigma2[i]) - Math.log(p2[i])));
}
@@ -411,23 +420,23 @@ public class LMCLUS extends AbstractAlgorithm<Clustering<Model>> {
double bestgoodness = Double.NEGATIVE_INFINITY;
double devPrev = jt[1] - jt[0];
- for(int i = 1; i < jt.length - 1; i++) {
+ for (int i = 1; i < jt.length - 1; i++) {
double devCur = jt[i + 1] - jt[i];
// System.out.println(p1[i]);
// System.out.println(jt[i + 1]);
// System.out.println(jt[i]);
// System.out.println(devCur);
// Local minimum found - calculate depth
- if(devCur >= 0 && devPrev <= 0) {
+ if (devCur >= 0 && devPrev <= 0) {
double lowestMaxima = Double.POSITIVE_INFINITY;
- for(int j = i - 1; j > 0; j--) {
- if(jt[j - 1] < jt[j]) {
+ for (int j = i - 1; j > 0; j--) {
+ if (jt[j - 1] < jt[j]) {
lowestMaxima = Math.min(lowestMaxima, jt[j]);
break;
}
}
- for(int j = i + 1; j < n - 2; j++) {
- if(jt[j + 1] < jt[j]) {
+ for (int j = i + 1; j < n - 2; j++) {
+ if (jt[j + 1] < jt[j]) {
lowestMaxima = Math.min(lowestMaxima, jt[j]);
break;
}
@@ -436,23 +445,25 @@ public class LMCLUS extends AbstractAlgorithm<Clustering<Model>> {
final double mud = mu1[i] - mu2[i];
double discriminability = mud * mud / (sigma1[i] * sigma1[i] + sigma2[i] * sigma2[i]);
- if(Double.isNaN(discriminability)) {
+ if (Double.isNaN(discriminability)) {
discriminability = -1;
}
double goodness = localDepth * discriminability;
- if(goodness > bestgoodness) {
+ if (goodness > bestgoodness) {
bestgoodness = goodness;
bestpos = i;
}
}
devPrev = devCur;
}
- return new double[] { histogram.getBinMax(bestpos), bestgoodness };
+ Iter iter = histogram.iter();
+ iter.seek(bestpos);
+ return new double[] { iter.getRight(), bestgoodness };
}
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
@Override
@@ -500,22 +511,27 @@ public class LMCLUS extends AbstractAlgorithm<Clustering<Model>> {
/**
* Parameter with the maximum dimension to search for
*/
- public static final OptionID MAXDIM_ID = OptionID.getOrCreateOptionID("lmclus.maxdim", "Maximum linear manifold dimension to search.");
+ public static final OptionID MAXDIM_ID = new OptionID("lmclus.maxdim", "Maximum linear manifold dimension to search.");
/**
* Parameter for the minimum cluster size
*/
- public static final OptionID MINSIZE_ID = OptionID.getOrCreateOptionID("lmclus.minsize", "Minimum cluster size to allow.");
+ public static final OptionID MINSIZE_ID = new OptionID("lmclus.minsize", "Minimum cluster size to allow.");
/**
* Sampling intensity level
*/
- public static final OptionID SAMPLINGL_ID = OptionID.getOrCreateOptionID("lmclus.sampling-level", "A number used to determine how many samples are taken in each search.");
+ public static final OptionID SAMPLINGL_ID = new OptionID("lmclus.sampling-level", "A number used to determine how many samples are taken in each search.");
/**
* Global significance threshold
*/
- public static final OptionID THRESHOLD_ID = OptionID.getOrCreateOptionID("lmclus.threshold", "Threshold to determine if a cluster was found.");
+ public static final OptionID THRESHOLD_ID = new OptionID("lmclus.threshold", "Threshold to determine if a cluster was found.");
+
+ /**
+ * Random seeding
+ */
+ public static final OptionID RANDOM_ID = new OptionID("lmclus.seed", "Random generator seed.");
/**
* Maximum dimensionality to search for
@@ -536,31 +552,43 @@ public class LMCLUS extends AbstractAlgorithm<Clustering<Model>> {
* Threshold
*/
private double threshold;
+
+ /**
+ * Random generator
+ */
+ private RandomFactory rnd;
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- IntParameter maxLMDimP = new IntParameter(MAXDIM_ID, new GreaterEqualConstraint(1), true);
- if(config.grab(maxLMDimP)) {
+ IntParameter maxLMDimP = new IntParameter(MAXDIM_ID);
+ maxLMDimP.addConstraint(new GreaterEqualConstraint(1));
+ maxLMDimP.setOptional(true);
+ if (config.grab(maxLMDimP)) {
maxdim = maxLMDimP.getValue();
}
- IntParameter minsizeP = new IntParameter(MINSIZE_ID, new GreaterEqualConstraint(1));
- if(config.grab(minsizeP)) {
+ IntParameter minsizeP = new IntParameter(MINSIZE_ID);
+ minsizeP.addConstraint(new GreaterEqualConstraint(1));
+ if (config.grab(minsizeP)) {
minsize = minsizeP.getValue();
}
IntParameter samplingLevelP = new IntParameter(SAMPLINGL_ID, 100);
- if(config.grab(samplingLevelP)) {
+ if (config.grab(samplingLevelP)) {
samplingLevel = samplingLevelP.getValue();
}
DoubleParameter sensivityThresholdP = new DoubleParameter(THRESHOLD_ID);
- if(config.grab(sensivityThresholdP)) {
+ if (config.grab(sensivityThresholdP)) {
threshold = sensivityThresholdP.getValue();
}
+ RandomParameter rndP = new RandomParameter(RANDOM_ID);
+ if (config.grab(rndP)) {
+ rnd = rndP.getValue();
+ }
}
@Override
protected LMCLUS makeInstance() {
- return new LMCLUS(maxdim, minsize, samplingLevel, threshold);
+ return new LMCLUS(maxdim, minsize, samplingLevel, threshold, rnd);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/ORCLUS.java b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/ORCLUS.java
index 2e9f4a9b..f567098b 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/ORCLUS.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/ORCLUS.java
@@ -37,33 +37,35 @@ import de.lmu.ifi.dbs.elki.data.model.Model;
import de.lmu.ifi.dbs.elki.data.type.TypeInformation;
import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
import de.lmu.ifi.dbs.elki.database.Database;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.ids.ModifiableDBIDs;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
-import de.lmu.ifi.dbs.elki.database.query.GenericDistanceResultPair;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.GenericDistanceDBIDList;
import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.logging.progress.IndefiniteProgress;
+import de.lmu.ifi.dbs.elki.math.linearalgebra.Centroid;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Matrix;
import de.lmu.ifi.dbs.elki.math.linearalgebra.SortedEigenPairs;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector;
import de.lmu.ifi.dbs.elki.math.linearalgebra.pca.PCAResult;
import de.lmu.ifi.dbs.elki.math.linearalgebra.pca.PCARunner;
import de.lmu.ifi.dbs.elki.utilities.ClassGenericsUtil;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
+import de.lmu.ifi.dbs.elki.utilities.RandomFactory;
import de.lmu.ifi.dbs.elki.utilities.documentation.Description;
import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
import de.lmu.ifi.dbs.elki.utilities.documentation.Title;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.IntervalConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.LessEqualConstraint;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.DoubleParameter;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.LongParameter;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.RandomParameter;
/**
* ORCLUS provides the ORCLUS algorithm, an algorithm to find clusters in high
@@ -83,38 +85,21 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.LongParameter;
@Title("ORCLUS: Arbitrarily ORiented projected CLUSter generation")
@Description("Algorithm to find correlation clusters in high dimensional spaces.")
@Reference(authors = "C. C. Aggarwal, P. S. Yu", title = "Finding Generalized Projected Clusters in High Dimensional Spaces", booktitle = "Proc. ACM SIGMOD Int. Conf. on Management of Data (SIGMOD '00)", url = "http://dx.doi.org/10.1145/342009.335383")
-public class ORCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClustering<Clustering<Model>, V> {
+public class ORCLUS<V extends NumberVector<?>> extends AbstractProjectedClustering<Clustering<Model>, V> {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(ORCLUS.class);
+ private static final Logging LOG = Logging.getLogger(ORCLUS.class);
/**
- * Parameter to specify the factor for reducing the number of current clusters
- * in each iteration, must be an integer greater than 0 and less than 1.
- * <p>
- * Default value: {@code 0.5}
- * </p>
- * <p>
- * Key: {@code -orclus.alpha}
- * </p>
- */
- public static final OptionID ALPHA_ID = OptionID.getOrCreateOptionID("orclus.alpha", "The factor for reducing the number of current clusters in each iteration.");
-
- /**
- * Parameter to specify the random generator seed.
- */
- public static final OptionID SEED_ID = OptionID.getOrCreateOptionID("orclus.seed", "The random number generator seed.");
-
- /**
- * Holds the value of {@link #ALPHA_ID}.
+ * Holds the value of {@link Parameterizer#ALPHA_ID}.
*/
private double alpha;
/**
- * Holds the value of {@link #SEED_ID}.
+ * Random generator
*/
- private Long seed;
+ private RandomFactory rnd;
/**
* The PCA utility object.
@@ -128,13 +113,13 @@ public class ORCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClust
* @param k_i k_i Parameter
* @param l l Parameter
* @param alpha Alpha Parameter
- * @param seed Seed parameter
+ * @param rnd Random generator
* @param pca PCA runner
*/
- public ORCLUS(int k, int k_i, int l, double alpha, long seed, PCARunner<V> pca) {
+ public ORCLUS(int k, int k_i, int l, double alpha, RandomFactory rnd, PCARunner<V> pca) {
super(k, k_i, l);
this.alpha = alpha;
- this.seed = seed;
+ this.rnd = rnd;
this.pca = pca;
}
@@ -148,9 +133,9 @@ public class ORCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClust
try {
DistanceQuery<V, DoubleDistance> distFunc = this.getDistanceQuery(database);
// current dimensionality associated with each seed
- int dim_c = DatabaseUtil.dimensionality(relation);
+ int dim_c = RelationUtil.dimensionality(relation);
- if(dim_c < l) {
+ if (dim_c < l) {
throw new IllegalStateException("Dimensionality of data < parameter l! " + "(" + dim_c + " < " + l + ")");
}
@@ -162,19 +147,19 @@ public class ORCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClust
double beta = StrictMath.exp(-StrictMath.log((double) dim_c / (double) l) * StrictMath.log(1 / alpha) / StrictMath.log((double) k_c / (double) k));
- IndefiniteProgress cprogress = logger.isVerbose() ? new IndefiniteProgress("Current number of clusters:", logger) : null;
+ IndefiniteProgress cprogress = LOG.isVerbose() ? new IndefiniteProgress("Current number of clusters:", LOG) : null;
- while(k_c > k) {
- if(cprogress != null) {
- cprogress.setProcessed(clusters.size(), logger);
+ while (k_c > k) {
+ if (cprogress != null) {
+ cprogress.setProcessed(clusters.size(), LOG);
}
// find partitioning induced by the seeds of the clusters
assign(relation, distFunc, clusters);
// determine current subspace associated with each cluster
- for(ORCLUSCluster cluster : clusters) {
- if(cluster.objectIDs.size() > 0) {
+ for (ORCLUSCluster cluster : clusters) {
+ if (cluster.objectIDs.size() > 0) {
cluster.basis = findBasis(relation, distFunc, cluster, dim_c);
}
}
@@ -187,19 +172,18 @@ public class ORCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClust
}
assign(relation, distFunc, clusters);
- if(cprogress != null) {
+ if (cprogress != null) {
cprogress.setProcessed(clusters.size());
- cprogress.setCompleted(logger);
+ cprogress.setCompleted(LOG);
}
// get the result
Clustering<Model> r = new Clustering<Model>("ORCLUS clustering", "orclus-clustering");
- for(ORCLUSCluster c : clusters) {
+ for (ORCLUSCluster c : clusters) {
r.addCluster(new Cluster<Model>(c.objectIDs, ClusterModel.CLUSTER));
}
return r;
- }
- catch(Exception e) {
+ } catch (Exception e) {
throw new IllegalStateException(e);
}
}
@@ -212,11 +196,11 @@ public class ORCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClust
* @return the initial seed list
*/
private List<ORCLUSCluster> initialSeeds(Relation<V> database, int k) {
- DBIDs randomSample = DBIDUtil.randomSample(database.getDBIDs(), k, seed);
- V factory = DatabaseUtil.assumeVectorField(database).getFactory();
+ DBIDs randomSample = DBIDUtil.randomSample(database.getDBIDs(), k, rnd);
+ NumberVector.Factory<V, ?> factory = RelationUtil.getNumberVectorFactory(database);
List<ORCLUSCluster> seeds = new ArrayList<ORCLUSCluster>();
- for(DBIDIter iter = randomSample.iter(); iter.valid(); iter.advance()) {
- seeds.add(new ORCLUSCluster(database.get(iter), iter.getDBID(), factory));
+ for (DBIDIter iter = randomSample.iter(); iter.valid(); iter.advance()) {
+ seeds.add(new ORCLUSCluster(database.get(iter), iter, factory));
}
return seeds;
}
@@ -231,15 +215,15 @@ public class ORCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClust
* assigned to
*/
private void assign(Relation<V> database, DistanceQuery<V, DoubleDistance> distFunc, List<ORCLUSCluster> clusters) {
- V factory = DatabaseUtil.assumeVectorField(database).getFactory();
+ NumberVector.Factory<V, ?> factory = RelationUtil.getNumberVectorFactory(database);
// clear the current clusters
- for(ORCLUSCluster cluster : clusters) {
+ for (ORCLUSCluster cluster : clusters) {
cluster.objectIDs.clear();
}
// projected centroids of the clusters
List<V> projectedCentroids = new ArrayList<V>(clusters.size());
- for(ORCLUSCluster c : clusters) {
+ for (ORCLUSCluster c : clusters) {
projectedCentroids.add(projection(c, c.centroid, factory));
}
@@ -251,11 +235,11 @@ public class ORCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClust
ORCLUSCluster minCluster = null;
// determine projected distance between o and cluster
- for(int i = 0; i < clusters.size(); i++) {
+ for (int i = 0; i < clusters.size(); i++) {
ORCLUSCluster c = clusters.get(i);
V o_proj = projection(c, o, factory);
DoubleDistance dist = distFunc.distance(o_proj, projectedCentroids.get(i));
- if(minDist == null || minDist.compareTo(dist) > 0) {
+ if (minDist == null || minDist.compareTo(dist) > 0) {
minDist = dist;
minCluster = c;
}
@@ -266,9 +250,9 @@ public class ORCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClust
}
// recompute the seed in each clusters
- for(ORCLUSCluster cluster : clusters) {
- if(cluster.objectIDs.size() > 0) {
- cluster.centroid = DatabaseUtil.centroid(database, cluster.objectIDs);
+ for (ORCLUSCluster cluster : clusters) {
+ if (cluster.objectIDs.size() > 0) {
+ cluster.centroid = Centroid.make(database, cluster.objectIDs).toVector(database);
}
}
}
@@ -286,13 +270,12 @@ public class ORCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClust
private Matrix findBasis(Relation<V> database, DistanceQuery<V, DoubleDistance> distFunc, ORCLUSCluster cluster, int dim) {
// covariance matrix of cluster
// Matrix covariance = Util.covarianceMatrix(database, cluster.objectIDs);
- List<DistanceResultPair<DoubleDistance>> results = new ArrayList<DistanceResultPair<DoubleDistance>>(cluster.objectIDs.size());
- for(DBIDIter it = cluster.objectIDs.iter(); it.valid(); it.advance()) {
+ GenericDistanceDBIDList<DoubleDistance> results = new GenericDistanceDBIDList<DoubleDistance>(cluster.objectIDs.size());
+ for (DBIDIter it = cluster.objectIDs.iter(); it.valid(); it.advance()) {
DoubleDistance distance = distFunc.distance(cluster.centroid, database.get(it));
- DistanceResultPair<DoubleDistance> qr = new GenericDistanceResultPair<DoubleDistance>(distance, it.getDBID());
- results.add(qr);
+ results.add(distance, it);
}
- Collections.sort(results);
+ results.sort();
PCAResult pcares = pca.processQueryResult(results, database);
SortedEigenPairs eigenPairs = pcares.getEigenPairs();
return eigenPairs.reverseEigenVectors(dim);
@@ -321,9 +304,9 @@ public class ORCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClust
*/
private void merge(Relation<V> database, DistanceQuery<V, DoubleDistance> distFunc, List<ORCLUSCluster> clusters, int k_new, int d_new, IndefiniteProgress cprogress) {
ArrayList<ProjectedEnergy> projectedEnergies = new ArrayList<ProjectedEnergy>();
- for(int i = 0; i < clusters.size(); i++) {
- for(int j = 0; j < clusters.size(); j++) {
- if(i >= j) {
+ for (int i = 0; i < clusters.size(); i++) {
+ for (int j = 0; j < clusters.size(); j++) {
+ if (i >= j) {
continue;
}
// projected energy of c_ij in subspace e_ij
@@ -335,21 +318,21 @@ public class ORCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClust
}
}
- while(clusters.size() > k_new) {
- if(cprogress != null) {
- cprogress.setProcessed(clusters.size(), logger);
+ while (clusters.size() > k_new) {
+ if (cprogress != null) {
+ cprogress.setProcessed(clusters.size(), LOG);
}
// find the smallest value of r_ij
ProjectedEnergy minPE = Collections.min(projectedEnergies);
// renumber the clusters by replacing cluster c_i with cluster c_ij
// and discarding cluster c_j
- for(int c = 0; c < clusters.size(); c++) {
- if(c == minPE.i) {
+ for (int c = 0; c < clusters.size(); c++) {
+ if (c == minPE.i) {
clusters.remove(c);
clusters.add(c, minPE.cluster);
}
- if(c == minPE.j) {
+ if (c == minPE.j) {
clusters.remove(c);
}
}
@@ -358,16 +341,15 @@ public class ORCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClust
int i = minPE.i;
int j = minPE.j;
Iterator<ProjectedEnergy> it = projectedEnergies.iterator();
- while(it.hasNext()) {
+ while (it.hasNext()) {
ProjectedEnergy pe = it.next();
- if(pe.i == i || pe.i == j || pe.j == i || pe.j == j) {
+ if (pe.i == i || pe.i == j || pe.j == i || pe.j == j) {
it.remove();
- }
- else {
- if(pe.i > j) {
+ } else {
+ if (pe.i > j) {
pe.i -= 1;
}
- if(pe.j > j) {
+ if (pe.j > j) {
pe.j -= 1;
}
}
@@ -375,11 +357,10 @@ public class ORCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClust
// ... and recompute them
ORCLUSCluster c_ij = minPE.cluster;
- for(int c = 0; c < clusters.size(); c++) {
- if(c < i) {
+ for (int c = 0; c < clusters.size(); c++) {
+ if (c < i) {
projectedEnergies.add(projectedEnergy(database, distFunc, clusters.get(c), c_ij, c, i, d_new));
- }
- else if(c > i) {
+ } else if (c > i) {
projectedEnergies.add(projectedEnergy(database, distFunc, clusters.get(c), c_ij, i, c, d_new));
}
}
@@ -404,11 +385,11 @@ public class ORCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClust
private ProjectedEnergy projectedEnergy(Relation<V> database, DistanceQuery<V, DoubleDistance> distFunc, ORCLUSCluster c_i, ORCLUSCluster c_j, int i, int j, int dim) {
// union of cluster c_i and c_j
ORCLUSCluster c_ij = union(database, distFunc, c_i, c_j, dim);
- V factory = DatabaseUtil.assumeVectorField(database).getFactory();
+ NumberVector.Factory<V, ?> factory = RelationUtil.getNumberVectorFactory(database);
DoubleDistance sum = getDistanceFunction().getDistanceFactory().nullDistance();
V c_proj = projection(c_ij, c_ij.centroid, factory);
- for(DBIDIter iter = c_ij.objectIDs.iter(); iter.valid(); iter.advance()) {
+ for (DBIDIter iter = c_ij.objectIDs.iter(); iter.valid(); iter.advance()) {
V o_proj = projection(c_ij, database.get(iter), factory);
DoubleDistance dist = distFunc.distance(o_proj, c_proj);
sum = sum.plus(dist.times(dist));
@@ -436,16 +417,15 @@ public class ORCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClust
// convert into array.
c.objectIDs = DBIDUtil.newArray(c.objectIDs);
- if(c.objectIDs.size() > 0) {
- c.centroid = DatabaseUtil.centroid(relation, c.objectIDs);
+ if (c.objectIDs.size() > 0) {
+ c.centroid = Centroid.make(relation, c.objectIDs).toVector(relation);
c.basis = findBasis(relation, distFunc, c, dim);
- }
- else {
- V factory = DatabaseUtil.assumeVectorField(relation).getFactory();
- Vector cent = c1.centroid.getColumnVector().plusEquals(c2.centroid.getColumnVector()).timesEquals(0.5);
+ } else {
+ NumberVector.Factory<V, ?> factory = RelationUtil.getNumberVectorFactory(relation);
+ Vector cent = c1.centroid.getColumnVector().plusEquals(c2.centroid.getColumnVector()).timesEquals(0.5);
c.centroid = factory.newNumberVector(cent.getArrayRef());
double[][] doubles = new double[c1.basis.getRowDimensionality()][dim];
- for(int i = 0; i < dim; i++) {
+ for (int i = 0; i < dim; i++) {
doubles[i][i] = 1;
}
c.basis = new Matrix(doubles);
@@ -462,7 +442,7 @@ public class ORCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClust
* @param factory Factory object / prototype
* @return the projection of double vector o in the subspace of cluster c
*/
- private V projection(ORCLUSCluster c, V o, V factory) {
+ private V projection(ORCLUSCluster c, V o, NumberVector.Factory<V, ?> factory) {
Matrix o_proj = o.getColumnVector().transposeTimes(c.basis);
double[] values = o_proj.getColumnPackedCopy();
return factory.newNumberVector(values);
@@ -475,7 +455,7 @@ public class ORCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClust
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -511,21 +491,19 @@ public class ORCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClust
* Creates a new cluster containing the specified object o.
*
* @param o the object belonging to this cluster.
+ * @param id Object id
* @param factory Factory object / prototype
*/
- ORCLUSCluster(V o, DBID id, V factory) {
+ ORCLUSCluster(V o, DBIDRef id, NumberVector.Factory<V, ?> factory) {
this.objectIDs.add(id);
// initially the basis ist the original axis-system
int dim = o.getDimensionality();
this.basis = Matrix.unitMatrix(dim);
- // TODO: can we replace this with some kind of clone() statement?
// initially the centroid is the value array of o
- double[] values = new double[o.getDimensionality()];
- for(int d = 1; d <= o.getDimensionality(); d++) {
- values[d - 1] = o.doubleValue(d);
- }
+ double[] values = o.getColumnVector().getArrayRef();
+ // FIXME: avoid going through 'values'
this.centroid = factory.newNumberVector(values);
}
}
@@ -571,10 +549,28 @@ public class ORCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClust
*
* @apiviz.exclude
*/
- public static class Parameterizer<V extends NumberVector<V, ?>> extends AbstractProjectedClustering.Parameterizer {
+ public static class Parameterizer<V extends NumberVector<?>> extends AbstractProjectedClustering.Parameterizer {
+ /**
+ * Parameter to specify the factor for reducing the number of current
+ * clusters in each iteration, must be an integer greater than 0 and less
+ * than 1.
+ * <p>
+ * Default value: {@code 0.5}
+ * </p>
+ * <p>
+ * Key: {@code -orclus.alpha}
+ * </p>
+ */
+ public static final OptionID ALPHA_ID = new OptionID("orclus.alpha", "The factor for reducing the number of current clusters in each iteration.");
+
+ /**
+ * Parameter to specify the random generator seed.
+ */
+ public static final OptionID SEED_ID = new OptionID("orclus.seed", "The random number generator seed.");
+
protected double alpha = -1;
- protected Long seed = null;
+ protected RandomFactory rnd;
protected PCARunner<V> pca = null;
@@ -593,22 +589,24 @@ public class ORCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClust
}
protected void configAlpha(Parameterization config) {
- DoubleParameter alphaP = new DoubleParameter(ALPHA_ID, new IntervalConstraint(0, IntervalConstraint.IntervalBoundary.OPEN, 1, IntervalConstraint.IntervalBoundary.CLOSE), 0.5);
- if(config.grab(alphaP)) {
- alpha = alphaP.getValue();
+ DoubleParameter alphaP = new DoubleParameter(ALPHA_ID, 0.5);
+ alphaP.addConstraint(new GreaterConstraint(0));
+ alphaP.addConstraint(new LessEqualConstraint(1));
+ if (config.grab(alphaP)) {
+ alpha = alphaP.doubleValue();
}
}
protected void configSeed(Parameterization config) {
- LongParameter seedP = new LongParameter(SEED_ID, true);
- if(config.grab(seedP)) {
- seed = seedP.getValue();
+ RandomParameter rndP = new RandomParameter(SEED_ID);
+ if (config.grab(rndP)) {
+ rnd = rndP.getValue();
}
}
@Override
protected ORCLUS<V> makeInstance() {
- return new ORCLUS<V>(k, k_i, l, alpha, seed, pca);
+ return new ORCLUS<V>(k, k_i, l, alpha, rnd, pca);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/cash/CASHInterval.java b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/cash/CASHInterval.java
index 46112498..0153ddc3 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/cash/CASHInterval.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/cash/CASHInterval.java
@@ -42,7 +42,7 @@ import de.lmu.ifi.dbs.elki.logging.LoggingConfiguration;
*/
public class CASHInterval extends HyperBoundingBox implements Comparable<CASHInterval> {
/**
- * Serial version number
+ * Serial version number.
*/
private static final long serialVersionUID = 1;
@@ -141,14 +141,14 @@ public class CASHInterval extends HyperBoundingBox implements Comparable<CASHInt
/**
* Removes the specified ids from this interval.
*
- * @param ids the set of ids to be removed
+ * @param ids2 the set of ids to be removed
*/
- public void removeIDs(DBIDs ids) {
- this.ids.removeDBIDs(ids);
+ public void removeIDs(DBIDs ids2) {
+ this.ids.removeDBIDs(ids2);
}
/**
- * Returns the number of objects associated with this interval
+ * Returns the number of objects associated with this interval.
*
* @return the number of objects associated with this interval
*/
@@ -157,18 +157,6 @@ public class CASHInterval extends HyperBoundingBox implements Comparable<CASHInt
}
/**
- * Returns true if this interval has already been split in the specified
- * dimension.
- *
- * @param d the dimension to be tested
- * @return true if this interval has already been split in the specified
- * dimension
- */
- public boolean isSplit(int d) {
- return maxSplitDimension >= d;
- }
-
- /**
* Returns a String representation of the HyperBoundingBox.
*
* @return String
@@ -286,9 +274,6 @@ public class CASHInterval extends HyperBoundingBox implements Comparable<CASHInt
}
}
- /**
- * @see Object#equals(Object)
- */
@Override
public boolean equals(Object o) {
if(this == o) {
@@ -305,9 +290,6 @@ public class CASHInterval extends HyperBoundingBox implements Comparable<CASHInt
return super.equals(o);
}
- /**
- * Returns the unique id of this interval as hash code.
- */
@Override
public int hashCode() {
return intervalID.hashCode();
@@ -330,24 +312,23 @@ public class CASHInterval extends HyperBoundingBox implements Comparable<CASHInt
return;
}
- int dim = getDimensionality();
- int childLevel = isSplit(dim) ? level + 1 : level;
-
- int splitDim = isSplit(dim) ? 1 : maxSplitDimension + 1;
- double splitPoint = getMin(splitDim) + (getMax(splitDim) - getMin(splitDim)) / 2;
+ final boolean issplit = (maxSplitDimension >= (getDimensionality() - 1));
+ final int childLevel = issplit ? level + 1 : level;
+ final int splitDim = issplit ? 0 : maxSplitDimension + 1;
+ final double splitPoint = getMin(splitDim) + (getMax(splitDim) - getMin(splitDim)) * .5;
// left and right child
for(int i = 0; i < 2; i++) {
- double[] min = SpatialUtil.getMin(this);
- double[] max = SpatialUtil.getMax(this);
+ double[] min = SpatialUtil.getMin(this); // clone
+ double[] max = SpatialUtil.getMax(this); // clone
// right child
if(i == 0) {
- min[splitDim - 1] = splitPoint;
+ min[splitDim] = splitPoint;
}
// left child
else {
- max[splitDim - 1] = splitPoint;
+ max[splitDim] = splitPoint;
}
ModifiableDBIDs childIDs = split.determineIDs(getIDs(), new HyperBoundingBox(min, max), d_min, d_max);
@@ -364,7 +345,7 @@ public class CASHInterval extends HyperBoundingBox implements Comparable<CASHInt
}
if(LoggingConfiguration.DEBUG) {
- StringBuffer msg = new StringBuffer();
+ StringBuilder msg = new StringBuilder();
msg.append("\nchild level ").append(childLevel).append(", split Dim ").append(splitDim);
if(leftChild != null) {
msg.append("\nleft ").append(leftChild);
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/cash/CASHIntervalSplit.java b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/cash/CASHIntervalSplit.java
index b0a12832..12f10725 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/cash/CASHIntervalSplit.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/cash/CASHIntervalSplit.java
@@ -27,7 +27,6 @@ import java.util.HashMap;
import java.util.Map;
import de.lmu.ifi.dbs.elki.data.HyperBoundingBox;
-import de.lmu.ifi.dbs.elki.data.ParameterizationFunction;
import de.lmu.ifi.dbs.elki.data.spatial.SpatialUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
@@ -69,7 +68,7 @@ public class CASHIntervalSplit {
/**
* The logger of the class.
*/
- private final static Logging logger = Logging.getLogger(CASHIntervalSplit.class);
+ private static final Logging LOG = Logging.getLogger(CASHIntervalSplit.class);
/**
* Initializes the logger and sets the debug status to the given value.
@@ -99,8 +98,8 @@ public class CASHIntervalSplit {
* exceeds minPts, null otherwise
*/
public ModifiableDBIDs determineIDs(DBIDs superSetIDs, HyperBoundingBox interval, double d_min, double d_max) {
- StringBuffer msg = new StringBuffer();
- if(logger.isDebugging()) {
+ StringBuilder msg = LOG.isDebugging() ? new StringBuilder() : null;
+ if(msg != null) {
msg.append("interval ").append(interval);
}
@@ -116,7 +115,7 @@ public class CASHIntervalSplit {
}
for(DBIDIter iter = superSetIDs.iter(); iter.valid(); iter.advance()) {
- DBID id = iter.getDBID();
+ DBID id = DBIDUtil.deref(iter);
Double f_min = minima.get(id);
Double f_max = maxima.get(id);
@@ -129,7 +128,7 @@ public class CASHIntervalSplit {
maxima.put(id, f_max);
}
- if(logger.isDebugging()) {
+ if(msg != null) {
msg.append("\n\nf_min ").append(f_min);
msg.append("\nf_max ").append(f_max);
msg.append("\nd_min ").append(d_min);
@@ -142,21 +141,21 @@ public class CASHIntervalSplit {
if(f_min <= d_max && f_max >= d_min) {
childIDs.add(id);
- if(logger.isDebugging()) {
+ if(msg != null) {
msg.append("\nid ").append(id).append(" appended");
}
}
else {
- if(logger.isDebugging()) {
+ if(msg != null) {
msg.append("\nid ").append(id).append(" NOT appended");
}
}
}
- if(logger.isDebugging()) {
+ if(msg != null) {
msg.append("\nchildIds ").append(childIDs.size());
- logger.debugFine(msg.toString());
+ LOG.debugFine(msg.toString());
}
if(childIDs.size() < minPts) {
diff --git a/src/de/lmu/ifi/dbs/elki/data/ParameterizationFunction.java b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/cash/ParameterizationFunction.java
index 29e46c96..56e68bfe 100644
--- a/src/de/lmu/ifi/dbs/elki/data/ParameterizationFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/cash/ParameterizationFunction.java
@@ -1,4 +1,4 @@
-package de.lmu.ifi.dbs.elki.data;
+package de.lmu.ifi.dbs.elki.algorithm.clustering.correlation.cash;
/*
This file is part of ELKI:
@@ -23,16 +23,12 @@ package de.lmu.ifi.dbs.elki.data;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.util.List;
-
+import de.lmu.ifi.dbs.elki.data.HyperBoundingBox;
+import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.data.spatial.SpatialUtil;
+import de.lmu.ifi.dbs.elki.math.MathUtil;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector;
-import de.lmu.ifi.dbs.elki.result.textwriter.TextWriteable;
-import de.lmu.ifi.dbs.elki.result.textwriter.TextWriterStream;
import de.lmu.ifi.dbs.elki.utilities.FormatUtil;
-import de.lmu.ifi.dbs.elki.utilities.datastructures.arraylike.ArrayAdapter;
-import de.lmu.ifi.dbs.elki.utilities.datastructures.arraylike.NumberArrayAdapter;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
/**
* A parameterization function describes all lines in a d-dimensional feature
@@ -42,7 +38,7 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
*
* @author Elke Achtert
*/
-public class ParameterizationFunction extends DoubleVector implements TextWriteable {
+public class ParameterizationFunction {
/**
* Available types for the global extremum.
*
@@ -50,25 +46,20 @@ public class ParameterizationFunction extends DoubleVector implements TextWritea
*/
public enum ExtremumType {
/**
- * Minimum
+ * Minimum.
*/
MINIMUM,
/**
- * Maximum
+ * Maximum.
*/
MAXIMUM,
/**
- * Constant
+ * Constant.
*/
CONSTANT
}
/**
- * Static factory
- */
- public static final ParameterizationFunction STATIC = new ParameterizationFunction(new double[] { 0.0 });
-
- /**
* A small number to handle numbers near 0 as 0.
*/
public static final double DELTA = 1E-10;
@@ -82,48 +73,21 @@ public class ParameterizationFunction extends DoubleVector implements TextWritea
* Holds the type of the global extremum.
*/
private ExtremumType extremumType;
-
+
/**
- * Provides a new parameterization function describing all lines in a
- * d-dimensional feature space intersecting in one point p.
- *
- * @param values the values of the point p
- */
- public ParameterizationFunction(double[] values) {
- super(values);
- determineGlobalExtremum();
- }
-
- /**
- * Provides a new parameterization function describing all lines in a
- * d-dimensional feature space intersecting in one point p.
- *
- * @param values the values of the point p
- */
- public ParameterizationFunction(Double[] values) {
- super(values);
- determineGlobalExtremum();
- }
-
- /**
- * Provides a new parameterization function describing all lines in a
- * d-dimensional feature space intersecting in one point p.
- *
- * @param values the values of the point p
+ * The actual vector.
*/
- public ParameterizationFunction(List<Double> values) {
- super(values);
- determineGlobalExtremum();
- }
+ private NumberVector<?> vec;
/**
* Provides a new parameterization function describing all lines in a
* d-dimensional feature space intersecting in one point p.
*
- * @param columnMatrix the values of the point p
+ * @param vec Existing vector
*/
- public ParameterizationFunction(Vector columnMatrix) {
- super(columnMatrix);
+ public ParameterizationFunction(NumberVector<?> vec) {
+ super();
+ this.vec = vec;
determineGlobalExtremum();
}
@@ -134,7 +98,7 @@ public class ParameterizationFunction extends DoubleVector implements TextWritea
* @return the function value at alpha
*/
public double function(double[] alpha) {
- int d = getDimensionality();
+ final int d = vec.getDimensionality();
if(alpha.length != d - 1) {
throw new IllegalArgumentException("Parameter alpha must have a " + "dimensionality of " + (d - 1) + ", read: " + alpha.length);
}
@@ -142,7 +106,7 @@ public class ParameterizationFunction extends DoubleVector implements TextWritea
double result = 0;
for(int i = 0; i < d; i++) {
double alpha_i = i == d - 1 ? 0 : alpha[i];
- result += doubleValue(i + 1) * sinusProduct(0, i, alpha) * Math.cos(alpha_i);
+ result += vec.doubleValue(i) * sinusProduct(0, i, alpha) * Math.cos(alpha_i);
}
return result;
}
@@ -156,7 +120,7 @@ public class ParameterizationFunction extends DoubleVector implements TextWritea
* in the given interval
*/
public HyperBoundingBox determineAlphaMinMax(HyperBoundingBox interval) {
- int dim = getDimensionality();
+ final int dim = vec.getDimensionality();
if(interval.getDimensionality() != dim - 1) {
throw new IllegalArgumentException("Interval needs to have dimensionality d=" + (dim - 1) + ", read: " + interval.getDimensionality());
}
@@ -224,9 +188,9 @@ public class ParameterizationFunction extends DoubleVector implements TextWritea
alpha_extreme_c[i] = centroid[i];
}
- double intervalLength = interval.getMax(n + 1) - interval.getMin(n + 1);
- alpha_extreme_l[n] = Math.random() * intervalLength + interval.getMin(n + 1);
- alpha_extreme_r[n] = Math.random() * intervalLength + interval.getMin(n + 1);
+ double intervalLength = interval.getMax(n) - interval.getMin(n);
+ alpha_extreme_l[n] = Math.random() * intervalLength + interval.getMin(n);
+ alpha_extreme_r[n] = Math.random() * intervalLength + interval.getMin(n);
double f_c = function(alpha_extreme_c);
double f_l = function(alpha_extreme_l);
@@ -258,7 +222,7 @@ public class ParameterizationFunction extends DoubleVector implements TextWritea
return ExtremumType.CONSTANT;
}
- throw new IllegalArgumentException("Houston, we have a problem!\n" + this + "\n" + "f_l " + f_l + "\n" + "f_c " + f_c + "\n" + "f_r " + f_r + "\n" + "p " + getColumnVector() + "\n" + "alpha " + FormatUtil.format(alpha_extreme_c) + "\n" + "alpha_l " + FormatUtil.format(alpha_extreme_l) + "\n" + "alpha_r " + FormatUtil.format(alpha_extreme_r) + "\n" + "n " + n);
+ throw new IllegalArgumentException("Houston, we have a problem!\n" + this + "\n" + "f_l " + f_l + "\n" + "f_c " + f_c + "\n" + "f_r " + f_r + "\n" + "p " + vec.getColumnVector() + "\n" + "alpha " + FormatUtil.format(alpha_extreme_c) + "\n" + "alpha_l " + FormatUtil.format(alpha_extreme_l) + "\n" + "alpha_r " + FormatUtil.format(alpha_extreme_r) + "\n" + "n " + n);
// + "box min " + FormatUtil.format(interval.getMin()) + "\n"
// + "box max " + FormatUtil.format(interval.getMax()) + "\n"
}
@@ -275,8 +239,8 @@ public class ParameterizationFunction extends DoubleVector implements TextWritea
*/
private double determineAlphaMin(int n, double[] alpha_min, HyperBoundingBox interval) {
double alpha_n = extremum_alpha_n(n, alpha_min);
- double lower = interval.getMin(n + 1);
- double upper = interval.getMax(n + 1);
+ double lower = interval.getMin(n);
+ double upper = interval.getMax(n);
double[] alpha_extreme = new double[alpha_min.length];
System.arraycopy(alpha_min, n, alpha_extreme, n, alpha_extreme.length - n);
@@ -338,8 +302,8 @@ public class ParameterizationFunction extends DoubleVector implements TextWritea
*/
private double determineAlphaMax(int n, double[] alpha_max, HyperBoundingBox interval) {
double alpha_n = extremum_alpha_n(n, alpha_max);
- double lower = interval.getMin(n + 1);
- double upper = interval.getMax(n + 1);
+ double lower = interval.getMin(n);
+ double upper = interval.getMax(n);
double[] alpha_extreme = new double[alpha_max.length];
System.arraycopy(alpha_max, n, alpha_extreme, n, alpha_extreme.length - n);
@@ -434,17 +398,17 @@ public class ParameterizationFunction extends DoubleVector implements TextWritea
* @return a string representation of the object.
*/
public String toString(int offset) {
- StringBuffer result = new StringBuffer();
- for(int d = 0; d < getDimensionality(); d++) {
+ StringBuilder result = new StringBuilder();
+ for(int d = 0; d < vec.getDimensionality(); d++) {
if(d != 0) {
result.append(" + \n").append(FormatUtil.whitespace(offset));
}
- result.append(FormatUtil.format(doubleValue(d + 1)));
+ result.append(FormatUtil.format(vec.doubleValue(d)));
for(int j = 0; j < d; j++) {
- result.append(" * sin(a_").append(j + 1).append(")");
+ result.append(" * sin(a_").append(j + 1).append(')');
}
- if(d != getDimensionality() - 1) {
- result.append(" * cos(a_").append(d + 1).append(")");
+ if(d != vec.getDimensionality() - 1) {
+ result.append(" * cos(a_").append(d + 1).append(')');
}
}
return result.toString();
@@ -472,11 +436,11 @@ public class ParameterizationFunction extends DoubleVector implements TextWritea
* Determines the global extremum of this parameterization function.
*/
private void determineGlobalExtremum() {
- alphaExtremum = new double[getDimensionality() - 1];
+ alphaExtremum = new double[vec.getDimensionality() - 1];
for(int n = alphaExtremum.length - 1; n >= 0; n--) {
alphaExtremum[n] = extremum_alpha_n(n, alphaExtremum);
if(Double.isNaN(alphaExtremum[n])) {
- throw new IllegalStateException("Houston, we have a problem!" + "\n" + this + "\n" + this.getColumnVector() + "\n" + FormatUtil.format(alphaExtremum));
+ throw new IllegalStateException("Houston, we have a problem!" + "\n" + this + "\n" + vec.getColumnVector() + "\n" + FormatUtil.format(alphaExtremum));
}
}
@@ -487,7 +451,7 @@ public class ParameterizationFunction extends DoubleVector implements TextWritea
* Determines the type of the global extremum.
*/
private void determineGlobalExtremumType() {
- double f = function(alphaExtremum);
+ final double f = function(alphaExtremum);
// create random alpha values
double[] alpha_1 = new double[alphaExtremum.length];
@@ -513,7 +477,6 @@ public class ParameterizationFunction extends DoubleVector implements TextWritea
else {
throw new IllegalStateException("Houston, we have a problem:" + "\n" + this + "\nextremum at " + FormatUtil.format(alphaExtremum) + "\nf " + f + "\nf1 " + f1 + "\nf2 " + f2);
}
-
}
/**
@@ -526,16 +489,16 @@ public class ParameterizationFunction extends DoubleVector implements TextWritea
*/
private double extremum_alpha_n(int n, double[] alpha) {
// arctan(infinity) = PI/2
- if(doubleValue(n + 1) == 0) {
- return 0.5 * Math.PI;
+ if(vec.doubleValue(n) == 0) {
+ return MathUtil.HALFPI;
}
double tan = 0;
- for(int j = n + 1; j < getDimensionality(); j++) {
- double alpha_j = j == getDimensionality() - 1 ? 0 : alpha[j];
- tan += doubleValue(j + 1) * sinusProduct(n + 1, j, alpha) * Math.cos(alpha_j);
+ for(int j = n + 1; j < vec.getDimensionality(); j++) {
+ double alpha_j = j == vec.getDimensionality() - 1 ? 0 : alpha[j];
+ tan += vec.doubleValue(j) * sinusProduct(n + 1, j, alpha) * Math.cos(alpha_j);
}
- tan /= doubleValue(n + 1);
+ tan /= vec.doubleValue(n);
// if (debug) {
// debugFiner("tan alpha_" + (n + 1) + " = " + tan);
@@ -547,51 +510,21 @@ public class ParameterizationFunction extends DoubleVector implements TextWritea
return alpha_n;
}
- @Override
- public void writeToText(TextWriterStream out, String label) {
- String pre = "";
- if(label != null) {
- pre = label + "=";
- }
- out.inlinePrintNoQuotes(pre + super.toString());
- }
-
- @Override
- public ParameterizationFunction newNumberVector(double[] values) {
- return new ParameterizationFunction(values);
- }
-
- @Override
- public <A> ParameterizationFunction newFeatureVector(A array, ArrayAdapter<Double, A> adapter) {
- final int dim = adapter.size(array);
- double[] values = new double[dim];
- for(int i = 0; i < dim; i++) {
- values[i] = adapter.get(array, i);
- }
- return new ParameterizationFunction(values);
- }
-
- @Override
- public <A> ParameterizationFunction newNumberVector(A array, NumberArrayAdapter<?, A> adapter) {
- final int dim = adapter.size(array);
- double[] values = new double[dim];
- for(int i = 0; i < dim; i++) {
- values[i] = adapter.getDouble(array, i);
- }
- return new ParameterizationFunction(values);
+ /**
+ * Get the actual vector used.
+ *
+ * @return Vector, for projection
+ */
+ public Vector getColumnVector() {
+ return vec.getColumnVector();
}
/**
- * Parameterization class
- *
- * @author Erich Schubert
+ * Get the vector dimensionality.
*
- * @apiviz.exclude
+ * @return Vector dimensionality
*/
- public static class Parameterizer extends AbstractParameterizer {
- @Override
- protected ParameterizationFunction makeInstance() {
- return STATIC;
- }
+ public int getDimensionality() {
+ return vec.getDimensionality();
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/gdbscan/CorePredicate.java b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/gdbscan/CorePredicate.java
index e75a89dc..a4440a29 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/gdbscan/CorePredicate.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/gdbscan/CorePredicate.java
@@ -39,11 +39,6 @@ import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
*/
public interface CorePredicate {
/**
- * Constant for the generic type {@code List<? extends DistanceResultPair<?>>}
- */
- public static final String NEIGHBOR_LIST = "neighborhood-list";
-
- /**
* Instantiate for a database.
*
* @param database Database to instantiate for
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/gdbscan/EpsilonNeighborPredicate.java b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/gdbscan/EpsilonNeighborPredicate.java
index cb24e8f1..2b946f1c 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/gdbscan/EpsilonNeighborPredicate.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/gdbscan/EpsilonNeighborPredicate.java
@@ -23,8 +23,6 @@ package de.lmu.ifi.dbs.elki.algorithm.clustering.gdbscan;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.util.List;
-
import de.lmu.ifi.dbs.elki.algorithm.AbstractDistanceBasedAlgorithm;
import de.lmu.ifi.dbs.elki.data.type.SimpleTypeInformation;
import de.lmu.ifi.dbs.elki.data.type.TypeInformation;
@@ -32,18 +30,15 @@ import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
import de.lmu.ifi.dbs.elki.database.Database;
import de.lmu.ifi.dbs.elki.database.QueryUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
-import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.ids.ModifiableDBIDs;
-import de.lmu.ifi.dbs.elki.database.query.DistanceDBIDResult;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.database.query.range.RangeQuery;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
import de.lmu.ifi.dbs.elki.distance.distancefunction.EuclideanDistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
-import de.lmu.ifi.dbs.elki.utilities.exceptions.AbortException;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.DistanceParameter;
@@ -63,6 +58,8 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ObjectParameter;
*
* @author Erich Schubert
*
+ * @apiviz.has Instance
+ *
* @param <D> Distance type
*/
@Reference(authors = "M. Ester, H.-P. Kriegel, J. Sander, and X. Xu", title = "A Density-Based Algorithm for Discovering Clusters in Large Spatial Databases with Noise", booktitle = "Proc. 2nd Int. Conf. on Knowledge Discovery and Data Mining (KDD '96), Portland, OR, 1996", url = "http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.71.1980")
@@ -91,18 +88,10 @@ public class EpsilonNeighborPredicate<O, D extends Distance<D>> implements Neigh
@SuppressWarnings("unchecked")
@Override
- public <T> Instance<T> instantiate(Database database, SimpleTypeInformation<?> type) {
- if(TypeUtil.DBIDS.isAssignableFromType(type)) {
- DistanceQuery<O, D> dq = QueryUtil.getDistanceQuery(database, distFunc);
- RangeQuery<O, D> rq = database.getRangeQuery(dq);
- return (Instance<T>) new DBIDInstance<D>(epsilon, rq, dq.getRelation().getDBIDs());
- }
- if(TypeUtil.NEIGHBORLIST.isAssignableFromType(type)) {
- DistanceQuery<O, D> dq = QueryUtil.getDistanceQuery(database, distFunc);
- RangeQuery<O, D> rq = database.getRangeQuery(dq);
- return (Instance<T>) new NeighborListInstance<D>(epsilon, rq, dq.getRelation().getDBIDs());
- }
- throw new AbortException("Incompatible predicate types");
+ public <T> NeighborPredicate.Instance<T> instantiate(Database database, SimpleTypeInformation<?> type) {
+ DistanceQuery<O, D> dq = QueryUtil.getDistanceQuery(database, distFunc);
+ RangeQuery<O, D> rq = database.getRangeQuery(dq);
+ return (NeighborPredicate.Instance<T>) new Instance<D>(epsilon, rq, dq.getRelation().getDBIDs());
}
@Override
@@ -120,7 +109,7 @@ public class EpsilonNeighborPredicate<O, D extends Distance<D>> implements Neigh
*
* @author Erich Schubert
*/
- public static class DBIDInstance<D extends Distance<D>> implements NeighborPredicate.Instance<DBIDs> {
+ public static class Instance<D extends Distance<D>> implements NeighborPredicate.Instance<DistanceDBIDResult<D>> {
/**
* Range to query with
*/
@@ -143,64 +132,7 @@ public class EpsilonNeighborPredicate<O, D extends Distance<D>> implements Neigh
* @param rq Range query to use
* @param ids DBIDs to process
*/
- public DBIDInstance(D epsilon, RangeQuery<?, D> rq, DBIDs ids) {
- super();
- this.epsilon = epsilon;
- this.rq = rq;
- this.ids = ids;
- }
-
- @Override
- public DBIDs getIDs() {
- return ids;
- }
-
- @Override
- public DBIDs getNeighbors(DBIDRef reference) {
- List<DistanceResultPair<D>> res = rq.getRangeForDBID(reference, epsilon);
- // Throw away the actual distance values ...
- ModifiableDBIDs neighbors = DBIDUtil.newHashSet(res.size());
- for(DistanceResultPair<D> dr : res) {
- neighbors.add(dr);
- }
- return neighbors;
- }
-
- @Override
- public void addDBIDs(ModifiableDBIDs ids, DBIDs neighbors) {
- ids.addDBIDs(neighbors);
- }
- }
-
- /**
- * Instance for a particular data set.
- *
- * @author Erich Schubert
- */
- public static class NeighborListInstance<D extends Distance<D>> implements NeighborPredicate.Instance<DistanceDBIDResult<D>> {
- /**
- * Range to query with
- */
- D epsilon;
-
- /**
- * Range query to use on the database.
- */
- RangeQuery<?, D> rq;
-
- /**
- * DBIDs to process
- */
- DBIDs ids;
-
- /**
- * Constructor.
- *
- * @param epsilon Epsilon
- * @param rq Range query to use
- * @param ids DBIDs to process
- */
- public NeighborListInstance(D epsilon, RangeQuery<?, D> rq, DBIDs ids) {
+ public Instance(D epsilon, RangeQuery<?, D> rq, DBIDs ids) {
super();
this.epsilon = epsilon;
this.rq = rq;
@@ -219,9 +151,7 @@ public class EpsilonNeighborPredicate<O, D extends Distance<D>> implements Neigh
@Override
public void addDBIDs(ModifiableDBIDs ids, DistanceDBIDResult<D> neighbors) {
- for(DistanceResultPair<D> neighbor : neighbors) {
- ids.add(neighbor);
- }
+ ids.addDBIDs(neighbors);
}
}
@@ -265,4 +195,4 @@ public class EpsilonNeighborPredicate<O, D extends Distance<D>> implements Neigh
return new EpsilonNeighborPredicate<O, D>(epsilon, distfun);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/gdbscan/GeneralizedDBSCAN.java b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/gdbscan/GeneralizedDBSCAN.java
index 2e1c2093..ef1cb0dc 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/gdbscan/GeneralizedDBSCAN.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/gdbscan/GeneralizedDBSCAN.java
@@ -67,15 +67,19 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ObjectParameter;
*
* @author Erich Schubert
* @author Arthur Zimek
+ *
+ * @apiviz.landmark
*
* @apiviz.has Instance
+ * @apiviz.composedOf CorePredicate
+ * @apiviz.composedOf NeighborPredicate
*/
@Reference(authors = "Jörg Sander, Martin Ester, Hans-Peter Kriegel, Xiaowei Xu", title = "Density-Based Clustering in Spatial Databases: The Algorithm GDBSCAN and Its Applications", booktitle = "Data Mining and Knowledge Discovery", url = "http://dx.doi.org/10.1023/A:1009745219419")
public class GeneralizedDBSCAN extends AbstractAlgorithm<Clustering<Model>> implements ClusteringAlgorithm<Clustering<Model>> {
/**
* Get a logger for this algorithm
*/
- final static Logging logger = Logging.getLogger(GeneralizedDBSCAN.class);
+ private static final Logging LOG = Logging.getLogger(GeneralizedDBSCAN.class);
/**
* The neighborhood predicate factory.
@@ -116,16 +120,34 @@ public class GeneralizedDBSCAN extends AbstractAlgorithm<Clustering<Model>> impl
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
* Instance for a particular data set.
*
* @author Erich Schubert
+ *
+ * @apiviz.composedOf CorePredicate.Instance
+ * @apiviz.composedOf NeighborPredicate.Instance
*/
public class Instance<T> {
/**
+ * Unprocessed IDs
+ */
+ private static final int UNPROCESSED = -2;
+
+ /**
+ * Noise IDs
+ */
+ private static final int NOISE = -1;
+
+ /**
+ * Noise IDs
+ */
+ private static final int FIRST_CLUSTER = 0;
+
+ /**
* The neighborhood predicate
*/
final NeighborPredicate.Instance<T> npred;
@@ -148,30 +170,29 @@ public class GeneralizedDBSCAN extends AbstractAlgorithm<Clustering<Model>> impl
}
/**
- * Run the actual DBSCAN algorithm.
+ * Run the actual GDBSCAN algorithm.
*
* @return Clustering result
*/
public Clustering<Model> run() {
final DBIDs ids = npred.getIDs();
// Setup progress logging
- final FiniteProgress progress = logger.isVerbose() ? new FiniteProgress("Clustering", ids.size(), logger) : null;
- final IndefiniteProgress clusprogress = logger.isVerbose() ? new IndefiniteProgress("Clusters", logger) : null;
+ final FiniteProgress progress = LOG.isVerbose() ? new FiniteProgress("Clustering", ids.size(), LOG) : null;
+ final IndefiniteProgress clusprogress = LOG.isVerbose() ? new IndefiniteProgress("Clusters", LOG) : null;
// (Temporary) store the cluster ID assigned.
- final WritableIntegerDataStore clusterids = DataStoreUtil.makeIntegerStorage(ids, DataStoreFactory.HINT_TEMP, -2);
+ final WritableIntegerDataStore clusterids = DataStoreUtil.makeIntegerStorage(ids, DataStoreFactory.HINT_TEMP, UNPROCESSED);
// Note: these are not exact!
final TIntArrayList clustersizes = new TIntArrayList();
// Implementation Note: using Integer objects should result in
// reduced memory use in the HashMap!
- final int noiseid = -1;
- int clusterid = 0;
+ int clusterid = FIRST_CLUSTER;
int clustersize = 0;
int noisesize = 0;
// Iterate over all objects in the database.
for(DBIDIter id = ids.iter(); id.valid(); id.advance()) {
// Skip already processed ids.
- if(clusterids.intValue(id) > -2) {
+ if(clusterids.intValue(id) != UNPROCESSED) {
continue;
}
// Evaluate Neighborhood predicate
@@ -185,25 +206,25 @@ public class GeneralizedDBSCAN extends AbstractAlgorithm<Clustering<Model>> impl
clustersize = 0;
clusterid += 1;
if(clusprogress != null) {
- clusprogress.setProcessed(clusterid, logger);
+ clusprogress.setProcessed(clusterid, LOG);
}
}
else {
// otherwise, it's a noise point
- clusterids.putInt(id, noiseid);
+ clusterids.putInt(id, NOISE);
noisesize += 1;
}
// We've completed this element
if(progress != null) {
- progress.incrementProcessed(logger);
+ progress.incrementProcessed(LOG);
}
}
// Finish progress logging.
if(progress != null) {
- progress.ensureCompleted(logger);
+ progress.ensureCompleted(LOG);
}
if(clusprogress != null) {
- clusprogress.setCompleted(logger);
+ clusprogress.setCompleted(LOG);
}
// Transform cluster ID mapping into a clustering result:
@@ -240,7 +261,7 @@ public class GeneralizedDBSCAN extends AbstractAlgorithm<Clustering<Model>> impl
* @param neighbors Neighbors acquired by initial getNeighbors call.
* @param progress Progress logging
*
- * @return cluster size;
+ * @return cluster size
*/
protected int setbasedExpandCluster(final int clusterid, final WritableIntegerDataStore clusterids, final T neighbors, final FiniteProgress progress) {
int clustersize = 0;
@@ -264,7 +285,7 @@ public class GeneralizedDBSCAN extends AbstractAlgorithm<Clustering<Model>> impl
npred.addDBIDs(activeSet, newneighbors);
}
if(progress != null) {
- progress.incrementProcessed(logger);
+ progress.incrementProcessed(LOG);
}
}
}
@@ -293,12 +314,12 @@ public class GeneralizedDBSCAN extends AbstractAlgorithm<Clustering<Model>> impl
/**
* Parameter for neighborhood predicate
*/
- public final static OptionID NEIGHBORHOODPRED_ID = OptionID.getOrCreateOptionID("gdbscan.neighborhood", "Neighborhood predicate for GDBSCAN");
+ public static final OptionID NEIGHBORHOODPRED_ID = new OptionID("gdbscan.neighborhood", "Neighborhood predicate for GDBSCAN");
/**
* Parameter for core predicate
*/
- public final static OptionID COREPRED_ID = OptionID.getOrCreateOptionID("gdbscan.core", "Core point predicate for GDBSCAN");
+ public static final OptionID COREPRED_ID = new OptionID("gdbscan.core", "Core point predicate for GDBSCAN");
@Override
protected void makeOptions(Parameterization config) {
@@ -320,4 +341,4 @@ public class GeneralizedDBSCAN extends AbstractAlgorithm<Clustering<Model>> impl
return new GeneralizedDBSCAN(npred, corepred);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/gdbscan/MinPtsCorePredicate.java b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/gdbscan/MinPtsCorePredicate.java
index b9852eca..47097f9b 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/gdbscan/MinPtsCorePredicate.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/gdbscan/MinPtsCorePredicate.java
@@ -23,16 +23,12 @@ package de.lmu.ifi.dbs.elki.algorithm.clustering.gdbscan;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.util.List;
-
import de.lmu.ifi.dbs.elki.data.type.SimpleTypeInformation;
import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
import de.lmu.ifi.dbs.elki.database.Database;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
-import de.lmu.ifi.dbs.elki.utilities.exceptions.AbortException;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
@@ -72,14 +68,8 @@ public class MinPtsCorePredicate implements CorePredicate {
@SuppressWarnings("unchecked")
@Override
- public <T> Instance<T> instantiate(Database database, SimpleTypeInformation<?> type) {
- if(TypeUtil.DBIDS.isAssignableFromType(type)) {
- return (Instance<T>) new DBIDsInstance(minpts);
- }
- if(TypeUtil.NEIGHBORLIST.isAssignableFromType(type)) {
- return (Instance<T>) new NeighborListInstance(minpts);
- }
- throw new AbortException("Incompatible predicate types");
+ public <T> CorePredicate.Instance<T> instantiate(Database database, SimpleTypeInformation<?> type) {
+ return (CorePredicate.Instance<T>) new Instance(minpts);
}
@Override
@@ -98,7 +88,7 @@ public class MinPtsCorePredicate implements CorePredicate {
*
* @author Erich Schubert
*/
- public static class DBIDsInstance implements CorePredicate.Instance<DBIDs> {
+ public static class Instance implements CorePredicate.Instance<DBIDs> {
/**
* The minpts parameter.
*/
@@ -109,7 +99,7 @@ public class MinPtsCorePredicate implements CorePredicate {
*
* @param minpts MinPts parameter
*/
- public DBIDsInstance(int minpts) {
+ public Instance(int minpts) {
super();
this.minpts = minpts;
}
@@ -121,33 +111,6 @@ public class MinPtsCorePredicate implements CorePredicate {
}
/**
- * Instance for a particular data set.
- *
- * @author Erich Schubert
- */
- public static class NeighborListInstance implements CorePredicate.Instance<List<? extends DistanceResultPair<?>>> {
- /**
- * The minpts parameter.
- */
- int minpts;
-
- /**
- * Constructor for this predicate.
- *
- * @param minpts MinPts parameter
- */
- public NeighborListInstance(int minpts) {
- super();
- this.minpts = minpts;
- }
-
- @Override
- public boolean isCorePoint(DBIDRef point, List<? extends DistanceResultPair<?>> neighbors) {
- return neighbors.size() >= minpts;
- }
- }
-
- /**
* Parameterization class
*
* @author Erich Schubert
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/gdbscan/NeighborPredicate.java b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/gdbscan/NeighborPredicate.java
index 4f9eca27..ed927696 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/gdbscan/NeighborPredicate.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/gdbscan/NeighborPredicate.java
@@ -91,4 +91,4 @@ public interface NeighborPredicate {
*/
public void addDBIDs(ModifiableDBIDs ids, T neighbors);
}
-}
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/AbstractKMeans.java b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/AbstractKMeans.java
index 92862909..47855aad 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/AbstractKMeans.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/AbstractKMeans.java
@@ -27,10 +27,12 @@ import java.util.ArrayList;
import java.util.List;
import de.lmu.ifi.dbs.elki.algorithm.AbstractPrimitiveDistanceBasedAlgorithm;
+import de.lmu.ifi.dbs.elki.algorithm.clustering.ClusteringAlgorithm;
import de.lmu.ifi.dbs.elki.data.Clustering;
import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.data.VectorUtil.SortDBIDsBySingleDimension;
import de.lmu.ifi.dbs.elki.data.model.MeanModel;
+import de.lmu.ifi.dbs.elki.data.type.CombinedTypeInformation;
import de.lmu.ifi.dbs.elki.data.type.TypeInformation;
import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
import de.lmu.ifi.dbs.elki.database.ids.ArrayModifiableDBIDs;
@@ -50,12 +52,14 @@ import de.lmu.ifi.dbs.elki.utilities.datastructures.QuickSelect;
*
* @author Erich Schubert
*
+ * @apiviz.has MeanModel
* @apiviz.composedOf KMeansInitialization
*
* @param <V> Vector type
* @param <D> Distance type
+ * @param <M> Cluster model type
*/
-public abstract class AbstractKMeans<V extends NumberVector<V, ?>, D extends Distance<D>> extends AbstractPrimitiveDistanceBasedAlgorithm<NumberVector<?, ?>, D, Clustering<MeanModel<V>>> implements KMeans {
+public abstract class AbstractKMeans<V extends NumberVector<?>, D extends Distance<D>, M extends MeanModel<V>> extends AbstractPrimitiveDistanceBasedAlgorithm<NumberVector<?>, D, Clustering<M>> implements KMeans, ClusteringAlgorithm<Clustering<M>> {
/**
* Holds the value of {@link #K_ID}.
*/
@@ -79,7 +83,7 @@ public abstract class AbstractKMeans<V extends NumberVector<V, ?>, D extends Dis
* @param maxiter Maxiter parameter
* @param initializer Function to generate the initial means
*/
- public AbstractKMeans(PrimitiveDistanceFunction<NumberVector<?, ?>, D> distanceFunction, int k, int maxiter, KMeansInitialization<V> initializer) {
+ public AbstractKMeans(PrimitiveDistanceFunction<? super NumberVector<?>, D> distanceFunction, int k, int maxiter, KMeansInitialization<V> initializer) {
super(distanceFunction);
this.k = k;
this.maxiter = maxiter;
@@ -95,12 +99,12 @@ public abstract class AbstractKMeans<V extends NumberVector<V, ?>, D extends Dis
* @param clusters cluster assignment
* @return true when the object was reassigned
*/
- protected boolean assignToNearestCluster(Relation<V> relation, List<? extends NumberVector<?, ?>> means, List<? extends ModifiableDBIDs> clusters) {
+ protected boolean assignToNearestCluster(Relation<V> relation, List<? extends NumberVector<?>> means, List<? extends ModifiableDBIDs> clusters) {
boolean changed = false;
if(getDistanceFunction() instanceof PrimitiveDoubleDistanceFunction) {
@SuppressWarnings("unchecked")
- final PrimitiveDoubleDistanceFunction<? super NumberVector<?, ?>> df = (PrimitiveDoubleDistanceFunction<? super NumberVector<?, ?>>) getDistanceFunction();
+ final PrimitiveDoubleDistanceFunction<? super NumberVector<?>> df = (PrimitiveDoubleDistanceFunction<? super NumberVector<?>>) getDistanceFunction();
for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
double mindist = Double.POSITIVE_INFINITY;
V fv = relation.get(iditer);
@@ -127,7 +131,7 @@ public abstract class AbstractKMeans<V extends NumberVector<V, ?>, D extends Dis
}
}
else {
- final PrimitiveDistanceFunction<? super NumberVector<?, ?>, D> df = getDistanceFunction();
+ final PrimitiveDistanceFunction<? super NumberVector<?>, D> df = getDistanceFunction();
for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
D mindist = df.getDistanceFactory().infiniteDistance();
V fv = relation.get(iditer);
@@ -158,7 +162,7 @@ public abstract class AbstractKMeans<V extends NumberVector<V, ?>, D extends Dis
@Override
public TypeInformation[] getInputTypeRestriction() {
- return TypeUtil.array(TypeUtil.NUMBER_VECTOR_FIELD);
+ return TypeUtil.array(new CombinedTypeInformation(TypeUtil.NUMBER_VECTOR_FIELD, getDistanceFunction().getInputTypeRestriction()));
}
/**
@@ -169,7 +173,7 @@ public abstract class AbstractKMeans<V extends NumberVector<V, ?>, D extends Dis
* @param database the database containing the vectors
* @return the mean vectors of the given clusters in the given database
*/
- protected List<Vector> means(List<? extends ModifiableDBIDs> clusters, List<? extends NumberVector<?, ?>> means, Relation<V> database) {
+ protected List<Vector> means(List<? extends ModifiableDBIDs> clusters, List<? extends NumberVector<?>> means, Relation<V> database) {
List<Vector> newMeans = new ArrayList<Vector>(k);
for(int i = 0; i < k; i++) {
ModifiableDBIDs list = clusters.get(i);
@@ -200,30 +204,30 @@ public abstract class AbstractKMeans<V extends NumberVector<V, ?>, D extends Dis
* @param database the database containing the vectors
* @return the mean vectors of the given clusters in the given database
*/
- protected List<NumberVector<?, ?>> medians(List<? extends ModifiableDBIDs> clusters, List<? extends NumberVector<?, ?>> medians, Relation<V> database) {
+ protected List<NumberVector<?>> medians(List<? extends ModifiableDBIDs> clusters, List<? extends NumberVector<?>> medians, Relation<V> database) {
final int dim = medians.get(0).getDimensionality();
final SortDBIDsBySingleDimension sorter = new SortDBIDsBySingleDimension(database);
- List<NumberVector<?, ?>> newMedians = new ArrayList<NumberVector<?, ?>>(k);
+ List<NumberVector<?>> newMedians = new ArrayList<NumberVector<?>>(k);
for(int i = 0; i < k; i++) {
ArrayModifiableDBIDs list = DBIDUtil.newArray(clusters.get(i));
if(list.size() > 0) {
Vector mean = new Vector(dim);
for(int d = 0; d < dim; d++) {
- sorter.setDimension(d + 1);
+ sorter.setDimension(d);
DBID id = QuickSelect.median(list, sorter);
- mean.set(d, database.get(id).doubleValue(d + 1));
+ mean.set(d, database.get(id).doubleValue(d));
}
newMedians.add(mean);
}
else {
- newMedians.add((NumberVector<?, ?>) medians.get(i));
+ newMedians.add((NumberVector<?>) medians.get(i));
}
}
return newMedians;
}
/**
- * Compute an incremental update for the mean
+ * Compute an incremental update for the mean.
*
* @param mean Mean to update
* @param vec Object vector
@@ -255,7 +259,7 @@ public abstract class AbstractKMeans<V extends NumberVector<V, ?>, D extends Dis
if(getDistanceFunction() instanceof PrimitiveDoubleDistanceFunction) {
// Raw distance function
@SuppressWarnings("unchecked")
- final PrimitiveDoubleDistanceFunction<? super NumberVector<?, ?>> df = (PrimitiveDoubleDistanceFunction<? super NumberVector<?, ?>>) getDistanceFunction();
+ final PrimitiveDoubleDistanceFunction<? super NumberVector<?>> df = (PrimitiveDoubleDistanceFunction<? super NumberVector<?>>) getDistanceFunction();
// Incremental update
for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
@@ -287,7 +291,7 @@ public abstract class AbstractKMeans<V extends NumberVector<V, ?>, D extends Dis
}
else {
// Raw distance function
- final PrimitiveDistanceFunction<? super NumberVector<?, ?>, D> df = getDistanceFunction();
+ final PrimitiveDistanceFunction<? super NumberVector<?>, D> df = getDistanceFunction();
// Incremental update
for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
@@ -319,4 +323,4 @@ public abstract class AbstractKMeans<V extends NumberVector<V, ?>, D extends Dis
}
return changed;
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/AbstractKMeansInitialization.java b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/AbstractKMeansInitialization.java
index a8effecd..3a69c806 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/AbstractKMeansInitialization.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/AbstractKMeansInitialization.java
@@ -22,9 +22,10 @@ package de.lmu.ifi.dbs.elki.algorithm.clustering.kmeans;
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+import de.lmu.ifi.dbs.elki.utilities.RandomFactory;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.LongParameter;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.RandomParameter;
/**
* Abstract base class for common k-means initializations.
@@ -35,17 +36,17 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.LongParameter;
*/
public abstract class AbstractKMeansInitialization<V> implements KMeansInitialization<V> {
/**
- * Holds the value of {@link KMeans#SEED_ID}.
+ * Random number generator
*/
- protected Long seed;
+ protected RandomFactory rnd;
/**
* Constructor.
*
- * @param seed Random seed.
+ * @param rnd Random number generator.
*/
- public AbstractKMeansInitialization(Long seed) {
- this.seed = seed;
+ public AbstractKMeansInitialization(RandomFactory rnd) {
+ this.rnd = rnd;
}
/**
@@ -56,14 +57,17 @@ public abstract class AbstractKMeansInitialization<V> implements KMeansInitializ
* @apiviz.exclude
*/
public abstract static class Parameterizer<V> extends AbstractParameterizer {
- protected Long seed;
+ /**
+ * Random generator
+ */
+ protected RandomFactory rnd;
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- LongParameter seedP = new LongParameter(KMeans.SEED_ID, true);
- if(config.grab(seedP)) {
- seed = seedP.getValue();
+ RandomParameter rndP = new RandomParameter(KMeans.SEED_ID);
+ if(config.grab(rndP)) {
+ rnd = rndP.getValue();
}
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/FirstKInitialMeans.java b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/FirstKInitialMeans.java
index 7a7f2867..1e51f4d6 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/FirstKInitialMeans.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/FirstKInitialMeans.java
@@ -77,7 +77,7 @@ public class FirstKInitialMeans<V> implements KMeansInitialization<V>, KMedoidsI
*
* @apiviz.exclude
*/
- public static class Parameterizer<V extends NumberVector<V, ?>> extends AbstractParameterizer {
+ public static class Parameterizer<V extends NumberVector<?>> extends AbstractParameterizer {
@Override
protected FirstKInitialMeans<V> makeInstance() {
return new FirstKInitialMeans<V>();
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/KMeans.java b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/KMeans.java
index 37171d4a..68fc4e48 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/KMeans.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/KMeans.java
@@ -34,22 +34,22 @@ public interface KMeans {
/**
* Parameter to specify the initialization method
*/
- public static final OptionID INIT_ID = OptionID.getOrCreateOptionID("kmeans.initialization", "Method to choose the initial means.");
+ public static final OptionID INIT_ID = new OptionID("kmeans.initialization", "Method to choose the initial means.");
/**
* Parameter to specify the number of clusters to find, must be an integer
* greater than 0.
*/
- public static final OptionID K_ID = OptionID.getOrCreateOptionID("kmeans.k", "The number of clusters to find.");
+ public static final OptionID K_ID = new OptionID("kmeans.k", "The number of clusters to find.");
/**
* Parameter to specify the number of clusters to find, must be an integer
* greater or equal to 0, where 0 means no limit.
*/
- public static final OptionID MAXITER_ID = OptionID.getOrCreateOptionID("kmeans.maxiter", "The maximum number of iterations to do. 0 means no limit.");
+ public static final OptionID MAXITER_ID = new OptionID("kmeans.maxiter", "The maximum number of iterations to do. 0 means no limit.");
/**
* Parameter to specify the random generator seed.
*/
- public static final OptionID SEED_ID = OptionID.getOrCreateOptionID("kmeans.seed", "The random number generator seed.");
+ public static final OptionID SEED_ID = new OptionID("kmeans.seed", "The random number generator seed.");
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/KMeansInitialization.java b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/KMeansInitialization.java
index 9e5d69f0..54b3a2ce 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/KMeansInitialization.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/KMeansInitialization.java
@@ -31,6 +31,8 @@ import de.lmu.ifi.dbs.elki.distance.distancefunction.PrimitiveDistanceFunction;
* Interface for initializing K-Means
*
* @author Erich Schubert
+ *
+ * @apiviz.landmark
*
* @param <V> Object type
*/
@@ -44,4 +46,4 @@ public interface KMeansInitialization<V> {
* @return List of chosen means for k-means
*/
public abstract List<V> chooseInitialMeans(Relation<V> relation, int k, PrimitiveDistanceFunction<? super V, ?> distanceFunction);
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/KMeansLloyd.java b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/KMeansLloyd.java
index b1b40632..f43c2277 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/KMeansLloyd.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/KMeansLloyd.java
@@ -27,19 +27,20 @@ import java.util.ArrayList;
import java.util.List;
import de.lmu.ifi.dbs.elki.algorithm.AbstractPrimitiveDistanceBasedAlgorithm;
-import de.lmu.ifi.dbs.elki.algorithm.clustering.ClusteringAlgorithm;
import de.lmu.ifi.dbs.elki.data.Cluster;
import de.lmu.ifi.dbs.elki.data.Clustering;
import de.lmu.ifi.dbs.elki.data.NumberVector;
-import de.lmu.ifi.dbs.elki.data.model.MeanModel;
+import de.lmu.ifi.dbs.elki.data.model.KMeansModel;
import de.lmu.ifi.dbs.elki.database.Database;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.ModifiableDBIDs;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
+import de.lmu.ifi.dbs.elki.distance.distancefunction.EuclideanDistanceFunction;
import de.lmu.ifi.dbs.elki.distance.distancefunction.PrimitiveDistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distancefunction.SquaredEuclideanDistanceFunction;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
import de.lmu.ifi.dbs.elki.logging.Logging;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.utilities.documentation.Description;
import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
import de.lmu.ifi.dbs.elki.utilities.documentation.Title;
@@ -62,7 +63,8 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ObjectParameter;
*
* @author Arthur Zimek
*
- * @apiviz.has MeanModel
+ * @apiviz.landmark
+ * @apiviz.has KMeansModel
*
* @param <V> vector datatype
* @param <D> distance value type
@@ -70,11 +72,11 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ObjectParameter;
@Title("K-Means")
@Description("Finds a partitioning into k clusters.")
@Reference(authors = "S. Lloyd", title = "Least squares quantization in PCM", booktitle = "IEEE Transactions on Information Theory 28 (2): 129–137.", url = "http://dx.doi.org/10.1109/TIT.1982.1056489")
-public class KMeansLloyd<V extends NumberVector<V, ?>, D extends Distance<D>> extends AbstractKMeans<V, D> implements ClusteringAlgorithm<Clustering<MeanModel<V>>> {
+public class KMeansLloyd<V extends NumberVector<?>, D extends Distance<D>> extends AbstractKMeans<V, D, KMeansModel<V>> {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(KMeansLloyd.class);
+ private static final Logging LOG = Logging.getLogger(KMeansLloyd.class);
/**
* Constructor.
@@ -82,55 +84,56 @@ public class KMeansLloyd<V extends NumberVector<V, ?>, D extends Distance<D>> ex
* @param distanceFunction distance function
* @param k k parameter
* @param maxiter Maxiter parameter
+ * @param initializer Initialization method
*/
- public KMeansLloyd(PrimitiveDistanceFunction<NumberVector<?, ?>, D> distanceFunction, int k, int maxiter, KMeansInitialization<V> initializer) {
+ public KMeansLloyd(PrimitiveDistanceFunction<NumberVector<?>, D> distanceFunction, int k, int maxiter, KMeansInitialization<V> initializer) {
super(distanceFunction, k, maxiter, initializer);
}
/**
- * Run k-means
+ * Run k-means.
*
* @param database Database
* @param relation relation to use
* @return result
*/
- public Clustering<MeanModel<V>> run(Database database, Relation<V> relation) {
- if(relation.size() <= 0) {
- return new Clustering<MeanModel<V>>("k-Means Clustering", "kmeans-clustering");
+ public Clustering<KMeansModel<V>> run(Database database, Relation<V> relation) {
+ if (relation.size() <= 0) {
+ return new Clustering<KMeansModel<V>>("k-Means Clustering", "kmeans-clustering");
}
// Choose initial means
- List<? extends NumberVector<?, ?>> means = initializer.chooseInitialMeans(relation, k, getDistanceFunction());
+ List<? extends NumberVector<?>> means = initializer.chooseInitialMeans(relation, k, getDistanceFunction());
// Setup cluster assignment store
List<ModifiableDBIDs> clusters = new ArrayList<ModifiableDBIDs>();
- for(int i = 0; i < k; i++) {
+ for (int i = 0; i < k; i++) {
clusters.add(DBIDUtil.newHashSet(relation.size() / k));
}
- for(int iteration = 0; maxiter <= 0 || iteration < maxiter; iteration++) {
- if(logger.isVerbose()) {
- logger.verbose("K-Means iteration " + (iteration + 1));
+ for (int iteration = 0; maxiter <= 0 || iteration < maxiter; iteration++) {
+ if (LOG.isVerbose()) {
+ LOG.verbose("K-Means iteration " + (iteration + 1));
}
boolean changed = assignToNearestCluster(relation, means, clusters);
// Stop if no cluster assignment changed.
- if(!changed) {
+ if (!changed) {
break;
}
// Recompute means.
means = means(clusters, means, relation);
}
// Wrap result
- final V factory = DatabaseUtil.assumeVectorField(relation).getFactory();
- Clustering<MeanModel<V>> result = new Clustering<MeanModel<V>>("k-Means Clustering", "kmeans-clustering");
- for(int i = 0; i < clusters.size(); i++) {
- MeanModel<V> model = new MeanModel<V>(factory.newNumberVector(means.get(i).getColumnVector().getArrayRef()));
- result.addCluster(new Cluster<MeanModel<V>>(clusters.get(i), model));
+ final NumberVector.Factory<V, ?> factory = RelationUtil.getNumberVectorFactory(relation);
+ Clustering<KMeansModel<V>> result = new Clustering<KMeansModel<V>>("k-Means Clustering", "kmeans-clustering");
+ for (int i = 0; i < clusters.size(); i++) {
+ KMeansModel<V> model = new KMeansModel<V>(factory.newNumberVector(means.get(i).getColumnVector().getArrayRef()));
+ result.addCluster(new Cluster<KMeansModel<V>>(clusters.get(i), model));
}
return result;
}
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -140,35 +143,53 @@ public class KMeansLloyd<V extends NumberVector<V, ?>, D extends Distance<D>> ex
*
* @apiviz.exclude
*/
- public static class Parameterizer<V extends NumberVector<V, ?>, D extends Distance<D>> extends AbstractPrimitiveDistanceBasedAlgorithm.Parameterizer<NumberVector<?, ?>, D> {
+ public static class Parameterizer<V extends NumberVector<?>, D extends Distance<D>> extends AbstractPrimitiveDistanceBasedAlgorithm.Parameterizer<NumberVector<?>, D> {
+ /**
+ * k Parameter.
+ */
protected int k;
+ /**
+ * Number of iterations.
+ */
protected int maxiter;
+ /**
+ * Initialization method.
+ */
protected KMeansInitialization<V> initializer;
@Override
protected void makeOptions(Parameterization config) {
- super.makeOptions(config);
- IntParameter kP = new IntParameter(K_ID, new GreaterConstraint(0));
- if(config.grab(kP)) {
+ ObjectParameter<PrimitiveDistanceFunction<NumberVector<?>, D>> distanceFunctionP = makeParameterDistanceFunction(SquaredEuclideanDistanceFunction.class, PrimitiveDistanceFunction.class);
+ if(config.grab(distanceFunctionP)) {
+ distanceFunction = distanceFunctionP.instantiateClass(config);
+ if (!(distanceFunction instanceof EuclideanDistanceFunction) && !(distanceFunction instanceof SquaredEuclideanDistanceFunction)) {
+ LOG.warning("k-means optimizes the sum of squares - it should be used with squared euclidean distance and may stop converging otherwise!");
+ }
+ }
+
+ IntParameter kP = new IntParameter(K_ID);
+ kP.addConstraint(new GreaterConstraint(0));
+ if (config.grab(kP)) {
k = kP.getValue();
}
ObjectParameter<KMeansInitialization<V>> initialP = new ObjectParameter<KMeansInitialization<V>>(INIT_ID, KMeansInitialization.class, RandomlyGeneratedInitialMeans.class);
- if(config.grab(initialP)) {
+ if (config.grab(initialP)) {
initializer = initialP.instantiateClass(config);
}
- IntParameter maxiterP = new IntParameter(MAXITER_ID, new GreaterEqualConstraint(0), 0);
- if(config.grab(maxiterP)) {
- maxiter = maxiterP.getValue();
+ IntParameter maxiterP = new IntParameter(MAXITER_ID, 0);
+ maxiterP.addConstraint(new GreaterEqualConstraint(0));
+ if (config.grab(maxiterP)) {
+ maxiter = maxiterP.intValue();
}
}
@Override
- protected AbstractKMeans<V, D> makeInstance() {
+ protected KMeansLloyd<V, D> makeInstance() {
return new KMeansLloyd<V, D>(distanceFunction, k, maxiter, initializer);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/KMeansMacQueen.java b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/KMeansMacQueen.java
index c729eb10..0cc7c363 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/KMeansMacQueen.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/KMeansMacQueen.java
@@ -27,21 +27,22 @@ import java.util.ArrayList;
import java.util.List;
import de.lmu.ifi.dbs.elki.algorithm.AbstractPrimitiveDistanceBasedAlgorithm;
-import de.lmu.ifi.dbs.elki.algorithm.clustering.ClusteringAlgorithm;
import de.lmu.ifi.dbs.elki.data.Cluster;
import de.lmu.ifi.dbs.elki.data.Clustering;
import de.lmu.ifi.dbs.elki.data.NumberVector;
-import de.lmu.ifi.dbs.elki.data.model.MeanModel;
+import de.lmu.ifi.dbs.elki.data.model.KMeansModel;
import de.lmu.ifi.dbs.elki.database.Database;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.ids.ModifiableDBIDs;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
+import de.lmu.ifi.dbs.elki.distance.distancefunction.EuclideanDistanceFunction;
import de.lmu.ifi.dbs.elki.distance.distancefunction.PrimitiveDistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distancefunction.SquaredEuclideanDistanceFunction;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.utilities.documentation.Description;
import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
import de.lmu.ifi.dbs.elki.utilities.documentation.Title;
@@ -62,8 +63,7 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ObjectParameter;
* </p>
*
* @author Erich Schubert
- *
- * @apiviz.has MeanModel
+ * @apiviz.has KMeansModel
*
* @param <V> vector type to use
* @param <D> distance function value type
@@ -71,11 +71,11 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ObjectParameter;
@Title("K-Means")
@Description("Finds a partitioning into k clusters.")
@Reference(authors = "J. MacQueen", title = "Some Methods for Classification and Analysis of Multivariate Observations", booktitle = "5th Berkeley Symp. Math. Statist. Prob., Vol. 1, 1967, pp 281-297", url = "http://projecteuclid.org/euclid.bsmsp/1200512992")
-public class KMeansMacQueen<V extends NumberVector<V, ?>, D extends Distance<D>> extends AbstractKMeans<V, D> implements ClusteringAlgorithm<Clustering<MeanModel<V>>> {
+public class KMeansMacQueen<V extends NumberVector<?>, D extends Distance<D>> extends AbstractKMeans<V, D, KMeansModel<V>> {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(KMeansMacQueen.class);
+ private static final Logging LOG = Logging.getLogger(KMeansMacQueen.class);
/**
* Constructor.
@@ -83,30 +83,31 @@ public class KMeansMacQueen<V extends NumberVector<V, ?>, D extends Distance<D>>
* @param distanceFunction distance function
* @param k k parameter
* @param maxiter Maxiter parameter
+ * @param initializer Initialization method
*/
- public KMeansMacQueen(PrimitiveDistanceFunction<NumberVector<?, ?>, D> distanceFunction, int k, int maxiter, KMeansInitialization<V> initializer) {
+ public KMeansMacQueen(PrimitiveDistanceFunction<NumberVector<?>, D> distanceFunction, int k, int maxiter, KMeansInitialization<V> initializer) {
super(distanceFunction, k, maxiter, initializer);
}
/**
- * Run k-means
+ * Run k-means.
*
* @param database Database
* @param relation relation to use
* @return Clustering result
*/
- public Clustering<MeanModel<V>> run(Database database, Relation<V> relation) {
- if(relation.size() <= 0) {
- return new Clustering<MeanModel<V>>("k-Means Clustering", "kmeans-clustering");
+ public Clustering<KMeansModel<V>> run(Database database, Relation<V> relation) {
+ if (relation.size() <= 0) {
+ return new Clustering<KMeansModel<V>>("k-Means Clustering", "kmeans-clustering");
}
// Choose initial means
List<Vector> means = new ArrayList<Vector>(k);
- for(NumberVector<?, ?> nv : initializer.chooseInitialMeans(relation, k, getDistanceFunction())) {
+ for (NumberVector<?> nv : initializer.chooseInitialMeans(relation, k, getDistanceFunction())) {
means.add(nv.getColumnVector());
}
// Initialize cluster and assign objects
List<ModifiableDBIDs> clusters = new ArrayList<ModifiableDBIDs>();
- for(int i = 0; i < k; i++) {
+ for (int i = 0; i < k; i++) {
clusters.add(DBIDUtil.newHashSet(relation.size() / k));
}
assignToNearestCluster(relation, means, clusters);
@@ -114,28 +115,28 @@ public class KMeansMacQueen<V extends NumberVector<V, ?>, D extends Distance<D>>
means = means(clusters, means, relation);
// Refine result
- for(int iteration = 0; maxiter <= 0 || iteration < maxiter; iteration++) {
- if(logger.isVerbose()) {
- logger.verbose("K-Means iteration " + (iteration + 1));
+ for (int iteration = 0; maxiter <= 0 || iteration < maxiter; iteration++) {
+ if (LOG.isVerbose()) {
+ LOG.verbose("K-Means iteration " + (iteration + 1));
}
boolean changed = macQueenIterate(relation, means, clusters);
- if(!changed) {
+ if (!changed) {
break;
}
}
- final V factory = DatabaseUtil.assumeVectorField(relation).getFactory();
- Clustering<MeanModel<V>> result = new Clustering<MeanModel<V>>("k-Means Clustering", "kmeans-clustering");
- for(int i = 0; i < clusters.size(); i++) {
+ final NumberVector.Factory<V, ?> factory = RelationUtil.getNumberVectorFactory(relation);
+ Clustering<KMeansModel<V>> result = new Clustering<KMeansModel<V>>("k-Means Clustering", "kmeans-clustering");
+ for (int i = 0; i < clusters.size(); i++) {
DBIDs ids = clusters.get(i);
- MeanModel<V> model = new MeanModel<V>(factory.newNumberVector(means.get(i).getArrayRef()));
- result.addCluster(new Cluster<MeanModel<V>>(ids, model));
+ KMeansModel<V> model = new KMeansModel<V>(factory.newNumberVector(means.get(i).getArrayRef()));
+ result.addCluster(new Cluster<KMeansModel<V>>(ids, model));
}
return result;
}
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -145,35 +146,53 @@ public class KMeansMacQueen<V extends NumberVector<V, ?>, D extends Distance<D>>
*
* @apiviz.exclude
*/
- public static class Parameterizer<V extends NumberVector<V, ?>, D extends Distance<D>> extends AbstractPrimitiveDistanceBasedAlgorithm.Parameterizer<NumberVector<?, ?>, D> {
+ public static class Parameterizer<V extends NumberVector<?>, D extends Distance<D>> extends AbstractPrimitiveDistanceBasedAlgorithm.Parameterizer<NumberVector<?>, D> {
+ /**
+ * k Parameter.
+ */
protected int k;
+ /**
+ * Maximum number of iterations.
+ */
protected int maxiter;
+ /**
+ * Initialization method.
+ */
protected KMeansInitialization<V> initializer;
@Override
protected void makeOptions(Parameterization config) {
- super.makeOptions(config);
- IntParameter kP = new IntParameter(K_ID, new GreaterConstraint(0));
- if(config.grab(kP)) {
+ ObjectParameter<PrimitiveDistanceFunction<NumberVector<?>, D>> distanceFunctionP = makeParameterDistanceFunction(SquaredEuclideanDistanceFunction.class, PrimitiveDistanceFunction.class);
+ if (config.grab(distanceFunctionP)) {
+ distanceFunction = distanceFunctionP.instantiateClass(config);
+ if (!(distanceFunction instanceof EuclideanDistanceFunction) && !(distanceFunction instanceof SquaredEuclideanDistanceFunction)) {
+ LOG.warning("k-means optimizes the sum of squares - it should be used with squared euclidean distance and may stop converging otherwise!");
+ }
+ }
+
+ IntParameter kP = new IntParameter(K_ID);
+ kP.addConstraint(new GreaterConstraint(0));
+ if (config.grab(kP)) {
k = kP.getValue();
}
ObjectParameter<KMeansInitialization<V>> initialP = new ObjectParameter<KMeansInitialization<V>>(INIT_ID, KMeansInitialization.class, RandomlyGeneratedInitialMeans.class);
- if(config.grab(initialP)) {
+ if (config.grab(initialP)) {
initializer = initialP.instantiateClass(config);
}
- IntParameter maxiterP = new IntParameter(MAXITER_ID, new GreaterEqualConstraint(0), 0);
- if(config.grab(maxiterP)) {
+ IntParameter maxiterP = new IntParameter(MAXITER_ID, 0);
+ maxiterP.addConstraint(new GreaterEqualConstraint(0));
+ if (config.grab(maxiterP)) {
maxiter = maxiterP.getValue();
}
}
@Override
- protected AbstractKMeans<V, D> makeInstance() {
+ protected KMeansMacQueen<V, D> makeInstance() {
return new KMeansMacQueen<V, D>(distanceFunction, k, maxiter, initializer);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/KMeansPlusPlusInitialMeans.java b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/KMeansPlusPlusInitialMeans.java
index 9afeff6c..a07953da 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/KMeansPlusPlusInitialMeans.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/KMeansPlusPlusInitialMeans.java
@@ -38,6 +38,7 @@ import de.lmu.ifi.dbs.elki.distance.distancefunction.PrimitiveDistanceFunction;
import de.lmu.ifi.dbs.elki.distance.distancefunction.PrimitiveDoubleDistanceFunction;
import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance;
import de.lmu.ifi.dbs.elki.logging.LoggingUtil;
+import de.lmu.ifi.dbs.elki.utilities.RandomFactory;
import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
import de.lmu.ifi.dbs.elki.utilities.exceptions.AbortException;
@@ -62,10 +63,10 @@ public class KMeansPlusPlusInitialMeans<V, D extends NumberDistance<D, ?>> exten
/**
* Constructor.
*
- * @param seed Random seed.
+ * @param rnd Random generator.
*/
- public KMeansPlusPlusInitialMeans(Long seed) {
- super(seed);
+ public KMeansPlusPlusInitialMeans(RandomFactory rnd) {
+ super(rnd);
}
@Override
@@ -81,8 +82,8 @@ public class KMeansPlusPlusInitialMeans<V, D extends NumberDistance<D, ?>> exten
// Chose first mean
List<V> means = new ArrayList<V>(k);
- Random random = (seed != null) ? new Random(seed) : new Random();
- DBID first = DBIDUtil.randomSample(relation.getDBIDs(), 1, random.nextLong()).iter().getDBID();
+ Random random = rnd.getRandom();
+ DBID first = DBIDUtil.deref(DBIDUtil.randomSample(relation.getDBIDs(), 1, new Random(random.nextLong())).iter());
means.add(relation.get(first));
ArrayDBIDs ids = DBIDUtil.ensureArray(relation.getDBIDs());
@@ -131,8 +132,8 @@ public class KMeansPlusPlusInitialMeans<V, D extends NumberDistance<D, ?>> exten
// Chose first mean
ArrayModifiableDBIDs means = DBIDUtil.newArray(k);
- Random random = (seed != null) ? new Random(seed) : new Random();
- DBID first = DBIDUtil.randomSample(distQ.getRelation().getDBIDs(), 1, random.nextLong()).iter().getDBID();
+ Random random = rnd.getRandom();
+ DBID first = DBIDUtil.deref(DBIDUtil.randomSample(distQ.getRelation().getDBIDs(), 1, new Random(random.nextLong())).iter());
means.add(first);
ArrayDBIDs ids = DBIDUtil.ensureArray(distQ.getRelation().getDBIDs());
@@ -176,7 +177,7 @@ public class KMeansPlusPlusInitialMeans<V, D extends NumberDistance<D, ?>> exten
double weightsum = 0.0;
DBIDIter it = ids.iter();
for(int i = 0; i < weights.length; i++, it.advance()) {
- if(latest.sameDBID(it)) {
+ if(DBIDUtil.equal(latest, it)) {
weights[i] = 0.0;
}
else {
@@ -243,7 +244,7 @@ public class KMeansPlusPlusInitialMeans<V, D extends NumberDistance<D, ?>> exten
public static class Parameterizer<V, D extends NumberDistance<D, ?>> extends AbstractKMeansInitialization.Parameterizer<V> {
@Override
protected KMeansPlusPlusInitialMeans<V, D> makeInstance() {
- return new KMeansPlusPlusInitialMeans<V, D>(seed);
+ return new KMeansPlusPlusInitialMeans<V, D>(rnd);
}
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/KMediansLloyd.java b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/KMediansLloyd.java
index 8c284981..9917337e 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/KMediansLloyd.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/KMediansLloyd.java
@@ -27,7 +27,6 @@ import java.util.ArrayList;
import java.util.List;
import de.lmu.ifi.dbs.elki.algorithm.AbstractPrimitiveDistanceBasedAlgorithm;
-import de.lmu.ifi.dbs.elki.algorithm.clustering.ClusteringAlgorithm;
import de.lmu.ifi.dbs.elki.data.Cluster;
import de.lmu.ifi.dbs.elki.data.Clustering;
import de.lmu.ifi.dbs.elki.data.NumberVector;
@@ -36,10 +35,10 @@ import de.lmu.ifi.dbs.elki.database.Database;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.ModifiableDBIDs;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.distance.distancefunction.PrimitiveDistanceFunction;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
import de.lmu.ifi.dbs.elki.logging.Logging;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
import de.lmu.ifi.dbs.elki.utilities.documentation.Title;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterConstraint;
@@ -61,18 +60,16 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ObjectParameter;
*
* @author Erich Schubert
*
- * @apiviz.has MeanModel
- *
* @param <V> vector datatype
* @param <D> distance value type
*/
@Title("K-Medians")
-@Reference(title = "Clustering via Concave Minimization", authors = "P. S. Bradley, O. L. Mangasarian, W. N. Street", booktitle = "Advances in neural information processing systems", url="http://nips.djvuzone.org/djvu/nips09/0368.djvu")
-public class KMediansLloyd<V extends NumberVector<V, ?>, D extends Distance<D>> extends AbstractKMeans<V, D> implements ClusteringAlgorithm<Clustering<MeanModel<V>>> {
+@Reference(title = "Clustering via Concave Minimization", authors = "P. S. Bradley, O. L. Mangasarian, W. N. Street", booktitle = "Advances in neural information processing systems", url = "http://nips.djvuzone.org/djvu/nips09/0368.djvu")
+public class KMediansLloyd<V extends NumberVector<?>, D extends Distance<D>> extends AbstractKMeans<V, D, MeanModel<V>> {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(KMediansLloyd.class);
+ private static final Logging LOG = Logging.getLogger(KMediansLloyd.class);
/**
* Constructor.
@@ -80,46 +77,47 @@ public class KMediansLloyd<V extends NumberVector<V, ?>, D extends Distance<D>>
* @param distanceFunction distance function
* @param k k parameter
* @param maxiter Maxiter parameter
+ * @param initializer Initialization method
*/
- public KMediansLloyd(PrimitiveDistanceFunction<NumberVector<?, ?>, D> distanceFunction, int k, int maxiter, KMeansInitialization<V> initializer) {
+ public KMediansLloyd(PrimitiveDistanceFunction<NumberVector<?>, D> distanceFunction, int k, int maxiter, KMeansInitialization<V> initializer) {
super(distanceFunction, k, maxiter, initializer);
}
/**
- * Run k-medians
+ * Run k-medians.
*
* @param database Database
* @param relation relation to use
* @return result
*/
public Clustering<MeanModel<V>> run(Database database, Relation<V> relation) {
- if(relation.size() <= 0) {
+ if (relation.size() <= 0) {
return new Clustering<MeanModel<V>>("k-Medians Clustering", "kmedians-clustering");
}
// Choose initial medians
- List<? extends NumberVector<?, ?>> medians = initializer.chooseInitialMeans(relation, k, getDistanceFunction());
+ List<? extends NumberVector<?>> medians = initializer.chooseInitialMeans(relation, k, getDistanceFunction());
// Setup cluster assignment store
List<ModifiableDBIDs> clusters = new ArrayList<ModifiableDBIDs>();
- for(int i = 0; i < k; i++) {
+ for (int i = 0; i < k; i++) {
clusters.add(DBIDUtil.newHashSet(relation.size() / k));
}
- for(int iteration = 0; maxiter <= 0 || iteration < maxiter; iteration++) {
- if(logger.isVerbose()) {
- logger.verbose("K-Medians iteration " + (iteration + 1));
+ for (int iteration = 0; maxiter <= 0 || iteration < maxiter; iteration++) {
+ if (LOG.isVerbose()) {
+ LOG.verbose("K-Medians iteration " + (iteration + 1));
}
boolean changed = assignToNearestCluster(relation, medians, clusters);
// Stop if no cluster assignment changed.
- if(!changed) {
+ if (!changed) {
break;
}
// Recompute medians.
medians = medians(clusters, medians, relation);
}
// Wrap result
- final V factory = DatabaseUtil.assumeVectorField(relation).getFactory();
+ final NumberVector.Factory<V, ?> factory = RelationUtil.getNumberVectorFactory(relation);
Clustering<MeanModel<V>> result = new Clustering<MeanModel<V>>("k-Medians Clustering", "kmedians-clustering");
- for(int i = 0; i < clusters.size(); i++) {
+ for (int i = 0; i < clusters.size(); i++) {
MeanModel<V> model = new MeanModel<V>(factory.newNumberVector(medians.get(i).getColumnVector().getArrayRef()));
result.addCluster(new Cluster<MeanModel<V>>(clusters.get(i), model));
}
@@ -128,7 +126,7 @@ public class KMediansLloyd<V extends NumberVector<V, ?>, D extends Distance<D>>
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -138,35 +136,46 @@ public class KMediansLloyd<V extends NumberVector<V, ?>, D extends Distance<D>>
*
* @apiviz.exclude
*/
- public static class Parameterizer<V extends NumberVector<V, ?>, D extends Distance<D>> extends AbstractPrimitiveDistanceBasedAlgorithm.Parameterizer<NumberVector<?, ?>, D> {
+ public static class Parameterizer<V extends NumberVector<?>, D extends Distance<D>> extends AbstractPrimitiveDistanceBasedAlgorithm.Parameterizer<NumberVector<?>, D> {
+ /**
+ * k Parameter.
+ */
protected int k;
+ /**
+ * Maximum number of iterations.
+ */
protected int maxiter;
+ /**
+ * Initialization method.
+ */
protected KMeansInitialization<V> initializer;
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- IntParameter kP = new IntParameter(K_ID, new GreaterConstraint(0));
- if(config.grab(kP)) {
- k = kP.getValue();
+ IntParameter kP = new IntParameter(K_ID);
+ kP.addConstraint(new GreaterConstraint(0));
+ if (config.grab(kP)) {
+ k = kP.intValue();
}
ObjectParameter<KMeansInitialization<V>> initialP = new ObjectParameter<KMeansInitialization<V>>(INIT_ID, KMeansInitialization.class, RandomlyGeneratedInitialMeans.class);
- if(config.grab(initialP)) {
+ if (config.grab(initialP)) {
initializer = initialP.instantiateClass(config);
}
- IntParameter maxiterP = new IntParameter(MAXITER_ID, new GreaterEqualConstraint(0), 0);
- if(config.grab(maxiterP)) {
- maxiter = maxiterP.getValue();
+ IntParameter maxiterP = new IntParameter(MAXITER_ID, 0);
+ maxiterP.addConstraint(new GreaterEqualConstraint(0));
+ if (config.grab(maxiterP)) {
+ maxiter = maxiterP.intValue();
}
}
@Override
- protected AbstractKMeans<V, D> makeInstance() {
+ protected KMediansLloyd<V, D> makeInstance() {
return new KMediansLloyd<V, D>(distanceFunction, k, maxiter, initializer);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/KMedoidsEM.java b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/KMedoidsEM.java
index a5c3d675..f4398458 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/KMedoidsEM.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/KMedoidsEM.java
@@ -78,7 +78,7 @@ public class KMedoidsEM<V, D extends NumberDistance<D, ?>> extends AbstractDista
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(KMedoidsEM.class);
+ private static final Logging LOG = Logging.getLogger(KMedoidsEM.class);
/**
* Holds the value of {@link AbstractKMeans#K_ID}.
@@ -118,7 +118,7 @@ public class KMedoidsEM<V, D extends NumberDistance<D, ?>> extends AbstractDista
* @return result
*/
public Clustering<MedoidModel> run(Database database, Relation<V> relation) {
- if(relation.size() <= 0) {
+ if (relation.size() <= 0) {
return new Clustering<MedoidModel>("k-Medoids Clustering", "kmedoids-clustering");
}
DistanceQuery<V, D> distQ = database.getDistanceQuery(relation, getDistanceFunction());
@@ -126,7 +126,7 @@ public class KMedoidsEM<V, D extends NumberDistance<D, ?>> extends AbstractDista
ArrayModifiableDBIDs medoids = DBIDUtil.newArray(initializer.chooseInitialMedoids(k, distQ));
// Setup cluster assignment store
List<ModifiableDBIDs> clusters = new ArrayList<ModifiableDBIDs>();
- for(int i = 0; i < k; i++) {
+ for (int i = 0; i < k; i++) {
clusters.add(DBIDUtil.newHashSet(relation.size() / k));
}
Mean[] mdists = Mean.newArray(k);
@@ -137,41 +137,41 @@ public class KMedoidsEM<V, D extends NumberDistance<D, ?>> extends AbstractDista
// Swap phase
boolean changed = true;
- while(changed) {
+ while (changed) {
changed = false;
// Try to swap the medoid with a better cluster member:
- for(int i = 0; i < k; i++) {
- DBID med = medoids.get(i);
+ int i = 0;
+ for (DBIDIter miter = medoids.iter(); miter.valid(); miter.advance(), i++) {
DBID best = null;
Mean bestm = mdists[i];
- for(DBIDIter iter = clusters.get(i).iter(); iter.valid(); iter.advance()) {
- if(med.sameDBID(iter)) {
+ for (DBIDIter iter = clusters.get(i).iter(); iter.valid(); iter.advance()) {
+ if (DBIDUtil.equal(miter, iter)) {
continue;
}
Mean mdist = new Mean();
- for(DBIDIter iter2 = clusters.get(i).iter(); iter2.valid(); iter2.advance()) {
+ for (DBIDIter iter2 = clusters.get(i).iter(); iter2.valid(); iter2.advance()) {
mdist.put(distQ.distance(iter, iter2).doubleValue());
}
- if(mdist.getMean() < bestm.getMean()) {
- best = iter.getDBID();
+ if (mdist.getMean() < bestm.getMean()) {
+ best = DBIDUtil.deref(iter);
bestm = mdist;
}
}
- if(best != null && !med.sameDBID(best)) {
+ if (best != null && !DBIDUtil.equal(miter, best)) {
changed = true;
medoids.set(i, best);
mdists[i] = bestm;
}
}
// Reassign
- if(changed) {
+ if (changed) {
assignToNearestCluster(medoids, mdists, clusters, distQ);
}
}
// Wrap result
Clustering<MedoidModel> result = new Clustering<MedoidModel>("k-Medoids Clustering", "kmedoids-clustering");
- for(int i = 0; i < clusters.size(); i++) {
+ for (int i = 0; i < clusters.size(); i++) {
MedoidModel model = new MedoidModel(medoids.get(i));
result.addCluster(new Cluster<MedoidModel>(clusters.get(i), model));
}
@@ -192,24 +192,27 @@ public class KMedoidsEM<V, D extends NumberDistance<D, ?>> extends AbstractDista
boolean changed = false;
double[] dists = new double[k];
- for(DBIDIter iditer = distQ.getRelation().iterDBIDs(); iditer.valid(); iditer.advance()) {
+ for (DBIDIter iditer = distQ.getRelation().iterDBIDs(); iditer.valid(); iditer.advance()) {
int minIndex = 0;
double mindist = Double.POSITIVE_INFINITY;
- for(int i = 0; i < k; i++) {
- dists[i] = distQ.distance(iditer, means.get(i)).doubleValue();
- if(dists[i] < mindist) {
- minIndex = i;
- mindist = dists[i];
+ {
+ int i = 0;
+ for (DBIDIter miter = means.iter(); miter.valid(); miter.advance(), i++) {
+ dists[i] = distQ.distance(iditer, miter).doubleValue();
+ if (dists[i] < mindist) {
+ minIndex = i;
+ mindist = dists[i];
+ }
}
}
- if(clusters.get(minIndex).add(iditer)) {
+ if (clusters.get(minIndex).add(iditer)) {
changed = true;
mdist[minIndex].put(mindist);
// Remove from previous cluster
// TODO: keep a list of cluster assignments to save this search?
- for(int i = 0; i < k; i++) {
- if(i != minIndex) {
- if(clusters.get(i).remove(iditer)) {
+ for (int i = 0; i < k; i++) {
+ if (i != minIndex) {
+ if (clusters.get(i).remove(iditer)) {
mdist[minIndex].put(dists[i], -1);
break;
}
@@ -227,7 +230,7 @@ public class KMedoidsEM<V, D extends NumberDistance<D, ?>> extends AbstractDista
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -247,19 +250,21 @@ public class KMedoidsEM<V, D extends NumberDistance<D, ?>> extends AbstractDista
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- IntParameter kP = new IntParameter(KMeans.K_ID, new GreaterConstraint(0));
- if(config.grab(kP)) {
- k = kP.getValue();
+ IntParameter kP = new IntParameter(KMeans.K_ID);
+ kP.addConstraint(new GreaterConstraint(0));
+ if (config.grab(kP)) {
+ k = kP.intValue();
}
ObjectParameter<KMedoidsInitialization<V>> initialP = new ObjectParameter<KMedoidsInitialization<V>>(KMeans.INIT_ID, KMedoidsInitialization.class, PAMInitialMeans.class);
- if(config.grab(initialP)) {
+ if (config.grab(initialP)) {
initializer = initialP.instantiateClass(config);
}
- IntParameter maxiterP = new IntParameter(KMeans.MAXITER_ID, new GreaterEqualConstraint(0), 0);
- if(config.grab(maxiterP)) {
- maxiter = maxiterP.getValue();
+ IntParameter maxiterP = new IntParameter(KMeans.MAXITER_ID, 0);
+ maxiterP.addConstraint(new GreaterEqualConstraint(0));
+ if (config.grab(maxiterP)) {
+ maxiter = maxiterP.intValue();
}
}
@@ -268,4 +273,4 @@ public class KMedoidsEM<V, D extends NumberDistance<D, ?>> extends AbstractDista
return new KMedoidsEM<V, D>(distanceFunction, k, maxiter, initializer);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/KMedoidsPAM.java b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/KMedoidsPAM.java
index 30c80084..906501e4 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/KMedoidsPAM.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/KMedoidsPAM.java
@@ -83,7 +83,7 @@ public class KMedoidsPAM<V, D extends NumberDistance<D, ?>> extends AbstractDist
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(KMedoidsPAM.class);
+ private static final Logging LOG = Logging.getLogger(KMedoidsPAM.class);
/**
* Holds the value of {@link AbstractKMeans#K_ID}.
@@ -123,7 +123,7 @@ public class KMedoidsPAM<V, D extends NumberDistance<D, ?>> extends AbstractDist
* @return result
*/
public Clustering<MedoidModel> run(Database database, Relation<V> relation) {
- if(relation.size() <= 0) {
+ if (relation.size() <= 0) {
return new Clustering<MedoidModel>("k-Medoids Clustering", "kmedoids-clustering");
}
DistanceQuery<V, D> distQ = database.getDistanceQuery(relation, getDistanceFunction());
@@ -132,7 +132,7 @@ public class KMedoidsPAM<V, D extends NumberDistance<D, ?>> extends AbstractDist
ArrayModifiableDBIDs medoids = DBIDUtil.newArray(initializer.chooseInitialMedoids(k, distQ));
// Setup cluster assignment store
List<ModifiableDBIDs> clusters = new ArrayList<ModifiableDBIDs>();
- for(int i = 0; i < k; i++) {
+ for (int i = 0; i < k; i++) {
clusters.add(DBIDUtil.newHashSet(relation.size() / k));
}
@@ -143,36 +143,35 @@ public class KMedoidsPAM<V, D extends NumberDistance<D, ?>> extends AbstractDist
// Swap phase
boolean changed = true;
- while(changed) {
+ while (changed) {
changed = false;
// Try to swap the medoid with a better cluster member:
double best = 0;
DBID bestid = null;
int bestcluster = -1;
- for(int i = 0; i < k; i++) {
- DBID med = medoids.get(i);
- for(DBIDIter iter = clusters.get(i).iter(); iter.valid(); iter.advance()) {
- if(med.sameDBID(iter)) {
+ int i = 0;
+ for (DBIDIter miter = medoids.iter(); miter.valid(); miter.advance(), i++) {
+ for (DBIDIter iter = clusters.get(i).iter(); iter.valid(); iter.advance()) {
+ if (DBIDUtil.equal(miter, iter)) {
continue;
}
// double disti = distQ.distance(id, med).doubleValue();
double cost = 0;
- for(int j = 0; j < k; j++) {
- for(DBIDIter iter2 = clusters.get(j).iter(); iter2.valid(); iter2.advance()) {
- double distcur = distQ.distance(iter2, medoids.get(j)).doubleValue();
+ DBIDIter olditer = medoids.iter();
+ for (int j = 0; j < k; j++, olditer.advance()) {
+ for (DBIDIter iter2 = clusters.get(j).iter(); iter2.valid(); iter2.advance()) {
+ double distcur = distQ.distance(iter2, olditer).doubleValue();
double distnew = distQ.distance(iter2, iter).doubleValue();
- if(j == i) {
+ if (j == i) {
// Cases 1 and 2.
double distsec = second.doubleValue(iter2);
- if(distcur > distsec) {
+ if (distcur > distsec) {
// Case 1, other would switch to a third medoid
cost += distsec - distcur; // Always positive!
- }
- else { // Would remain with the candidate
+ } else { // Would remain with the candidate
cost += distnew - distcur; // Could be negative
}
- }
- else {
+ } else {
// Cases 3-4: objects from other clusters
if (distcur < distnew) {
// Case 3: no change
@@ -185,20 +184,20 @@ public class KMedoidsPAM<V, D extends NumberDistance<D, ?>> extends AbstractDist
}
if (cost < best) {
best = cost;
- bestid = iter.getDBID();
+ bestid = DBIDUtil.deref(iter);
bestcluster = i;
}
}
}
- if(logger.isDebugging()) {
- logger.debug("Best cost: " + best);
+ if (LOG.isDebugging()) {
+ LOG.debug("Best cost: " + best);
}
- if(bestid != null) {
+ if (bestid != null) {
changed = true;
medoids.set(bestcluster, bestid);
}
// Reassign
- if(changed) {
+ if (changed) {
// TODO: can we save some of these recomputations?
assignToNearestCluster(medoids, ids, second, clusters, distQ);
}
@@ -206,7 +205,7 @@ public class KMedoidsPAM<V, D extends NumberDistance<D, ?>> extends AbstractDist
// Wrap result
Clustering<MedoidModel> result = new Clustering<MedoidModel>("k-Medoids Clustering", "kmedoids-clustering");
- for(int i = 0; i < clusters.size(); i++) {
+ for (int i = 0; i < clusters.size(); i++) {
MedoidModel model = new MedoidModel(medoids.get(i));
result.addCluster(new Cluster<MedoidModel>(clusters.get(i), model));
}
@@ -227,28 +226,30 @@ public class KMedoidsPAM<V, D extends NumberDistance<D, ?>> extends AbstractDist
protected boolean assignToNearestCluster(ArrayDBIDs means, DBIDs ids, WritableDoubleDataStore second, List<? extends ModifiableDBIDs> clusters, DistanceQuery<V, D> distQ) {
boolean changed = false;
- for(DBIDIter iditer = distQ.getRelation().iterDBIDs(); iditer.valid(); iditer.advance()) {
+ for (DBIDIter iditer = distQ.getRelation().iterDBIDs(); iditer.valid(); iditer.advance()) {
int minIndex = 0;
double mindist = Double.POSITIVE_INFINITY;
double mindist2 = Double.POSITIVE_INFINITY;
- for(int i = 0; i < k; i++) {
- double dist = distQ.distance(iditer, means.get(i)).doubleValue();
- if(dist < mindist) {
- minIndex = i;
- mindist2 = mindist;
- mindist = dist;
- }
- else if(dist < mindist2) {
- mindist2 = dist;
+ {
+ int i = 0;
+ for (DBIDIter miter = means.iter(); miter.valid(); miter.advance(), i++) {
+ double dist = distQ.distance(iditer, miter).doubleValue();
+ if (dist < mindist) {
+ minIndex = i;
+ mindist2 = mindist;
+ mindist = dist;
+ } else if (dist < mindist2) {
+ mindist2 = dist;
+ }
}
}
- if(clusters.get(minIndex).add(iditer)) {
+ if (clusters.get(minIndex).add(iditer)) {
changed = true;
// Remove from previous cluster
// TODO: keep a list of cluster assignments to save this search?
- for(int i = 0; i < k; i++) {
- if(i != minIndex) {
- if(clusters.get(i).remove(iditer)) {
+ for (int i = 0; i < k; i++) {
+ if (i != minIndex) {
+ if (clusters.get(i).remove(iditer)) {
break;
}
}
@@ -266,7 +267,7 @@ public class KMedoidsPAM<V, D extends NumberDistance<D, ?>> extends AbstractDist
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -286,19 +287,21 @@ public class KMedoidsPAM<V, D extends NumberDistance<D, ?>> extends AbstractDist
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- IntParameter kP = new IntParameter(KMeans.K_ID, new GreaterConstraint(0));
- if(config.grab(kP)) {
- k = kP.getValue();
+ IntParameter kP = new IntParameter(KMeans.K_ID);
+ kP.addConstraint(new GreaterConstraint(0));
+ if (config.grab(kP)) {
+ k = kP.intValue();
}
ObjectParameter<KMedoidsInitialization<V>> initialP = new ObjectParameter<KMedoidsInitialization<V>>(KMeans.INIT_ID, KMedoidsInitialization.class, PAMInitialMeans.class);
- if(config.grab(initialP)) {
+ if (config.grab(initialP)) {
initializer = initialP.instantiateClass(config);
}
- IntParameter maxiterP = new IntParameter(KMeans.MAXITER_ID, new GreaterEqualConstraint(0), 0);
- if(config.grab(maxiterP)) {
- maxiter = maxiterP.getValue();
+ IntParameter maxiterP = new IntParameter(KMeans.MAXITER_ID, 0);
+ maxiterP.addConstraint(new GreaterEqualConstraint(0));
+ if (config.grab(maxiterP)) {
+ maxiter = maxiterP.intValue();
}
}
@@ -307,4 +310,4 @@ public class KMedoidsPAM<V, D extends NumberDistance<D, ?>> extends AbstractDist
return new KMedoidsPAM<V, D>(distanceFunction, k, maxiter, initializer);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/PAMInitialMeans.java b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/PAMInitialMeans.java
index 094c37bb..1fc7160e 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/PAMInitialMeans.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/PAMInitialMeans.java
@@ -113,7 +113,7 @@ public class PAMInitialMeans<V, D extends NumberDistance<D, ?>> implements KMean
}
if(mean.getMean() < best) {
best = mean.getMean();
- bestid = iter.getDBID();
+ bestid = DBIDUtil.deref(iter);
if(bestd != null) {
bestd.destroy();
}
@@ -133,23 +133,21 @@ public class PAMInitialMeans<V, D extends NumberDistance<D, ?>> implements KMean
DBID bestid = null;
WritableDoubleDataStore bestd = null;
for(DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
- DBID id = iter.getDBID();
- if(medids.contains(id)) {
+ if(medids.contains(iter)) {
continue;
}
WritableDoubleDataStore newd = DataStoreUtil.makeDoubleStorage(ids, DataStoreFactory.HINT_HOT | DataStoreFactory.HINT_TEMP);
mean.reset();
for(DBIDIter iter2 = ids.iter(); iter2.valid(); iter2.advance()) {
- DBID other = iter2.getDBID();
- double dn = distQ.distance(id, other).doubleValue();
- double v = Math.min(dn, mindist.doubleValue(other));
+ double dn = distQ.distance(iter, iter2).doubleValue();
+ double v = Math.min(dn, mindist.doubleValue(iter2));
mean.put(v);
- newd.put(other, v);
+ newd.put(iter2, v);
}
assert (mean.getCount() == ids.size());
if(mean.getMean() < best) {
best = mean.getMean();
- bestid = id;
+ bestid = DBIDUtil.deref(iter);
if(bestd != null) {
bestd.destroy();
}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/RandomlyChosenInitialMeans.java b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/RandomlyChosenInitialMeans.java
index 5b9da923..78e59be7 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/RandomlyChosenInitialMeans.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/RandomlyChosenInitialMeans.java
@@ -31,6 +31,7 @@ import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.distancefunction.PrimitiveDistanceFunction;
+import de.lmu.ifi.dbs.elki.utilities.RandomFactory;
/**
* Initialize K-means by randomly choosing k exsiting elements as cluster
@@ -44,15 +45,15 @@ public class RandomlyChosenInitialMeans<V> extends AbstractKMeansInitialization<
/**
* Constructor.
*
- * @param seed Random seed.
+ * @param rnd Random generator.
*/
- public RandomlyChosenInitialMeans(Long seed) {
- super(seed);
+ public RandomlyChosenInitialMeans(RandomFactory rnd) {
+ super(rnd);
}
@Override
public List<V> chooseInitialMeans(Relation<V> relation, int k, PrimitiveDistanceFunction<? super V, ?> distanceFunction) {
- DBIDs ids = DBIDUtil.randomSample(relation.getDBIDs(), k, seed);
+ DBIDs ids = DBIDUtil.randomSample(relation.getDBIDs(), k, rnd);
List<V> means = new ArrayList<V>(k);
for(DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
means.add(relation.get(iter));
@@ -62,7 +63,7 @@ public class RandomlyChosenInitialMeans<V> extends AbstractKMeansInitialization<
@Override
public DBIDs chooseInitialMedoids(int k, DistanceQuery<? super V, ?> distanceFunction) {
- return DBIDUtil.randomSample(distanceFunction.getRelation().getDBIDs(), k, seed);
+ return DBIDUtil.randomSample(distanceFunction.getRelation().getDBIDs(), k, rnd);
}
/**
@@ -76,7 +77,7 @@ public class RandomlyChosenInitialMeans<V> extends AbstractKMeansInitialization<
@Override
protected RandomlyChosenInitialMeans<V> makeInstance() {
- return new RandomlyChosenInitialMeans<V>(seed);
+ return new RandomlyChosenInitialMeans<V>(rnd);
}
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/RandomlyGeneratedInitialMeans.java b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/RandomlyGeneratedInitialMeans.java
index 00ed08c4..300f5cb0 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/RandomlyGeneratedInitialMeans.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/RandomlyGeneratedInitialMeans.java
@@ -28,9 +28,11 @@ import java.util.Random;
import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.distance.distancefunction.PrimitiveDistanceFunction;
import de.lmu.ifi.dbs.elki.math.MathUtil;
import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
+import de.lmu.ifi.dbs.elki.utilities.RandomFactory;
import de.lmu.ifi.dbs.elki.utilities.pairs.Pair;
/**
@@ -41,29 +43,30 @@ import de.lmu.ifi.dbs.elki.utilities.pairs.Pair;
*
* @param <V> Vector type
*/
-public class RandomlyGeneratedInitialMeans<V extends NumberVector<V, ?>> extends AbstractKMeansInitialization<V> {
+public class RandomlyGeneratedInitialMeans<V extends NumberVector<?>> extends AbstractKMeansInitialization<V> {
/**
* Constructor.
*
- * @param seed Random seed.
+ * @param rnd Random generator.
*/
- public RandomlyGeneratedInitialMeans(Long seed) {
- super(seed);
+ public RandomlyGeneratedInitialMeans(RandomFactory rnd) {
+ super(rnd);
}
@Override
public List<V> chooseInitialMeans(Relation<V> relation, int k, PrimitiveDistanceFunction<? super V, ?> distanceFunction) {
- final int dim = DatabaseUtil.dimensionality(relation);
+ final int dim = RelationUtil.dimensionality(relation);
+ NumberVector.Factory<V, ?> factory = RelationUtil.getNumberVectorFactory(relation);
Pair<V, V> minmax = DatabaseUtil.computeMinMax(relation);
List<V> means = new ArrayList<V>(k);
- final Random random = (this.seed != null) ? new Random(this.seed) : new Random();
+ final Random random = rnd.getRandom();
for(int i = 0; i < k; i++) {
double[] r = MathUtil.randomDoubleArray(dim, random);
// Rescale
for(int d = 0; d < dim; d++) {
- r[d] = minmax.first.doubleValue(d + 1) + (minmax.second.doubleValue(d + 1) - minmax.first.doubleValue(d + 1)) * r[d];
+ r[d] = minmax.first.doubleValue(d) + (minmax.second.doubleValue(d) - minmax.first.doubleValue(d)) * r[d];
}
- means.add(minmax.first.newNumberVector(r));
+ means.add(factory.newNumberVector(r));
}
return means;
}
@@ -75,10 +78,10 @@ public class RandomlyGeneratedInitialMeans<V extends NumberVector<V, ?>> extends
*
* @apiviz.exclude
*/
- public static class Parameterizer<V extends NumberVector<V, ?>> extends AbstractKMeansInitialization.Parameterizer<V> {
+ public static class Parameterizer<V extends NumberVector<?>> extends AbstractKMeansInitialization.Parameterizer<V> {
@Override
protected RandomlyGeneratedInitialMeans<V> makeInstance() {
- return new RandomlyGeneratedInitialMeans<V>(seed);
+ return new RandomlyGeneratedInitialMeans<V>(rnd);
}
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/package-info.java b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/package-info.java
index eed031df..4ba1ce09 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/package-info.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/package-info.java
@@ -8,6 +8,10 @@
* partition the database complete or is in any other sense a relaxed clustering result.
*
* @apiviz.exclude de.lmu.ifi.dbs.elki.algorithm.clustering.OPTICSXi.SteepAreaResult
+ * @apiviz.exclude de.lmu.ifi.dbs.elki.algorithm.Algorithm
+ * @apiviz.exclude de.lmu.ifi.dbs.elki.algorithm.AbstractAlgorithm
+ * @apiviz.exclude de.lmu.ifi.dbs.elki.algorithm.AbstractDistanceBasedAlgorithm
+ * @apiviz.exclude de.lmu.ifi.dbs.elki.algorithm.clustering.trivial.*
*
* @see de.lmu.ifi.dbs.elki.algorithm
*/
@@ -33,4 +37,4 @@ GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package de.lmu.ifi.dbs.elki.algorithm.clustering; \ No newline at end of file
+package de.lmu.ifi.dbs.elki.algorithm.clustering;
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/subspace/CLIQUE.java b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/subspace/CLIQUE.java
index 01a693e4..37b3eb57 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/subspace/CLIQUE.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/subspace/CLIQUE.java
@@ -43,13 +43,13 @@ import de.lmu.ifi.dbs.elki.data.Subspace;
import de.lmu.ifi.dbs.elki.data.model.SubspaceModel;
import de.lmu.ifi.dbs.elki.data.type.TypeInformation;
import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.ModifiableDBIDs;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.logging.Logging;
+import de.lmu.ifi.dbs.elki.math.linearalgebra.Centroid;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Matrix;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.utilities.FormatUtil;
import de.lmu.ifi.dbs.elki.utilities.documentation.Description;
import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
@@ -57,7 +57,7 @@ import de.lmu.ifi.dbs.elki.utilities.documentation.Title;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterConstraint;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.IntervalConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.LessConstraint;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.DoubleParameter;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.Flag;
@@ -96,11 +96,11 @@ import de.lmu.ifi.dbs.elki.utilities.pairs.Pair;
@Title("CLIQUE: Automatic Subspace Clustering of High Dimensional Data for Data Mining Applications")
@Description("Grid-based algorithm to identify dense clusters in subspaces of maximum dimensionality.")
@Reference(authors = "R. Agrawal, J. Gehrke, D. Gunopulos, P. Raghavan", title = "Automatic Subspace Clustering of High Dimensional Data for Data Mining Applications", booktitle = "Proc. SIGMOD Conference, Seattle, WA, 1998", url = "http://dx.doi.org/10.1145/276304.276314")
-public class CLIQUE<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Clustering<SubspaceModel<V>>> implements SubspaceClusteringAlgorithm<SubspaceModel<V>> {
+public class CLIQUE<V extends NumberVector<?>> extends AbstractAlgorithm<Clustering<SubspaceModel<V>>> implements SubspaceClusteringAlgorithm<SubspaceModel<V>> {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(CLIQUE.class);
+ private static final Logging LOG = Logging.getLogger(CLIQUE.class);
/**
* Parameter to specify the number of intervals (units) in each dimension,
@@ -109,7 +109,7 @@ public class CLIQUE<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Clus
* Key: {@code -clique.xsi}
* </p>
*/
- public static final OptionID XSI_ID = OptionID.getOrCreateOptionID("clique.xsi", "The number of intervals (units) in each dimension.");
+ public static final OptionID XSI_ID = new OptionID("clique.xsi", "The number of intervals (units) in each dimension.");
/**
* Parameter to specify the density threshold for the selectivity of a unit,
@@ -119,7 +119,7 @@ public class CLIQUE<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Clus
* Key: {@code -clique.tau}
* </p>
*/
- public static final OptionID TAU_ID = OptionID.getOrCreateOptionID("clique.tau", "The density threshold for the selectivity of a unit, where the selectivity is" + "the fraction of total feature vectors contained in this unit.");
+ public static final OptionID TAU_ID = new OptionID("clique.tau", "The density threshold for the selectivity of a unit, where the selectivity is" + "the fraction of total feature vectors contained in this unit.");
/**
* Flag to indicate that only subspaces with large coverage (i.e. the fraction
@@ -129,7 +129,7 @@ public class CLIQUE<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Clus
* Key: {@code -clique.prune}
* </p>
*/
- public static final OptionID PRUNE_ID = OptionID.getOrCreateOptionID("clique.prune", "Flag to indicate that only subspaces with large coverage " + "(i.e. the fraction of the database that is covered by the dense units) " + "are selected, the rest will be pruned.");
+ public static final OptionID PRUNE_ID = new OptionID("clique.prune", "Flag to indicate that only subspaces with large coverage " + "(i.e. the fraction of the database that is covered by the dense units) " + "are selected, the rest will be pruned.");
/**
* Holds the value of {@link #XSI_ID}.
@@ -169,53 +169,53 @@ public class CLIQUE<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Clus
public Clustering<SubspaceModel<V>> run(Relation<V> relation) {
// 1. Identification of subspaces that contain clusters
// TODO: use step logging.
- if(logger.isVerbose()) {
- logger.verbose("*** 1. Identification of subspaces that contain clusters ***");
+ if(LOG.isVerbose()) {
+ LOG.verbose("*** 1. Identification of subspaces that contain clusters ***");
}
SortedMap<Integer, List<CLIQUESubspace<V>>> dimensionToDenseSubspaces = new TreeMap<Integer, List<CLIQUESubspace<V>>>();
List<CLIQUESubspace<V>> denseSubspaces = findOneDimensionalDenseSubspaces(relation);
- dimensionToDenseSubspaces.put(0, denseSubspaces);
- if(logger.isVerbose()) {
- logger.verbose(" 1-dimensional dense subspaces: " + denseSubspaces.size());
+ dimensionToDenseSubspaces.put(Integer.valueOf(0), denseSubspaces);
+ if(LOG.isVerbose()) {
+ LOG.verbose(" 1-dimensional dense subspaces: " + denseSubspaces.size());
}
- if(logger.isDebugging()) {
+ if(LOG.isDebugging()) {
for(CLIQUESubspace<V> s : denseSubspaces) {
- logger.debug(s.toString(" "));
+ LOG.debug(s.toString(" "));
}
}
- int dimensionality = DatabaseUtil.dimensionality(relation);
+ int dimensionality = RelationUtil.dimensionality(relation);
for(int k = 2; k <= dimensionality && !denseSubspaces.isEmpty(); k++) {
denseSubspaces = findDenseSubspaces(relation, denseSubspaces);
- dimensionToDenseSubspaces.put(k - 1, denseSubspaces);
- if(logger.isVerbose()) {
- logger.verbose(" " + k + "-dimensional dense subspaces: " + denseSubspaces.size());
+ dimensionToDenseSubspaces.put(Integer.valueOf(k - 1), denseSubspaces);
+ if(LOG.isVerbose()) {
+ LOG.verbose(" " + k + "-dimensional dense subspaces: " + denseSubspaces.size());
}
- if(logger.isDebugging()) {
+ if(LOG.isDebugging()) {
for(CLIQUESubspace<V> s : denseSubspaces) {
- logger.debug(s.toString(" "));
+ LOG.debug(s.toString(" "));
}
}
}
// 2. Identification of clusters
- if(logger.isVerbose()) {
- logger.verbose("*** 2. Identification of clusters ***");
+ if(LOG.isVerbose()) {
+ LOG.verbose("*** 2. Identification of clusters ***");
}
// build result
int numClusters = 1;
Clustering<SubspaceModel<V>> result = new Clustering<SubspaceModel<V>>("CLIQUE clustering", "clique-clustering");
for(Integer dim : dimensionToDenseSubspaces.keySet()) {
List<CLIQUESubspace<V>> subspaces = dimensionToDenseSubspaces.get(dim);
- List<Pair<Subspace<V>, ModifiableDBIDs>> modelsAndClusters = determineClusters(subspaces);
+ List<Pair<Subspace, ModifiableDBIDs>> modelsAndClusters = determineClusters(subspaces);
- if(logger.isVerbose()) {
- logger.verbose(" " + (dim + 1) + "-dimensional clusters: " + modelsAndClusters.size());
+ if(LOG.isVerbose()) {
+ LOG.verbose(" " + (dim + 1) + "-dimensional clusters: " + modelsAndClusters.size());
}
- for(Pair<Subspace<V>, ModifiableDBIDs> modelAndCluster : modelsAndClusters) {
+ for(Pair<Subspace, ModifiableDBIDs> modelAndCluster : modelsAndClusters) {
Cluster<SubspaceModel<V>> newCluster = new Cluster<SubspaceModel<V>>(modelAndCluster.second);
- newCluster.setModel(new SubspaceModel<V>(modelAndCluster.first, DatabaseUtil.centroid(relation, modelAndCluster.second)));
+ newCluster.setModel(new SubspaceModel<V>(modelAndCluster.first, Centroid.make(relation, modelAndCluster.second).toVector(relation)));
newCluster.setName("cluster_" + numClusters++);
result.addCluster(newCluster);
}
@@ -232,13 +232,13 @@ public class CLIQUE<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Clus
* @return the clusters in the specified dense subspaces and the corresponding
* cluster models
*/
- private List<Pair<Subspace<V>, ModifiableDBIDs>> determineClusters(List<CLIQUESubspace<V>> denseSubspaces) {
- List<Pair<Subspace<V>, ModifiableDBIDs>> clusters = new ArrayList<Pair<Subspace<V>, ModifiableDBIDs>>();
+ private List<Pair<Subspace, ModifiableDBIDs>> determineClusters(List<CLIQUESubspace<V>> denseSubspaces) {
+ List<Pair<Subspace, ModifiableDBIDs>> clusters = new ArrayList<Pair<Subspace, ModifiableDBIDs>>();
for(CLIQUESubspace<V> subspace : denseSubspaces) {
- List<Pair<Subspace<V>, ModifiableDBIDs>> clustersInSubspace = subspace.determineClusters();
- if(logger.isDebugging()) {
- logger.debugFine("Subspace " + subspace + " clusters " + clustersInSubspace.size());
+ List<Pair<Subspace, ModifiableDBIDs>> clustersInSubspace = subspace.determineClusters();
+ if(LOG.isDebugging()) {
+ LOG.debugFine("Subspace " + subspace + " clusters " + clustersInSubspace.size());
}
clusters.addAll(clustersInSubspace);
}
@@ -289,7 +289,7 @@ public class CLIQUE<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Clus
* @return the created one dimensional units
*/
private Collection<CLIQUEUnit<V>> initOneDimensionalUnits(Relation<V> database) {
- int dimensionality = DatabaseUtil.dimensionality(database);
+ int dimensionality = RelationUtil.dimensionality(database);
// initialize minima and maxima
double[] minima = new double[dimensionality];
double[] maxima = new double[dimensionality];
@@ -312,12 +312,12 @@ public class CLIQUE<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Clus
unit_lengths[d] = (maxima[d] - minima[d]) / xsi;
}
- if(logger.isDebuggingFiner()) {
- StringBuffer msg = new StringBuffer();
+ if(LOG.isDebuggingFiner()) {
+ StringBuilder msg = new StringBuilder();
msg.append(" minima: ").append(FormatUtil.format(minima, ", ", 2));
msg.append("\n maxima: ").append(FormatUtil.format(maxima, ", ", 2));
msg.append("\n unit lengths: ").append(FormatUtil.format(unit_lengths, ", ", 2));
- logger.debugFiner(msg.toString());
+ LOG.debugFiner(msg.toString());
}
// determine the boundaries of the units
@@ -332,10 +332,10 @@ public class CLIQUE<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Clus
}
}
}
- if(logger.isDebuggingFiner()) {
- StringBuffer msg = new StringBuffer();
+ if(LOG.isDebuggingFiner()) {
+ StringBuilder msg = new StringBuilder();
msg.append(" unit bounds ").append(FormatUtil.format(new Matrix(unit_bounds), " "));
- logger.debugFiner(msg.toString());
+ LOG.debugFiner(msg.toString());
}
// build the 1 dimensional units
@@ -346,10 +346,10 @@ public class CLIQUE<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Clus
}
}
- if(logger.isDebuggingFiner()) {
- StringBuffer msg = new StringBuffer();
+ if(LOG.isDebuggingFiner()) {
+ StringBuilder msg = new StringBuilder();
msg.append(" total number of 1-dim units: ").append(units.size());
- logger.debugFiner(msg.toString());
+ LOG.debugFiner(msg.toString());
}
return units;
@@ -367,12 +367,12 @@ public class CLIQUE<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Clus
if(minima.length != featureVector.getDimensionality()) {
throw new IllegalArgumentException("FeatureVectors differ in length.");
}
- for(int d = 1; d <= featureVector.getDimensionality(); d++) {
- if((featureVector.doubleValue(d)) > maxima[d - 1]) {
- maxima[d - 1] = (featureVector.doubleValue(d));
+ for(int d = 0; d < featureVector.getDimensionality(); d++) {
+ if((featureVector.doubleValue(d)) > maxima[d]) {
+ maxima[d] = (featureVector.doubleValue(d));
}
- if((featureVector.doubleValue(d)) < minima[d - 1]) {
- minima[d - 1] = (featureVector.doubleValue(d));
+ if((featureVector.doubleValue(d)) < minima[d]) {
+ minima[d] = (featureVector.doubleValue(d));
}
}
}
@@ -387,38 +387,37 @@ public class CLIQUE<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Clus
*/
private List<CLIQUESubspace<V>> findOneDimensionalDenseSubspaceCandidates(Relation<V> database) {
Collection<CLIQUEUnit<V>> units = initOneDimensionalUnits(database);
- Collection<CLIQUEUnit<V>> denseUnits = new ArrayList<CLIQUEUnit<V>>();
- Map<Integer, CLIQUESubspace<V>> denseSubspaces = new HashMap<Integer, CLIQUESubspace<V>>();
-
// identify dense units
double total = database.size();
- for(DBIDIter it = database.iterDBIDs(); it.valid();) {
+ for(DBIDIter it = database.iterDBIDs(); it.valid(); it.advance()) {
V featureVector = database.get(it);
- final DBID id = it.getDBID();
- it.advance();
for(CLIQUEUnit<V> unit : units) {
- unit.addFeatureVector(id, featureVector);
- // unit is a dense unit
- // FIXME: why it.valid()?
- if(!it.valid() && unit.selectivity(total) >= tau) {
- denseUnits.add(unit);
- // add the dense unit to its subspace
- int dim = unit.getIntervals().iterator().next().getDimension();
- CLIQUESubspace<V> subspace_d = denseSubspaces.get(dim);
- if(subspace_d == null) {
- subspace_d = new CLIQUESubspace<V>(dim);
- denseSubspaces.put(dim, subspace_d);
- }
- subspace_d.addDenseUnit(unit);
+ unit.addFeatureVector(it, featureVector);
+ }
+ }
+
+ Collection<CLIQUEUnit<V>> denseUnits = new ArrayList<CLIQUEUnit<V>>();
+ Map<Integer, CLIQUESubspace<V>> denseSubspaces = new HashMap<Integer, CLIQUESubspace<V>>();
+ for(CLIQUEUnit<V> unit : units) {
+ // unit is a dense unit
+ if(unit.selectivity(total) >= tau) {
+ denseUnits.add(unit);
+ // add the dense unit to its subspace
+ int dim = unit.getIntervals().iterator().next().getDimension();
+ CLIQUESubspace<V> subspace_d = denseSubspaces.get(Integer.valueOf(dim));
+ if(subspace_d == null) {
+ subspace_d = new CLIQUESubspace<V>(dim);
+ denseSubspaces.put(Integer.valueOf(dim), subspace_d);
}
+ subspace_d.addDenseUnit(unit);
}
}
- if(logger.isDebugging()) {
- StringBuffer msg = new StringBuffer();
+ if(LOG.isDebugging()) {
+ StringBuilder msg = new StringBuilder();
msg.append(" number of 1-dim dense units: ").append(denseUnits.size());
msg.append("\n number of 1-dim dense subspace candidates: ").append(denseSubspaces.size());
- logger.debugFine(msg.toString());
+ LOG.debugFine(msg.toString());
}
List<CLIQUESubspace<V>> subspaceCandidates = new ArrayList<CLIQUESubspace<V>>(denseSubspaces.values());
@@ -574,7 +573,7 @@ public class CLIQUE<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Clus
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -584,7 +583,7 @@ public class CLIQUE<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Clus
*
* @apiviz.exclude
*/
- public static class Parameterizer<V extends NumberVector<V, ?>> extends AbstractParameterizer {
+ public static class Parameterizer<V extends NumberVector<?>> extends AbstractParameterizer {
protected int xsi;
protected double tau;
@@ -594,19 +593,22 @@ public class CLIQUE<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Clus
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- IntParameter xsiP = new IntParameter(XSI_ID, new GreaterConstraint(0));
+ IntParameter xsiP = new IntParameter(XSI_ID);
+ xsiP.addConstraint(new GreaterConstraint(0));
if(config.grab(xsiP)) {
- xsi = xsiP.getValue();
+ xsi = xsiP.intValue();
}
- DoubleParameter tauP = new DoubleParameter(TAU_ID, new IntervalConstraint(0, IntervalConstraint.IntervalBoundary.OPEN, 1, IntervalConstraint.IntervalBoundary.OPEN));
+ DoubleParameter tauP = new DoubleParameter(TAU_ID);
+ tauP.addConstraint(new GreaterConstraint(0));
+ tauP.addConstraint(new LessConstraint(1));
if(config.grab(tauP)) {
- tau = tauP.getValue();
+ tau = tauP.doubleValue();
}
Flag pruneF = new Flag(PRUNE_ID);
if(config.grab(pruneF)) {
- prune = pruneF.getValue();
+ prune = pruneF.isTrue();
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/subspace/DiSH.java b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/subspace/DiSH.java
index df3fe8b5..a3496a0e 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/subspace/DiSH.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/subspace/DiSH.java
@@ -47,6 +47,7 @@ import de.lmu.ifi.dbs.elki.database.ids.ArrayModifiableDBIDs;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.distance.distancefunction.IndexBasedDistanceFunction;
import de.lmu.ifi.dbs.elki.distance.distancefunction.ProxyDistanceFunction;
import de.lmu.ifi.dbs.elki.distance.distancefunction.subspace.DiSHDistanceFunction;
@@ -55,10 +56,11 @@ import de.lmu.ifi.dbs.elki.distance.distancevalue.PreferenceVectorBasedCorrelati
import de.lmu.ifi.dbs.elki.index.preprocessed.preference.DiSHPreferenceVectorIndex;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.logging.progress.FiniteProgress;
+import de.lmu.ifi.dbs.elki.math.linearalgebra.Centroid;
+import de.lmu.ifi.dbs.elki.math.linearalgebra.ProjectedCentroid;
import de.lmu.ifi.dbs.elki.result.optics.ClusterOrderEntry;
import de.lmu.ifi.dbs.elki.result.optics.ClusterOrderResult;
import de.lmu.ifi.dbs.elki.utilities.ClassGenericsUtil;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.utilities.FormatUtil;
import de.lmu.ifi.dbs.elki.utilities.datastructures.hierarchy.HierarchyReferenceLists;
import de.lmu.ifi.dbs.elki.utilities.documentation.Description;
@@ -99,11 +101,11 @@ import de.lmu.ifi.dbs.elki.utilities.pairs.Pair;
@Title("DiSH: Detecting Subspace cluster Hierarchies")
@Description("Algorithm to find hierarchical correlation clusters in subspaces.")
@Reference(authors = "E. Achtert, C. Böhm, H.-P. Kriegel, P. Kröger, I. Müller-Gorman, A. Zimek", title = "Detection and Visualization of Subspace Cluster Hierarchies", booktitle = "Proc. 12th International Conference on Database Systems for Advanced Applications (DASFAA), Bangkok, Thailand, 2007", url = "http://dx.doi.org/10.1007/978-3-540-71703-4_15")
-public class DiSH<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Clustering<SubspaceModel<V>>> implements SubspaceClusteringAlgorithm<SubspaceModel<V>> {
+public class DiSH<V extends NumberVector<?>> extends AbstractAlgorithm<Clustering<SubspaceModel<V>>> implements SubspaceClusteringAlgorithm<SubspaceModel<V>> {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(DiSH.class);
+ private static final Logging LOG = Logging.getLogger(DiSH.class);
/**
* Parameter that specifies the maximum radius of the neighborhood to be
@@ -116,7 +118,7 @@ public class DiSH<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Cluste
* Key: {@code -dish.epsilon}
* </p>
*/
- public static final OptionID EPSILON_ID = OptionID.getOrCreateOptionID("dish.epsilon", "The maximum radius of the neighborhood " + "to be considered in each dimension for determination of " + "the preference vector.");
+ public static final OptionID EPSILON_ID = new OptionID("dish.epsilon", "The maximum radius of the neighborhood " + "to be considered in each dimension for determination of " + "the preference vector.");
/**
* Parameter that specifies the a minimum number of points as a smoothing
@@ -128,7 +130,7 @@ public class DiSH<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Cluste
* Key: {@code -dish.mu}
* </p>
*/
- public static final OptionID MU_ID = OptionID.getOrCreateOptionID("dish.mu", "The minimum number of points as a smoothing factor to avoid the single-link-effekt.");
+ public static final OptionID MU_ID = new OptionID("dish.mu", "The minimum number of points as a smoothing factor to avoid the single-link-effekt.");
/**
* Holds the value of {@link #EPSILON_ID}.
@@ -167,13 +169,13 @@ public class DiSH<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Cluste
*/
public Clustering<SubspaceModel<V>> run(Database database, Relation<V> relation) {
// Instantiate DiSH distance (and thus run the preprocessor)
- if(logger.isVerbose()) {
- logger.verbose("*** Run DiSH preprocessor.");
+ if (LOG.isVerbose()) {
+ LOG.verbose("*** Run DiSH preprocessor.");
}
DiSHDistanceFunction.Instance<V> dishDistanceQuery = dishDistance.instantiate(relation);
// Configure and run OPTICS.
- if(logger.isVerbose()) {
- logger.verbose("*** Run OPTICS algorithm.");
+ if (LOG.isVerbose()) {
+ LOG.verbose("*** Run OPTICS algorithm.");
}
ListParameterization opticsconfig = new ListParameterization(opticsAlgorithmParameters);
opticsconfig.addParameter(OPTICS.DISTANCE_FUNCTION_ID, ProxyDistanceFunction.proxy(dishDistanceQuery));
@@ -183,8 +185,8 @@ public class DiSH<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Cluste
optics = opticsconfig.tryInstantiate(cls);
ClusterOrderResult<PreferenceVectorBasedCorrelationDistance> opticsResult = optics.run(database, relation);
- if(logger.isVerbose()) {
- logger.verbose("*** Compute Clusters.");
+ if (LOG.isVerbose()) {
+ LOG.verbose("*** Compute Clusters.");
}
return computeClusters(relation, opticsResult, dishDistanceQuery);
}
@@ -197,64 +199,64 @@ public class DiSH<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Cluste
* @param distFunc Distance function
*/
private Clustering<SubspaceModel<V>> computeClusters(Relation<V> database, ClusterOrderResult<PreferenceVectorBasedCorrelationDistance> clusterOrder, DiSHDistanceFunction.Instance<V> distFunc) {
- int dimensionality = DatabaseUtil.dimensionality(database);
+ int dimensionality = RelationUtil.dimensionality(database);
int minpts = dishDistance.getMinpts();
// extract clusters
Map<BitSet, List<Pair<BitSet, ArrayModifiableDBIDs>>> clustersMap = extractClusters(database, distFunc, clusterOrder);
- if(logger.isVerbose()) {
- StringBuffer msg = new StringBuffer("Step 1: extract clusters");
- for(List<Pair<BitSet, ArrayModifiableDBIDs>> clusterList : clustersMap.values()) {
- for(Pair<BitSet, ArrayModifiableDBIDs> c : clusterList) {
- msg.append("\n").append(FormatUtil.format(dimensionality, c.first)).append(" ids ").append(c.second.size());
+ if (LOG.isVerbose()) {
+ StringBuilder msg = new StringBuilder("Step 1: extract clusters");
+ for (List<Pair<BitSet, ArrayModifiableDBIDs>> clusterList : clustersMap.values()) {
+ for (Pair<BitSet, ArrayModifiableDBIDs> c : clusterList) {
+ msg.append('\n').append(FormatUtil.format(dimensionality, c.first)).append(" ids ").append(c.second.size());
}
}
- logger.verbose(msg.toString());
+ LOG.verbose(msg.toString());
}
// check if there are clusters < minpts
checkClusters(database, distFunc, clustersMap, minpts);
- if(logger.isVerbose()) {
- StringBuffer msg = new StringBuffer("Step 2: check clusters");
- for(List<Pair<BitSet, ArrayModifiableDBIDs>> clusterList : clustersMap.values()) {
- for(Pair<BitSet, ArrayModifiableDBIDs> c : clusterList) {
- msg.append("\n").append(FormatUtil.format(dimensionality, c.first)).append(" ids ").append(c.second.size());
+ if (LOG.isVerbose()) {
+ StringBuilder msg = new StringBuilder("Step 2: check clusters");
+ for (List<Pair<BitSet, ArrayModifiableDBIDs>> clusterList : clustersMap.values()) {
+ for (Pair<BitSet, ArrayModifiableDBIDs> c : clusterList) {
+ msg.append('\n').append(FormatUtil.format(dimensionality, c.first)).append(" ids ").append(c.second.size());
}
}
- logger.verbose(msg.toString());
+ LOG.verbose(msg.toString());
}
// sort the clusters
List<Cluster<SubspaceModel<V>>> clusters = sortClusters(database, clustersMap);
- if(logger.isVerbose()) {
- StringBuffer msg = new StringBuffer("Step 3: sort clusters");
- for(Cluster<SubspaceModel<V>> c : clusters) {
- msg.append("\n").append(FormatUtil.format(dimensionality, c.getModel().getSubspace().getDimensions())).append(" ids ").append(c.size());
+ if (LOG.isVerbose()) {
+ StringBuilder msg = new StringBuilder("Step 3: sort clusters");
+ for (Cluster<SubspaceModel<V>> c : clusters) {
+ msg.append('\n').append(FormatUtil.format(dimensionality, c.getModel().getSubspace().getDimensions())).append(" ids ").append(c.size());
}
- logger.verbose(msg.toString());
+ LOG.verbose(msg.toString());
}
// build the hierarchy
buildHierarchy(database, distFunc, clusters, dimensionality);
- if(logger.isVerbose()) {
- StringBuffer msg = new StringBuffer("Step 4: build hierarchy");
- for(Cluster<SubspaceModel<V>> c : clusters) {
- msg.append("\n").append(FormatUtil.format(dimensionality, c.getModel().getDimensions())).append(" ids ").append(c.size());
- for(Cluster<SubspaceModel<V>> cluster : c.getParents()) {
+ if (LOG.isVerbose()) {
+ StringBuilder msg = new StringBuilder("Step 4: build hierarchy");
+ for (Cluster<SubspaceModel<V>> c : clusters) {
+ msg.append('\n').append(FormatUtil.format(dimensionality, c.getModel().getDimensions())).append(" ids ").append(c.size());
+ for (Cluster<SubspaceModel<V>> cluster : c.getParents()) {
msg.append("\n parent ").append(cluster);
}
- for(Cluster<SubspaceModel<V>> cluster : c.getChildren()) {
+ for (Cluster<SubspaceModel<V>> cluster : c.getChildren()) {
msg.append("\n child ").append(cluster);
}
}
- logger.verbose(msg.toString());
+ LOG.verbose(msg.toString());
}
// build result
Clustering<SubspaceModel<V>> result = new Clustering<SubspaceModel<V>>("DiSH clustering", "dish-clustering");
- for(Cluster<SubspaceModel<V>> c : clusters) {
- if(c.getParents() == null || c.getParents().isEmpty()) {
+ for (Cluster<SubspaceModel<V>> c : clusters) {
+ if (c.getParents() == null || c.getParents().isEmpty()) {
result.addCluster(c);
}
}
@@ -270,12 +272,12 @@ public class DiSH<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Cluste
* @return the extracted clusters
*/
private Map<BitSet, List<Pair<BitSet, ArrayModifiableDBIDs>>> extractClusters(Relation<V> database, DiSHDistanceFunction.Instance<V> distFunc, ClusterOrderResult<PreferenceVectorBasedCorrelationDistance> clusterOrder) {
- FiniteProgress progress = logger.isVerbose() ? new FiniteProgress("Extract Clusters", database.size(), logger) : null;
+ FiniteProgress progress = LOG.isVerbose() ? new FiniteProgress("Extract Clusters", database.size(), LOG) : null;
int processed = 0;
Map<BitSet, List<Pair<BitSet, ArrayModifiableDBIDs>>> clustersMap = new HashMap<BitSet, List<Pair<BitSet, ArrayModifiableDBIDs>>>();
Map<DBID, ClusterOrderEntry<PreferenceVectorBasedCorrelationDistance>> entryMap = new HashMap<DBID, ClusterOrderEntry<PreferenceVectorBasedCorrelationDistance>>();
Map<DBID, Pair<BitSet, ArrayModifiableDBIDs>> entryToClusterMap = new HashMap<DBID, Pair<BitSet, ArrayModifiableDBIDs>>();
- for(Iterator<ClusterOrderEntry<PreferenceVectorBasedCorrelationDistance>> it = clusterOrder.iterator(); it.hasNext();) {
+ for (Iterator<ClusterOrderEntry<PreferenceVectorBasedCorrelationDistance>> it = clusterOrder.iterator(); it.hasNext();) {
ClusterOrderEntry<PreferenceVectorBasedCorrelationDistance> entry = it.next();
entryMap.put(entry.getID(), entry);
@@ -284,68 +286,68 @@ public class DiSH<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Cluste
// get the list of (parallel) clusters for the preference vector
List<Pair<BitSet, ArrayModifiableDBIDs>> parallelClusters = clustersMap.get(preferenceVector);
- if(parallelClusters == null) {
+ if (parallelClusters == null) {
parallelClusters = new ArrayList<Pair<BitSet, ArrayModifiableDBIDs>>();
clustersMap.put(preferenceVector, parallelClusters);
}
// look for the proper cluster
Pair<BitSet, ArrayModifiableDBIDs> cluster = null;
- for(Pair<BitSet, ArrayModifiableDBIDs> c : parallelClusters) {
- V c_centroid = DatabaseUtil.centroid(database, c.second, c.first);
+ for (Pair<BitSet, ArrayModifiableDBIDs> c : parallelClusters) {
+ V c_centroid = ProjectedCentroid.make(c.first, database, c.second).toVector(database);
PreferenceVectorBasedCorrelationDistance dist = distFunc.correlationDistance(object, c_centroid, preferenceVector, preferenceVector);
- if(dist.getCorrelationValue() == entry.getReachability().getCorrelationValue()) {
+ if (dist.getCorrelationValue() == entry.getReachability().getCorrelationValue()) {
double d = distFunc.weightedDistance(object, c_centroid, dist.getCommonPreferenceVector());
- if(d <= 2 * epsilon) {
+ if (d <= 2 * epsilon) {
cluster = c;
break;
}
}
}
- if(cluster == null) {
+ if (cluster == null) {
cluster = new Pair<BitSet, ArrayModifiableDBIDs>(preferenceVector, DBIDUtil.newArray());
parallelClusters.add(cluster);
}
cluster.second.add(entry.getID());
entryToClusterMap.put(entry.getID(), cluster);
- if(progress != null) {
- progress.setProcessed(++processed, logger);
+ if (progress != null) {
+ progress.setProcessed(++processed, LOG);
}
}
- if(progress != null) {
- progress.ensureCompleted(logger);
+ if (progress != null) {
+ progress.ensureCompleted(LOG);
}
- if(logger.isDebuggingFiner()) {
- StringBuffer msg = new StringBuffer("Step 0");
- for(List<Pair<BitSet, ArrayModifiableDBIDs>> clusterList : clustersMap.values()) {
- for(Pair<BitSet, ArrayModifiableDBIDs> c : clusterList) {
- msg.append("\n").append(FormatUtil.format(DatabaseUtil.dimensionality(database), c.first)).append(" ids ").append(c.second.size());
+ if (LOG.isDebuggingFiner()) {
+ StringBuilder msg = new StringBuilder("Step 0");
+ for (List<Pair<BitSet, ArrayModifiableDBIDs>> clusterList : clustersMap.values()) {
+ for (Pair<BitSet, ArrayModifiableDBIDs> c : clusterList) {
+ msg.append('\n').append(FormatUtil.format(RelationUtil.dimensionality(database), c.first)).append(" ids ").append(c.second.size());
}
}
- logger.debugFiner(msg.toString());
+ LOG.debugFiner(msg.toString());
}
// add the predecessor to the cluster
- for(BitSet pv : clustersMap.keySet()) {
+ for (BitSet pv : clustersMap.keySet()) {
List<Pair<BitSet, ArrayModifiableDBIDs>> parallelClusters = clustersMap.get(pv);
- for(Pair<BitSet, ArrayModifiableDBIDs> cluster : parallelClusters) {
- if(cluster.second.isEmpty()) {
+ for (Pair<BitSet, ArrayModifiableDBIDs> cluster : parallelClusters) {
+ if (cluster.second.isEmpty()) {
continue;
}
DBID firstID = cluster.second.get(0);
ClusterOrderEntry<PreferenceVectorBasedCorrelationDistance> entry = entryMap.get(firstID);
DBID predecessorID = entry.getPredecessorID();
- if(predecessorID == null) {
+ if (predecessorID == null) {
continue;
}
ClusterOrderEntry<PreferenceVectorBasedCorrelationDistance> predecessor = entryMap.get(predecessorID);
// parallel cluster
- if(predecessor.getReachability().getCommonPreferenceVector().equals(entry.getReachability().getCommonPreferenceVector())) {
+ if (predecessor.getReachability().getCommonPreferenceVector().equals(entry.getReachability().getCommonPreferenceVector())) {
continue;
}
- if(predecessor.getReachability().compareTo(entry.getReachability()) < 0) {
+ if (predecessor.getReachability().compareTo(entry.getReachability()) < 0) {
continue;
}
@@ -369,22 +371,21 @@ public class DiSH<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Cluste
* @return a sorted list of the clusters
*/
private List<Cluster<SubspaceModel<V>>> sortClusters(Relation<V> database, Map<BitSet, List<Pair<BitSet, ArrayModifiableDBIDs>>> clustersMap) {
- final int db_dim = DatabaseUtil.dimensionality(database);
+ final int db_dim = RelationUtil.dimensionality(database);
// int num = 1;
List<Cluster<SubspaceModel<V>>> clusters = new ArrayList<Cluster<SubspaceModel<V>>>();
- for(BitSet pv : clustersMap.keySet()) {
+ for (BitSet pv : clustersMap.keySet()) {
List<Pair<BitSet, ArrayModifiableDBIDs>> parallelClusters = clustersMap.get(pv);
- for(int i = 0; i < parallelClusters.size(); i++) {
+ for (int i = 0; i < parallelClusters.size(); i++) {
Pair<BitSet, ArrayModifiableDBIDs> c = parallelClusters.get(i);
Cluster<SubspaceModel<V>> cluster = new Cluster<SubspaceModel<V>>(c.second);
- cluster.setModel(new SubspaceModel<V>(new Subspace<V>(c.first), DatabaseUtil.centroid(database, c.second)));
+ cluster.setModel(new SubspaceModel<V>(new Subspace(c.first), Centroid.make(database, c.second).toVector(database)));
cluster.setHierarchy(new HierarchyReferenceLists<Cluster<SubspaceModel<V>>>(cluster, new ArrayList<Cluster<SubspaceModel<V>>>(), new ArrayList<Cluster<SubspaceModel<V>>>()));
// cluster.setName("Cluster_" + num++);
String subspace = FormatUtil.format(cluster.getModel().getSubspace().getDimensions(), db_dim, "");
- if(parallelClusters.size() > 1) {
+ if (parallelClusters.size() > 1) {
cluster.setName("Cluster_" + subspace + "_" + i);
- }
- else {
+ } else {
cluster.setName("Cluster_" + subspace);
}
clusters.add(cluster);
@@ -417,11 +418,11 @@ public class DiSH<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Cluste
List<Pair<BitSet, ArrayModifiableDBIDs>> notAssigned = new ArrayList<Pair<BitSet, ArrayModifiableDBIDs>>();
Map<BitSet, List<Pair<BitSet, ArrayModifiableDBIDs>>> newClustersMap = new HashMap<BitSet, List<Pair<BitSet, ArrayModifiableDBIDs>>>();
Pair<BitSet, ArrayModifiableDBIDs> noise = new Pair<BitSet, ArrayModifiableDBIDs>(new BitSet(), DBIDUtil.newArray());
- for(BitSet pv : clustersMap.keySet()) {
+ for (BitSet pv : clustersMap.keySet()) {
// noise
- if(pv.cardinality() == 0) {
+ if (pv.cardinality() == 0) {
List<Pair<BitSet, ArrayModifiableDBIDs>> parallelClusters = clustersMap.get(pv);
- for(Pair<BitSet, ArrayModifiableDBIDs> c : parallelClusters) {
+ for (Pair<BitSet, ArrayModifiableDBIDs> c : parallelClusters) {
noise.second.addDBIDs(c.second);
}
}
@@ -429,11 +430,10 @@ public class DiSH<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Cluste
else {
List<Pair<BitSet, ArrayModifiableDBIDs>> parallelClusters = clustersMap.get(pv);
List<Pair<BitSet, ArrayModifiableDBIDs>> newParallelClusters = new ArrayList<Pair<BitSet, ArrayModifiableDBIDs>>(parallelClusters.size());
- for(Pair<BitSet, ArrayModifiableDBIDs> c : parallelClusters) {
- if(!pv.equals(new BitSet()) && c.second.size() < minpts) {
+ for (Pair<BitSet, ArrayModifiableDBIDs> c : parallelClusters) {
+ if (!pv.equals(new BitSet()) && c.second.size() < minpts) {
notAssigned.add(c);
- }
- else {
+ } else {
newParallelClusters.add(c);
}
}
@@ -444,15 +444,14 @@ public class DiSH<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Cluste
clustersMap.clear();
clustersMap.putAll(newClustersMap);
- for(Pair<BitSet, ArrayModifiableDBIDs> c : notAssigned) {
- if(c.second.isEmpty()) {
+ for (Pair<BitSet, ArrayModifiableDBIDs> c : notAssigned) {
+ if (c.second.isEmpty()) {
continue;
}
Pair<BitSet, ArrayModifiableDBIDs> parent = findParent(database, distFunc, c, clustersMap);
- if(parent != null) {
+ if (parent != null) {
parent.second.addDBIDs(c.second);
- }
- else {
+ } else {
noise.second.addDBIDs(c.second);
}
}
@@ -472,30 +471,30 @@ public class DiSH<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Cluste
* @return the parent of the specified cluster
*/
private Pair<BitSet, ArrayModifiableDBIDs> findParent(Relation<V> database, DiSHDistanceFunction.Instance<V> distFunc, Pair<BitSet, ArrayModifiableDBIDs> child, Map<BitSet, List<Pair<BitSet, ArrayModifiableDBIDs>>> clustersMap) {
- V child_centroid = DatabaseUtil.centroid(database, child.second, child.first);
+ V child_centroid = ProjectedCentroid.make(child.first, database, child.second).toVector(database);
Pair<BitSet, ArrayModifiableDBIDs> result = null;
int resultCardinality = -1;
BitSet childPV = child.first;
int childCardinality = childPV.cardinality();
- for(BitSet parentPV : clustersMap.keySet()) {
+ for (BitSet parentPV : clustersMap.keySet()) {
int parentCardinality = parentPV.cardinality();
- if(parentCardinality >= childCardinality) {
+ if (parentCardinality >= childCardinality) {
continue;
}
- if(resultCardinality != -1 && parentCardinality <= resultCardinality) {
+ if (resultCardinality != -1 && parentCardinality <= resultCardinality) {
continue;
}
BitSet pv = (BitSet) childPV.clone();
pv.and(parentPV);
- if(pv.equals(parentPV)) {
+ if (pv.equals(parentPV)) {
List<Pair<BitSet, ArrayModifiableDBIDs>> parentList = clustersMap.get(parentPV);
- for(Pair<BitSet, ArrayModifiableDBIDs> parent : parentList) {
- V parent_centroid = DatabaseUtil.centroid(database, parent.second, parentPV);
+ for (Pair<BitSet, ArrayModifiableDBIDs> parent : parentList) {
+ V parent_centroid = ProjectedCentroid.make(parentPV, database, parent.second).toVector(database);
double d = distFunc.weightedDistance(child_centroid, parent_centroid, parentPV);
- if(d <= 2 * epsilon) {
+ if (d <= 2 * epsilon) {
result = parent;
resultCardinality = parentCardinality;
break;
@@ -516,64 +515,62 @@ public class DiSH<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Cluste
* @param database the database containing the data objects
*/
private void buildHierarchy(Relation<V> database, DiSHDistanceFunction.Instance<V> distFunc, List<Cluster<SubspaceModel<V>>> clusters, int dimensionality) {
- StringBuffer msg = new StringBuffer();
- final int db_dim = DatabaseUtil.dimensionality(database);
+ StringBuilder msg = new StringBuilder();
+ final int db_dim = RelationUtil.dimensionality(database);
- for(int i = 0; i < clusters.size() - 1; i++) {
+ for (int i = 0; i < clusters.size() - 1; i++) {
Cluster<SubspaceModel<V>> c_i = clusters.get(i);
int subspaceDim_i = dimensionality - c_i.getModel().getSubspace().dimensionality();
- V ci_centroid = DatabaseUtil.centroid(database, c_i.getIDs(), c_i.getModel().getDimensions());
+ V ci_centroid = ProjectedCentroid.make(c_i.getModel().getDimensions(), database, c_i.getIDs()).toVector(database);
- for(int j = i + 1; j < clusters.size(); j++) {
+ for (int j = i + 1; j < clusters.size(); j++) {
Cluster<SubspaceModel<V>> c_j = clusters.get(j);
int subspaceDim_j = dimensionality - c_j.getModel().getSubspace().dimensionality();
- if(subspaceDim_i < subspaceDim_j) {
- if(logger.isDebugging()) {
- msg.append("\n l_i=").append(subspaceDim_i).append(" pv_i=[").append(FormatUtil.format(db_dim, c_i.getModel().getSubspace().getDimensions())).append("]");
- msg.append("\n l_j=").append(subspaceDim_j).append(" pv_j=[").append(FormatUtil.format(db_dim, c_j.getModel().getSubspace().getDimensions())).append("]");
+ if (subspaceDim_i < subspaceDim_j) {
+ if (LOG.isDebugging()) {
+ msg.append("\n l_i=").append(subspaceDim_i).append(" pv_i=[").append(FormatUtil.format(db_dim, c_i.getModel().getSubspace().getDimensions())).append(']');
+ msg.append("\n l_j=").append(subspaceDim_j).append(" pv_j=[").append(FormatUtil.format(db_dim, c_j.getModel().getSubspace().getDimensions())).append(']');
}
// noise level reached
- if(c_j.getModel().getSubspace().dimensionality() == 0) {
+ if (c_j.getModel().getSubspace().dimensionality() == 0) {
// no parents exists -> parent is noise
- if(c_i.getParents().isEmpty()) {
+ if (c_i.getParents().isEmpty()) {
c_j.getChildren().add(c_i);
c_i.getParents().add(c_j);
- if(logger.isDebugging()) {
+ if (LOG.isDebugging()) {
msg.append("\n [").append(FormatUtil.format(db_dim, c_j.getModel().getSubspace().getDimensions()));
msg.append("] is parent of [").append(FormatUtil.format(db_dim, c_i.getModel().getSubspace().getDimensions()));
- msg.append("]");
+ msg.append(']');
}
}
- }
- else {
- V cj_centroid = DatabaseUtil.centroid(database, c_j.getIDs(), c_j.getModel().getDimensions());
+ } else {
+ V cj_centroid = ProjectedCentroid.make(c_j.getModel().getDimensions(), database, c_j.getIDs()).toVector(database);
PreferenceVectorBasedCorrelationDistance distance = distFunc.correlationDistance(ci_centroid, cj_centroid, c_i.getModel().getSubspace().getDimensions(), c_j.getModel().getSubspace().getDimensions());
double d = distFunc.weightedDistance(ci_centroid, cj_centroid, distance.getCommonPreferenceVector());
- if(logger.isDebugging()) {
+ if (LOG.isDebugging()) {
msg.append("\n dist = ").append(distance.getCorrelationValue());
}
- if(distance.getCorrelationValue() == subspaceDim_j) {
- if(logger.isDebugging()) {
+ if (distance.getCorrelationValue() == subspaceDim_j) {
+ if (LOG.isDebugging()) {
msg.append("\n d = ").append(d);
}
- if(d <= 2 * epsilon) {
+ if (d <= 2 * epsilon) {
// no parent exists or c_j is not a parent of the already
// existing parents
- if(c_i.getParents().isEmpty() || !isParent(database, distFunc, c_j, c_i.getParents())) {
+ if (c_i.getParents().isEmpty() || !isParent(database, distFunc, c_j, c_i.getParents())) {
c_j.getChildren().add(c_i);
c_i.getParents().add(c_j);
- if(logger.isDebugging()) {
+ if (LOG.isDebugging()) {
msg.append("\n [").append(FormatUtil.format(db_dim, c_j.getModel().getSubspace().getDimensions()));
msg.append("] is parent of [");
msg.append(FormatUtil.format(db_dim, c_i.getModel().getSubspace().getDimensions()));
- msg.append("]");
+ msg.append(']');
}
}
- }
- else {
+ } else {
throw new RuntimeException("Should never happen: d = " + d);
}
}
@@ -581,8 +578,8 @@ public class DiSH<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Cluste
}
}
}
- if(logger.isDebugging()) {
- logger.debug(msg.toString());
+ if (LOG.isDebugging()) {
+ LOG.debug(msg.toString());
}
}
@@ -599,14 +596,14 @@ public class DiSH<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Cluste
* the children clusters, false otherwise
*/
private boolean isParent(Relation<V> database, DiSHDistanceFunction.Instance<V> distFunc, Cluster<SubspaceModel<V>> parent, List<Cluster<SubspaceModel<V>>> children) {
- V parent_centroid = DatabaseUtil.centroid(database, parent.getIDs(), parent.getModel().getDimensions());
- int dimensionality = DatabaseUtil.dimensionality(database);
+ V parent_centroid = ProjectedCentroid.make(parent.getModel().getDimensions(), database, parent.getIDs()).toVector(database);
+ int dimensionality = RelationUtil.dimensionality(database);
int subspaceDim_parent = dimensionality - parent.getModel().getSubspace().dimensionality();
- for(Cluster<SubspaceModel<V>> child : children) {
- V child_centroid = DatabaseUtil.centroid(database, child.getIDs(), child.getModel().getDimensions());
+ for (Cluster<SubspaceModel<V>> child : children) {
+ V child_centroid = ProjectedCentroid.make(child.getModel().getDimensions(), database, child.getIDs()).toVector(database);
PreferenceVectorBasedCorrelationDistance distance = distFunc.correlationDistance(parent_centroid, child_centroid, parent.getModel().getSubspace().getDimensions(), child.getModel().getSubspace().getDimensions());
- if(distance.getCorrelationValue() == subspaceDim_parent) {
+ if (distance.getCorrelationValue() == subspaceDim_parent) {
return true;
}
}
@@ -621,7 +618,7 @@ public class DiSH<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Cluste
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -631,7 +628,7 @@ public class DiSH<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Cluste
*
* @apiviz.exclude
*/
- public static class Parameterizer<V extends NumberVector<V, ?>> extends AbstractParameterizer {
+ public static class Parameterizer<V extends NumberVector<?>> extends AbstractParameterizer {
protected double epsilon = 0.0;
protected int mu = 1;
@@ -644,14 +641,16 @@ public class DiSH<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Cluste
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- DoubleParameter epsilonP = new DoubleParameter(EPSILON_ID, new GreaterEqualConstraint(0), 0.001);
- if(config.grab(epsilonP)) {
- epsilon = epsilonP.getValue();
+ DoubleParameter epsilonP = new DoubleParameter(EPSILON_ID, 0.001);
+ epsilonP.addConstraint(new GreaterEqualConstraint(0));
+ if (config.grab(epsilonP)) {
+ epsilon = epsilonP.doubleValue();
}
- IntParameter muP = new IntParameter(MU_ID, new GreaterConstraint(0), 1);
- if(config.grab(muP)) {
- mu = muP.getValue();
+ IntParameter muP = new IntParameter(MU_ID, 1);
+ muP.addConstraint(new GreaterConstraint(0));
+ if (config.grab(muP)) {
+ mu = muP.intValue();
}
configDiSHDistance(config, epsilon, mu);
@@ -703,4 +702,4 @@ public class DiSH<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Cluste
return new DiSH<V>(epsilon, dishDistance, opticsO);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/subspace/HiSC.java b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/subspace/HiSC.java
index 40ab60a8..58f3acef 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/subspace/HiSC.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/subspace/HiSC.java
@@ -34,7 +34,8 @@ import de.lmu.ifi.dbs.elki.utilities.documentation.Description;
import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
import de.lmu.ifi.dbs.elki.utilities.documentation.Title;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.IntervalConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.LessConstraint;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.ChainedParameterization;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.ListParameterization;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
@@ -60,11 +61,11 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.DoubleParameter;
@Title("Finding Hierarchies of Subspace Clusters")
@Description("Algorithm for detecting hierarchies of subspace clusters.")
@Reference(authors = "E. Achtert, C. Böhm, H.-P. Kriegel, P. Kröger, I. Müller-Gorman, A. Zimek", title = "Finding Hierarchies of Subspace Clusters", booktitle = "Proc. 10th Europ. Conf. on Principles and Practice of Knowledge Discovery in Databases (PKDD'06), Berlin, Germany, 2006", url = "http://www.dbs.ifi.lmu.de/Publikationen/Papers/PKDD06-HiSC.pdf")
-public class HiSC<V extends NumberVector<V, ?>> extends OPTICS<V, PreferenceVectorBasedCorrelationDistance> {
+public class HiSC<V extends NumberVector<?>> extends OPTICS<V, PreferenceVectorBasedCorrelationDistance> {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(HiSC.class);
+ private static final Logging LOG = Logging.getLogger(HiSC.class);
/**
* Constructor.
@@ -77,7 +78,7 @@ public class HiSC<V extends NumberVector<V, ?>> extends OPTICS<V, PreferenceVect
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -87,16 +88,18 @@ public class HiSC<V extends NumberVector<V, ?>> extends OPTICS<V, PreferenceVect
*
* @apiviz.exclude
*/
- public static class Parameterizer<V extends NumberVector<V, ?>> extends AbstractParameterizer {
+ public static class Parameterizer<V extends NumberVector<?>> extends AbstractParameterizer {
HiSCDistanceFunction<V> distanceFunction;
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- DoubleParameter alphaP = new DoubleParameter(HiSCPreferenceVectorIndex.Factory.ALPHA_ID, new IntervalConstraint(0.0, IntervalConstraint.IntervalBoundary.OPEN, 1.0, IntervalConstraint.IntervalBoundary.OPEN), HiSCPreferenceVectorIndex.Factory.DEFAULT_ALPHA);
+ DoubleParameter alphaP = new DoubleParameter(HiSCPreferenceVectorIndex.Factory.ALPHA_ID, HiSCPreferenceVectorIndex.Factory.DEFAULT_ALPHA);
+ alphaP.addConstraint(new GreaterConstraint(0.0));
+ alphaP.addConstraint(new LessConstraint(1.0));
double alpha = 0.0;
if(config.grab(alphaP)) {
- alpha = alphaP.getValue();
+ alpha = alphaP.doubleValue();
}
// Configure HiSC distance function
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/subspace/PROCLUS.java b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/subspace/PROCLUS.java
index 4eedbecd..ef49ff10 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/subspace/PROCLUS.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/subspace/PROCLUS.java
@@ -23,15 +23,17 @@ package de.lmu.ifi.dbs.elki.algorithm.clustering.subspace;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+import gnu.trove.iterator.TIntIterator;
+import gnu.trove.set.TIntSet;
+import gnu.trove.set.hash.TIntHashSet;
+
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collections;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
-import java.util.Set;
import de.lmu.ifi.dbs.elki.algorithm.clustering.AbstractProjectedClustering;
import de.lmu.ifi.dbs.elki.data.Cluster;
@@ -47,16 +49,20 @@ import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
+import de.lmu.ifi.dbs.elki.database.ids.DistanceDBIDPair;
import de.lmu.ifi.dbs.elki.database.ids.ModifiableDBIDs;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
-import de.lmu.ifi.dbs.elki.database.query.GenericDistanceResultPair;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.database.query.range.RangeQuery;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResultUtil;
import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.logging.progress.IndefiniteProgress;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
+import de.lmu.ifi.dbs.elki.math.Mean;
+import de.lmu.ifi.dbs.elki.math.linearalgebra.Centroid;
+import de.lmu.ifi.dbs.elki.utilities.RandomFactory;
import de.lmu.ifi.dbs.elki.utilities.documentation.Description;
import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
import de.lmu.ifi.dbs.elki.utilities.documentation.Title;
@@ -64,7 +70,7 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterConstraint;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.LongParameter;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.RandomParameter;
import de.lmu.ifi.dbs.elki.utilities.pairs.CTriple;
import de.lmu.ifi.dbs.elki.utilities.pairs.Pair;
@@ -90,11 +96,11 @@ import de.lmu.ifi.dbs.elki.utilities.pairs.Pair;
@Title("PROCLUS: PROjected CLUStering")
@Description("Algorithm to find subspace clusters in high dimensional spaces.")
@Reference(authors = "C. C. Aggarwal, C. Procopiuc, J. L. Wolf, P. S. Yu, J. S. Park", title = "Fast Algorithms for Projected Clustering", booktitle = "Proc. ACM SIGMOD Int. Conf. on Management of Data (SIGMOD '99)", url = "http://dx.doi.org/10.1145/304181.304188")
-public class PROCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClustering<Clustering<SubspaceModel<V>>, V> implements SubspaceClusteringAlgorithm<SubspaceModel<V>> {
+public class PROCLUS<V extends NumberVector<?>> extends AbstractProjectedClustering<Clustering<SubspaceModel<V>>, V> implements SubspaceClusteringAlgorithm<SubspaceModel<V>> {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(PROCLUS.class);
+ private static final Logging LOG = Logging.getLogger(PROCLUS.class);
/**
* Parameter to specify the multiplier for the initial number of medoids, must
@@ -106,12 +112,7 @@ public class PROCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClus
* Key: {@code -proclus.mi}
* </p>
*/
- public static final OptionID M_I_ID = OptionID.getOrCreateOptionID("proclus.mi", "The multiplier for the initial number of medoids.");
-
- /**
- * Parameter to specify the random generator seed.
- */
- public static final OptionID SEED_ID = OptionID.getOrCreateOptionID("proclus.seed", "The random number generator seed.");
+ public static final OptionID M_I_ID = new OptionID("proclus.mi", "The multiplier for the initial number of medoids.");
/**
* Holds the value of {@link #M_I_ID}.
@@ -119,9 +120,9 @@ public class PROCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClus
private int m_i;
/**
- * Holds the value of {@link #SEED_ID}.
+ * Random generator
*/
- private Long seed;
+ private RandomFactory rnd;
/**
* Java constructor.
@@ -130,12 +131,12 @@ public class PROCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClus
* @param k_i k_i Parameter
* @param l l Parameter
* @param m_i m_i Parameter
- * @param seed Random generator seed
+ * @param rnd Random generator
*/
- public PROCLUS(int k, int k_i, int l, int m_i, Long seed) {
+ public PROCLUS(int k, int k_i, int l, int m_i, RandomFactory rnd) {
super(k, k_i, l);
this.m_i = m_i;
- this.seed = seed;
+ this.rnd = rnd;
}
/**
@@ -147,19 +148,16 @@ public class PROCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClus
public Clustering<SubspaceModel<V>> run(Database database, Relation<V> relation) {
DistanceQuery<V, DoubleDistance> distFunc = this.getDistanceQuery(database);
RangeQuery<V, DoubleDistance> rangeQuery = database.getRangeQuery(distFunc);
- final Random random = new Random();
- if(seed != null) {
- random.setSeed(seed);
- }
+ final Random random = rnd.getRandom();
- if(DatabaseUtil.dimensionality(relation) < l) {
- throw new IllegalStateException("Dimensionality of data < parameter l! " + "(" + DatabaseUtil.dimensionality(relation) + " < " + l + ")");
+ if(RelationUtil.dimensionality(relation) < l) {
+ throw new IllegalStateException("Dimensionality of data < parameter l! " + "(" + RelationUtil.dimensionality(relation) + " < " + l + ")");
}
// TODO: use a StepProgress!
// initialization phase
- if(logger.isVerbose()) {
- logger.verbose("1. Initialization phase...");
+ if(LOG.isVerbose()) {
+ LOG.verbose("1. Initialization phase...");
}
int sampleSize = Math.min(relation.size(), k_i * k);
DBIDs sampleSet = DBIDUtil.randomSample(relation.getDBIDs(), sampleSize, random.nextLong());
@@ -167,39 +165,39 @@ public class PROCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClus
int medoidSize = Math.min(relation.size(), m_i * k);
DBIDs medoids = greedy(distFunc, sampleSet, medoidSize, random);
- if(logger.isDebugging()) {
- StringBuffer msg = new StringBuffer();
- msg.append("\n");
- msg.append("sampleSize ").append(sampleSize).append("\n");
- msg.append("sampleSet ").append(sampleSet).append("\n");
- msg.append("medoidSize ").append(medoidSize).append("\n");
- msg.append("m ").append(medoids).append("\n");
- logger.debugFine(msg.toString());
+ if(LOG.isDebugging()) {
+ StringBuilder msg = new StringBuilder();
+ msg.append('\n');
+ msg.append("sampleSize ").append(sampleSize).append('\n');
+ msg.append("sampleSet ").append(sampleSet).append('\n');
+ msg.append("medoidSize ").append(medoidSize).append('\n');
+ msg.append("m ").append(medoids).append('\n');
+ LOG.debugFine(msg.toString());
}
// iterative phase
- if(logger.isVerbose()) {
- logger.verbose("2. Iterative phase...");
+ if(LOG.isVerbose()) {
+ LOG.verbose("2. Iterative phase...");
}
double bestObjective = Double.POSITIVE_INFINITY;
ModifiableDBIDs m_best = null;
ModifiableDBIDs m_bad = null;
ModifiableDBIDs m_current = initialSet(medoids, k, random);
- if(logger.isDebugging()) {
- StringBuffer msg = new StringBuffer();
- msg.append("\n");
- msg.append("m_c ").append(m_current).append("\n");
- logger.debugFine(msg.toString());
+ if(LOG.isDebugging()) {
+ StringBuilder msg = new StringBuilder();
+ msg.append('\n');
+ msg.append("m_c ").append(m_current).append('\n');
+ LOG.debugFine(msg.toString());
}
- IndefiniteProgress cprogress = logger.isVerbose() ? new IndefiniteProgress("Current number of clusters:", logger) : null;
+ IndefiniteProgress cprogress = LOG.isVerbose() ? new IndefiniteProgress("Current number of clusters:", LOG) : null;
// TODO: Use DataStore and Trove for performance
Map<DBID, PROCLUSCluster> clusters = null;
int loops = 0;
while(loops < 10) {
- Map<DBID, Set<Integer>> dimensions = findDimensions(m_current, relation, distFunc, rangeQuery);
+ Map<DBID, TIntSet> dimensions = findDimensions(m_current, relation, distFunc, rangeQuery);
clusters = assignPoints(dimensions, relation);
double objectiveFunction = evaluateClusters(clusters, dimensions, relation);
@@ -214,20 +212,20 @@ public class PROCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClus
m_current = computeM_current(medoids, m_best, m_bad, random);
loops++;
if(cprogress != null) {
- cprogress.setProcessed(clusters.size(), logger);
+ cprogress.setProcessed(clusters.size(), LOG);
}
}
if(cprogress != null) {
- cprogress.setCompleted(logger);
+ cprogress.setCompleted(LOG);
}
// refinement phase
- if(logger.isVerbose()) {
- logger.verbose("3. Refinement phase...");
+ if(LOG.isVerbose()) {
+ LOG.verbose("3. Refinement phase...");
}
- List<Pair<V, Set<Integer>>> dimensions = findDimensions(new ArrayList<PROCLUSCluster>(clusters.values()), relation);
+ List<Pair<V, TIntSet>> dimensions = findDimensions(new ArrayList<PROCLUSCluster>(clusters.values()), relation);
List<PROCLUSCluster> finalClusters = finalAssignment(dimensions, relation);
// build result
@@ -235,7 +233,7 @@ public class PROCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClus
Clustering<SubspaceModel<V>> result = new Clustering<SubspaceModel<V>>("ProClus clustering", "proclus-clustering");
for(PROCLUSCluster c : finalClusters) {
Cluster<SubspaceModel<V>> cluster = new Cluster<SubspaceModel<V>>(c.objectIDs);
- cluster.setModel(new SubspaceModel<V>(new Subspace<V>(c.getDimensions()), c.centroid));
+ cluster.setModel(new SubspaceModel<V>(new Subspace(c.getDimensions()), c.centroid));
cluster.setName("cluster_" + numClusters++);
result.addCluster(cluster);
@@ -259,40 +257,41 @@ public class PROCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClus
// m_1 is random point of S
DBID m_i = s.remove(random.nextInt(s.size()));
medoids.add(m_i);
- if(logger.isDebugging()) {
- logger.debugFiner("medoids " + medoids);
+ if(LOG.isDebugging()) {
+ LOG.debugFiner("medoids " + medoids);
}
// compute distances between each point in S and m_i
- Map<DBID, DistanceResultPair<DoubleDistance>> distances = new HashMap<DBID, DistanceResultPair<DoubleDistance>>();
+ // FIXME: don't use maps, so we can work with DBIDRef
+ Map<DBID, DistanceDBIDPair<DoubleDistance>> distances = new HashMap<DBID, DistanceDBIDPair<DoubleDistance>>();
for(DBIDIter iter = s.iter(); iter.valid(); iter.advance()) {
- DBID id = iter.getDBID();
+ DBID id = DBIDUtil.deref(iter);
DoubleDistance dist = distFunc.distance(id, m_i);
- distances.put(id, new GenericDistanceResultPair<DoubleDistance>(dist, id));
+ distances.put(id, DBIDUtil.newDistancePair(dist, id));
}
for(int i = 1; i < m; i++) {
// choose medoid m_i to be far from prevois medoids
- List<DistanceResultPair<DoubleDistance>> d = new ArrayList<DistanceResultPair<DoubleDistance>>(distances.values());
- Collections.sort(d);
+ List<DistanceDBIDPair<DoubleDistance>> d = new ArrayList<DistanceDBIDPair<DoubleDistance>>(distances.values());
+ DistanceDBIDResultUtil.sortByDistance(d);
- m_i = d.get(d.size() - 1).getDBID();
+ m_i = DBIDUtil.deref(d.get(d.size() - 1));
medoids.add(m_i);
s.remove(m_i);
distances.remove(m_i);
// compute distances of each point to closest medoid
for(DBIDIter iter = s.iter(); iter.valid(); iter.advance()) {
- DBID id = iter.getDBID();
+ DBID id = DBIDUtil.deref(iter);
DoubleDistance dist_new = distFunc.distance(id, m_i);
DoubleDistance dist_old = distances.get(id).getDistance();
DoubleDistance dist = dist_new.compareTo(dist_old) < 0 ? dist_new : dist_old;
- distances.put(id, new GenericDistanceResultPair<DoubleDistance>(dist, id));
+ distances.put(id, DBIDUtil.newDistancePair(dist, id));
}
- if(logger.isDebugging()) {
- logger.debugFiner("medoids " + medoids);
+ if(LOG.isDebugging()) {
+ LOG.debugFiner("medoids " + medoids);
}
}
@@ -332,7 +331,7 @@ public class PROCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClus
ModifiableDBIDs m_current = DBIDUtil.newHashSet();
for(DBIDIter iter = m_best.iter(); iter.valid(); iter.advance()) {
- DBID m_i = iter.getDBID();
+ DBID m_i = DBIDUtil.deref(iter);
if(m_bad.contains(m_i)) {
int currentSize = m_current.size();
while(m_current.size() == currentSize) {
@@ -359,17 +358,17 @@ public class PROCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClus
* @param distFunc the distance function
* @return a mapping of the medoid's id to its locality
*/
- private Map<DBID, List<DistanceResultPair<DoubleDistance>>> getLocalities(DBIDs medoids, Relation<V> database, DistanceQuery<V, DoubleDistance> distFunc, RangeQuery<V, DoubleDistance> rangeQuery) {
- Map<DBID, List<DistanceResultPair<DoubleDistance>>> result = new HashMap<DBID, List<DistanceResultPair<DoubleDistance>>>();
+ private Map<DBID, DistanceDBIDResult<DoubleDistance>> getLocalities(DBIDs medoids, Relation<V> database, DistanceQuery<V, DoubleDistance> distFunc, RangeQuery<V, DoubleDistance> rangeQuery) {
+ Map<DBID, DistanceDBIDResult<DoubleDistance>> result = new HashMap<DBID, DistanceDBIDResult<DoubleDistance>>();
for(DBIDIter iter = medoids.iter(); iter.valid(); iter.advance()) {
- DBID m = iter.getDBID();
+ DBID m = DBIDUtil.deref(iter);
// determine minimum distance between current medoid m and any other
// medoid m_i
DoubleDistance minDist = null;
for(DBIDIter iter2 = medoids.iter(); iter2.valid(); iter2.advance()) {
- DBID m_i = iter2.getDBID();
- if(m_i == m) {
+ DBID m_i = DBIDUtil.deref(iter2);
+ if(DBIDUtil.equal(m_i, m)) {
continue;
}
DoubleDistance currentDist = distFunc.distance(m, m_i);
@@ -380,7 +379,7 @@ public class PROCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClus
// determine points in sphere centered at m with radius minDist
assert minDist != null;
- List<DistanceResultPair<DoubleDistance>> qr = rangeQuery.getRangeForDBID(m, minDist);
+ DistanceDBIDResult<DoubleDistance> qr = rangeQuery.getRangeForDBID(m, minDist);
result.put(m, qr);
}
@@ -397,23 +396,23 @@ public class PROCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClus
* @return the set of correlated dimensions for each medoid in the specified
* medoid set
*/
- private Map<DBID, Set<Integer>> findDimensions(DBIDs medoids, Relation<V> database, DistanceQuery<V, DoubleDistance> distFunc, RangeQuery<V, DoubleDistance> rangeQuery) {
+ private Map<DBID, TIntSet> findDimensions(DBIDs medoids, Relation<V> database, DistanceQuery<V, DoubleDistance> distFunc, RangeQuery<V, DoubleDistance> rangeQuery) {
// get localities
- Map<DBID, List<DistanceResultPair<DoubleDistance>>> localities = getLocalities(medoids, database, distFunc, rangeQuery);
+ Map<DBID, DistanceDBIDResult<DoubleDistance>> localities = getLocalities(medoids, database, distFunc, rangeQuery);
// compute x_ij = avg distance from points in l_i to medoid m_i
- int dim = DatabaseUtil.dimensionality(database);
+ int dim = RelationUtil.dimensionality(database);
Map<DBID, double[]> averageDistances = new HashMap<DBID, double[]>();
for(DBIDIter iter = medoids.iter(); iter.valid(); iter.advance()) {
- DBID m_i = iter.getDBID();
+ DBID m_i = DBIDUtil.deref(iter);
V medoid_i = database.get(m_i);
- List<DistanceResultPair<DoubleDistance>> l_i = localities.get(m_i);
+ DistanceDBIDResult<DoubleDistance> l_i = localities.get(m_i);
double[] x_i = new double[dim];
- for(DistanceResultPair<DoubleDistance> qr : l_i) {
- V o = database.get(qr.getDBID());
+ for(DBIDIter qr = l_i.iter(); qr.valid(); qr.advance()) {
+ V o = database.get(qr);
for(int d = 0; d < dim; d++) {
- x_i[d] += Math.abs(medoid_i.doubleValue(d + 1) - o.doubleValue(d + 1));
+ x_i[d] += Math.abs(medoid_i.doubleValue(d) - o.doubleValue(d));
}
}
for(int d = 0; d < dim; d++) {
@@ -422,11 +421,11 @@ public class PROCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClus
averageDistances.put(m_i, x_i);
}
- Map<DBID, Set<Integer>> dimensionMap = new HashMap<DBID, Set<Integer>>();
+ Map<DBID, TIntSet> dimensionMap = new HashMap<DBID, TIntSet>();
List<CTriple<Double, DBID, Integer>> z_ijs = new ArrayList<CTriple<Double, DBID, Integer>>();
for(DBIDIter iter = medoids.iter(); iter.valid(); iter.advance()) {
- DBID m_i = iter.getDBID();
- Set<Integer> dims_i = new HashSet<Integer>();
+ DBID m_i = DBIDUtil.deref(iter);
+ TIntSet dims_i = new TIntHashSet();
dimensionMap.put(m_i, dims_i);
double[] x_i = averageDistances.get(m_i);
@@ -447,7 +446,7 @@ public class PROCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClus
sigma_i = Math.sqrt(sigma_i);
for(int j = 0; j < dim; j++) {
- z_ijs.add(new CTriple<Double, DBID, Integer>((x_i[j] - y_i) / sigma_i, m_i, j + 1));
+ z_ijs.add(new CTriple<Double, DBID, Integer>((x_i[j] - y_i) / sigma_i, m_i, j));
}
}
Collections.sort(z_ijs);
@@ -455,15 +454,15 @@ public class PROCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClus
int max = Math.max(k * l, 2);
for(int m = 0; m < max; m++) {
CTriple<Double, DBID, Integer> z_ij = z_ijs.get(m);
- Set<Integer> dims_i = dimensionMap.get(z_ij.getSecond());
+ TIntSet dims_i = dimensionMap.get(z_ij.getSecond());
dims_i.add(z_ij.getThird());
- if(logger.isDebugging()) {
- StringBuffer msg = new StringBuffer();
- msg.append("\n");
- msg.append("z_ij ").append(z_ij).append("\n");
- msg.append("D_i ").append(dims_i).append("\n");
- logger.debugFiner(msg.toString());
+ if(LOG.isDebugging()) {
+ StringBuilder msg = new StringBuilder();
+ msg.append('\n');
+ msg.append("z_ij ").append(z_ij).append('\n');
+ msg.append("D_i ").append(dims_i).append('\n');
+ LOG.debugFiner(msg.toString());
}
}
return dimensionMap;
@@ -478,9 +477,9 @@ public class PROCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClus
* @return the set of correlated dimensions for each specified cluster
* centroid
*/
- private List<Pair<V, Set<Integer>>> findDimensions(List<PROCLUSCluster> clusters, Relation<V> database) {
+ private List<Pair<V, TIntSet>> findDimensions(List<PROCLUSCluster> clusters, Relation<V> database) {
// compute x_ij = avg distance from points in c_i to c_i.centroid
- int dim = DatabaseUtil.dimensionality(database);
+ int dim = RelationUtil.dimensionality(database);
Map<Integer, double[]> averageDistances = new HashMap<Integer, double[]>();
for(int i = 0; i < clusters.size(); i++) {
@@ -489,7 +488,7 @@ public class PROCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClus
for(DBIDIter iter = c_i.objectIDs.iter(); iter.valid(); iter.advance()) {
V o = database.get(iter);
for(int d = 0; d < dim; d++) {
- x_i[d] += Math.abs(c_i.centroid.doubleValue(d + 1) - o.doubleValue(d + 1));
+ x_i[d] += Math.abs(c_i.centroid.doubleValue(d) - o.doubleValue(d));
}
}
for(int d = 0; d < dim; d++) {
@@ -518,38 +517,38 @@ public class PROCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClus
sigma_i = Math.sqrt(sigma_i);
for(int j = 0; j < dim; j++) {
- z_ijs.add(new CTriple<Double, Integer, Integer>((x_i[j] - y_i) / sigma_i, i, j + 1));
+ z_ijs.add(new CTriple<Double, Integer, Integer>((x_i[j] - y_i) / sigma_i, i, j));
}
}
Collections.sort(z_ijs);
// mapping cluster index -> dimensions
- Map<Integer, Set<Integer>> dimensionMap = new HashMap<Integer, Set<Integer>>();
+ Map<Integer, TIntSet> dimensionMap = new HashMap<Integer, TIntSet>();
int max = Math.max(k * l, 2);
for(int m = 0; m < max; m++) {
CTriple<Double, Integer, Integer> z_ij = z_ijs.get(m);
- Set<Integer> dims_i = dimensionMap.get(z_ij.getSecond());
+ TIntSet dims_i = dimensionMap.get(z_ij.getSecond());
if(dims_i == null) {
- dims_i = new HashSet<Integer>();
+ dims_i = new TIntHashSet();
dimensionMap.put(z_ij.getSecond(), dims_i);
}
dims_i.add(z_ij.getThird());
- if(logger.isDebugging()) {
- StringBuffer msg = new StringBuffer();
- msg.append("\n");
- msg.append("z_ij ").append(z_ij).append("\n");
- msg.append("D_i ").append(dims_i).append("\n");
- logger.debugFiner(msg.toString());
+ if(LOG.isDebugging()) {
+ StringBuilder msg = new StringBuilder();
+ msg.append('\n');
+ msg.append("z_ij ").append(z_ij).append('\n');
+ msg.append("D_i ").append(dims_i).append('\n');
+ LOG.debugFiner(msg.toString());
}
}
// mapping cluster -> dimensions
- List<Pair<V, Set<Integer>>> result = new ArrayList<Pair<V, Set<Integer>>>();
+ List<Pair<V, TIntSet>> result = new ArrayList<Pair<V, TIntSet>>();
for(int i : dimensionMap.keySet()) {
- Set<Integer> dims_i = dimensionMap.get(i);
+ TIntSet dims_i = dimensionMap.get(i);
PROCLUSCluster c_i = clusters.get(i);
- result.add(new Pair<V, Set<Integer>>(c_i.centroid, dims_i));
+ result.add(new Pair<V, TIntSet>(c_i.centroid, dims_i));
}
return result;
}
@@ -562,26 +561,26 @@ public class PROCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClus
* @param database the database containing the objects
* @return the assignments of the object to the clusters
*/
- private Map<DBID, PROCLUSCluster> assignPoints(Map<DBID, Set<Integer>> dimensions, Relation<V> database) {
+ private Map<DBID, PROCLUSCluster> assignPoints(Map<DBID, TIntSet> dimensions, Relation<V> database) {
Map<DBID, ModifiableDBIDs> clusterIDs = new HashMap<DBID, ModifiableDBIDs>();
for(DBID m_i : dimensions.keySet()) {
clusterIDs.put(m_i, DBIDUtil.newHashSet());
}
for(DBIDIter it = database.iterDBIDs(); it.valid(); it.advance()) {
- DBID p_id = it.getDBID();
+ DBID p_id = DBIDUtil.deref(it);
V p = database.get(p_id);
- DistanceResultPair<DoubleDistance> minDist = null;
+ DistanceDBIDPair<DoubleDistance> minDist = null;
for(DBID m_i : dimensions.keySet()) {
V m = database.get(m_i);
- DistanceResultPair<DoubleDistance> currentDist = new GenericDistanceResultPair<DoubleDistance>(manhattanSegmentalDistance(p, m, dimensions.get(m_i)), m_i);
- if(minDist == null || currentDist.compareTo(minDist) < 0) {
+ DistanceDBIDPair<DoubleDistance> currentDist = DBIDUtil.newDistancePair(manhattanSegmentalDistance(p, m, dimensions.get(m_i)), m_i);
+ if(minDist == null || currentDist.compareByDistance(minDist) < 0) {
minDist = currentDist;
}
}
// add p to cluster with mindist
assert minDist != null;
- ModifiableDBIDs ids = clusterIDs.get(minDist.getDBID());
+ ModifiableDBIDs ids = clusterIDs.get(DBIDUtil.deref(minDist));
ids.add(p_id);
}
@@ -589,17 +588,17 @@ public class PROCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClus
for(DBID m_i : dimensions.keySet()) {
ModifiableDBIDs objectIDs = clusterIDs.get(m_i);
if(!objectIDs.isEmpty()) {
- Set<Integer> clusterDimensions = dimensions.get(m_i);
- V centroid = DatabaseUtil.centroid(database, objectIDs);
+ TIntSet clusterDimensions = dimensions.get(m_i);
+ V centroid = Centroid.make(database, objectIDs).toVector(database);
clusters.put(m_i, new PROCLUSCluster(objectIDs, clusterDimensions, centroid));
}
}
- if(logger.isDebugging()) {
- StringBuffer msg = new StringBuffer();
- msg.append("\n");
- msg.append("clusters ").append(clusters).append("\n");
- logger.debugFine(msg.toString());
+ if(LOG.isDebugging()) {
+ StringBuilder msg = new StringBuilder();
+ msg.append('\n');
+ msg.append("clusters ").append(clusters).append('\n');
+ LOG.debugFine(msg.toString());
}
return clusters;
}
@@ -612,20 +611,20 @@ public class PROCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClus
* @param database the database containing the objects
* @return the assignments of the object to the clusters
*/
- private List<PROCLUSCluster> finalAssignment(List<Pair<V, Set<Integer>>> dimensions, Relation<V> database) {
+ private List<PROCLUSCluster> finalAssignment(List<Pair<V, TIntSet>> dimensions, Relation<V> database) {
Map<Integer, ModifiableDBIDs> clusterIDs = new HashMap<Integer, ModifiableDBIDs>();
for(int i = 0; i < dimensions.size(); i++) {
clusterIDs.put(i, DBIDUtil.newHashSet());
}
for(DBIDIter it = database.iterDBIDs(); it.valid(); it.advance()) {
- DBID p_id = it.getDBID();
+ DBID p_id = DBIDUtil.deref(it);
V p = database.get(p_id);
Pair<DoubleDistance, Integer> minDist = null;
for(int i = 0; i < dimensions.size(); i++) {
- Pair<V, Set<Integer>> pair_i = dimensions.get(i);
+ Pair<V, TIntSet> pair_i = dimensions.get(i);
V c_i = pair_i.first;
- Set<Integer> dimensions_i = pair_i.second;
+ TIntSet dimensions_i = pair_i.second;
DoubleDistance currentDist = manhattanSegmentalDistance(p, c_i, dimensions_i);
if(minDist == null || currentDist.compareTo(minDist.first) < 0) {
minDist = new Pair<DoubleDistance, Integer>(currentDist, i);
@@ -641,17 +640,17 @@ public class PROCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClus
for(int i = 0; i < dimensions.size(); i++) {
ModifiableDBIDs objectIDs = clusterIDs.get(i);
if(!objectIDs.isEmpty()) {
- Set<Integer> clusterDimensions = dimensions.get(i).second;
- V centroid = DatabaseUtil.centroid(database, objectIDs);
+ TIntSet clusterDimensions = dimensions.get(i).second;
+ V centroid = Centroid.make(database, objectIDs).toVector(database);
clusters.add(new PROCLUSCluster(objectIDs, clusterDimensions, centroid));
}
}
- if(logger.isDebugging()) {
- StringBuffer msg = new StringBuffer();
- msg.append("\n");
- msg.append("clusters ").append(clusters).append("\n");
- logger.debugFine(msg.toString());
+ if(LOG.isDebugging()) {
+ StringBuilder msg = new StringBuilder();
+ msg.append('\n');
+ msg.append("clusters ").append(clusters).append('\n');
+ LOG.debugFine(msg.toString());
}
return clusters;
}
@@ -666,9 +665,10 @@ public class PROCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClus
* @return the Manhattan segmental distance between o1 and o2 relative to the
* specified dimensions
*/
- private DoubleDistance manhattanSegmentalDistance(V o1, V o2, Set<Integer> dimensions) {
+ private DoubleDistance manhattanSegmentalDistance(V o1, V o2, TIntSet dimensions) {
double result = 0;
- for(Integer d : dimensions) {
+ for (TIntIterator iter = dimensions.iterator(); iter.hasNext(); ) {
+ final int d = iter.next();
result += Math.abs(o1.doubleValue(d) - o2.doubleValue(d));
}
result /= dimensions.size();
@@ -683,15 +683,16 @@ public class PROCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClus
* @param database the database holding the objects
* @return a measure for the cluster quality
*/
- private double evaluateClusters(Map<DBID, PROCLUSCluster> clusters, Map<DBID, Set<Integer>> dimensions, Relation<V> database) {
+ private double evaluateClusters(Map<DBID, PROCLUSCluster> clusters, Map<DBID, TIntSet> dimensions, Relation<V> database) {
double result = 0;
for(DBID m_i : clusters.keySet()) {
PROCLUSCluster c_i = clusters.get(m_i);
V centroid_i = c_i.centroid;
- Set<Integer> dims_i = dimensions.get(m_i);
+ TIntSet dims_i = dimensions.get(m_i);
double w_i = 0;
- for(Integer j : dims_i) {
+ for (TIntIterator iter = dims_i.iterator(); iter.hasNext(); ) {
+ final int j = iter.next();
w_i += avgDistance(centroid_i, c_i.objectIDs, database, j);
}
@@ -714,12 +715,12 @@ public class PROCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClus
* specified dimension
*/
private double avgDistance(V centroid, DBIDs objectIDs, Relation<V> database, int dimension) {
- double avg = 0;
+ Mean avg = new Mean();
for(DBIDIter iter = objectIDs.iter(); iter.valid(); iter.advance()) {
V o = database.get(iter);
- avg += Math.abs(centroid.doubleValue(dimension) - o.doubleValue(dimension));
+ avg.put(Math.abs(centroid.doubleValue(dimension) - o.doubleValue(dimension)));
}
- return avg / objectIDs.size();
+ return avg.getMean();
}
/**
@@ -748,7 +749,7 @@ public class PROCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClus
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -765,7 +766,7 @@ public class PROCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClus
/**
* The correlated dimensions of this cluster.
*/
- Set<Integer> dimensions;
+ TIntSet dimensions;
/**
* The centroids of this cluster along each dimension.
@@ -779,7 +780,7 @@ public class PROCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClus
* @param dimensions the correlated dimensions of this cluster
* @param centroid the centroid of this cluster
*/
- public PROCLUSCluster(ModifiableDBIDs objectIDs, Set<Integer> dimensions, V centroid) {
+ public PROCLUSCluster(ModifiableDBIDs objectIDs, TIntSet dimensions, V centroid) {
this.objectIDs = objectIDs;
this.dimensions = dimensions;
this.centroid = centroid;
@@ -787,19 +788,19 @@ public class PROCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClus
@Override
public String toString() {
- StringBuffer result = new StringBuffer();
+ StringBuilder result = new StringBuilder();
result.append("Dimensions: [");
boolean notFirst = false;
- for(Integer d : dimensions) {
+ for(TIntIterator iter = dimensions.iterator(); iter.hasNext(); ) {
if(notFirst) {
- result.append(",");
+ result.append(',');
}
else {
notFirst = true;
}
- result.append(d);
+ result.append(iter.next());
}
- result.append("]");
+ result.append(']');
result.append("\nCentroid: ").append(centroid);
return result.toString();
@@ -812,8 +813,8 @@ public class PROCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClus
*/
public BitSet getDimensions() {
BitSet result = new BitSet();
- for(int d : dimensions) {
- result.set(d - 1);
+ for(TIntIterator iter = dimensions.iterator(); iter.hasNext(); ) {
+ result.set(iter.next());
}
return result;
}
@@ -826,10 +827,15 @@ public class PROCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClus
*
* @apiviz.exclude
*/
- public static class Parameterizer<V extends NumberVector<V, ?>> extends AbstractProjectedClustering.Parameterizer {
+ public static class Parameterizer<V extends NumberVector<?>> extends AbstractProjectedClustering.Parameterizer {
+ /**
+ * Parameter to specify the random generator seed.
+ */
+ public static final OptionID SEED_ID = new OptionID("proclus.seed", "The random number generator seed.");
+
protected int m_i = -1;
- protected Long seed = null;
+ protected RandomFactory rnd;
@Override
protected void makeOptions(Parameterization config) {
@@ -845,15 +851,15 @@ public class PROCLUS<V extends NumberVector<V, ?>> extends AbstractProjectedClus
m_i = m_iP.getValue();
}
- LongParameter seedP = new LongParameter(SEED_ID, true);
- if(config.grab(seedP)) {
- seed = seedP.getValue();
+ RandomParameter rndP = new RandomParameter(SEED_ID);
+ if(config.grab(rndP)) {
+ rnd = rndP.getValue();
}
}
@Override
protected PROCLUS<V> makeInstance() {
- return new PROCLUS<V>(k, k_i, l, m_i, seed);
+ return new PROCLUS<V>(k, k_i, l, m_i, rnd);
}
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/subspace/PreDeCon.java b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/subspace/PreDeCon.java
index 4ca5a564..fc3228eb 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/subspace/PreDeCon.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/subspace/PreDeCon.java
@@ -58,11 +58,11 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameteriz
@Title("PreDeCon: Subspace Preference weighted Density Connected Clustering")
@Description("PreDeCon computes clusters of subspace preference weighted connected points. " + "The algorithm searches for local subgroups of a set of feature vectors having " + "a low variance along one or more (but not all) attributes.")
@Reference(authors = "C. Böhm, K. Kailing, H.-P. Kriegel, P. Kröger", title = "Density Connected Clustering with Local Subspace Preferences", booktitle = "Proc. 4th IEEE Int. Conf. on Data Mining (ICDM'04), Brighton, UK, 2004", url = "http://dx.doi.org/10.1109/ICDM.2004.10087")
-public class PreDeCon<V extends NumberVector<V, ?>> extends AbstractProjectedDBSCAN<Clustering<Model>, V> {
+public class PreDeCon<V extends NumberVector<?>> extends AbstractProjectedDBSCAN<Clustering<Model>, V> {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(PreDeCon.class);
+ private static final Logging LOG = Logging.getLogger(PreDeCon.class);
/**
* Constructor.
@@ -88,7 +88,7 @@ public class PreDeCon<V extends NumberVector<V, ?>> extends AbstractProjectedDBS
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -98,7 +98,7 @@ public class PreDeCon<V extends NumberVector<V, ?>> extends AbstractProjectedDBS
*
* @apiviz.exclude
*/
- public static class Parameterizer<V extends NumberVector<V, ?>> extends AbstractProjectedDBSCAN.Parameterizer<V, DoubleDistance> {
+ public static class Parameterizer<V extends NumberVector<?>> extends AbstractProjectedDBSCAN.Parameterizer<V, DoubleDistance> {
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/subspace/SUBCLU.java b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/subspace/SUBCLU.java
index c47c74b6..46c5f0b8 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/subspace/SUBCLU.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/subspace/SUBCLU.java
@@ -42,12 +42,13 @@ import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
import de.lmu.ifi.dbs.elki.database.ProxyDatabase;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.distance.distancefunction.subspace.AbstractDimensionsSelectingDoubleDistanceFunction;
import de.lmu.ifi.dbs.elki.distance.distancefunction.subspace.SubspaceEuclideanDistanceFunction;
import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.logging.progress.StepProgress;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
+import de.lmu.ifi.dbs.elki.math.linearalgebra.Centroid;
import de.lmu.ifi.dbs.elki.utilities.documentation.Description;
import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
import de.lmu.ifi.dbs.elki.utilities.documentation.Title;
@@ -84,11 +85,11 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ObjectParameter;
@Title("SUBCLU: Density connected Subspace Clustering")
@Description("Algorithm to detect arbitrarily shaped and positioned clusters in subspaces. SUBCLU delivers for each subspace the same clusters DBSCAN would have found, when applied to this subspace seperately.")
@Reference(authors = "K. Kailing, H.-P. Kriegel, P. Kröger", title = "Density connected Subspace Clustering for High Dimensional Data. ", booktitle = "Proc. SIAM Int. Conf. on Data Mining (SDM'04), Lake Buena Vista, FL, 2004")
-public class SUBCLU<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Clustering<SubspaceModel<V>>> implements SubspaceClusteringAlgorithm<SubspaceModel<V>> {
+public class SUBCLU<V extends NumberVector<?>> extends AbstractAlgorithm<Clustering<SubspaceModel<V>>> implements SubspaceClusteringAlgorithm<SubspaceModel<V>> {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(SUBCLU.class);
+ private static final Logging LOG = Logging.getLogger(SUBCLU.class);
/**
* The distance function to determine the distance between database objects.
@@ -99,7 +100,7 @@ public class SUBCLU<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Clus
* Key: {@code -subclu.distancefunction}
* </p>
*/
- public static final OptionID DISTANCE_FUNCTION_ID = OptionID.getOrCreateOptionID("subclu.distancefunction", "Distance function to determine the distance between database objects.");
+ public static final OptionID DISTANCE_FUNCTION_ID = new OptionID("subclu.distancefunction", "Distance function to determine the distance between database objects.");
/**
* Parameter to specify the maximum radius of the neighborhood to be
@@ -109,7 +110,7 @@ public class SUBCLU<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Clus
* Key: {@code -subclu.epsilon}
* </p>
*/
- public static final OptionID EPSILON_ID = OptionID.getOrCreateOptionID("subclu.epsilon", "The maximum radius of the neighborhood to be considered.");
+ public static final OptionID EPSILON_ID = new OptionID("subclu.epsilon", "The maximum radius of the neighborhood to be considered.");
/**
* Parameter to specify the threshold for minimum number of points in the
@@ -118,7 +119,7 @@ public class SUBCLU<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Clus
* Key: {@code -subclu.minpts}
* </p>
*/
- public static final OptionID MINPTS_ID = OptionID.getOrCreateOptionID("subclu.minpts", "Threshold for minimum number of points in the epsilon-neighborhood of a point.");
+ public static final OptionID MINPTS_ID = new OptionID("subclu.minpts", "Threshold for minimum number of points in the epsilon-neighborhood of a point.");
/**
* Holds the instance of the distance function specified by
@@ -162,36 +163,36 @@ public class SUBCLU<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Clus
* @return Clustering result
*/
public Clustering<SubspaceModel<V>> run(Relation<V> relation) {
- final int dimensionality = DatabaseUtil.dimensionality(relation);
+ final int dimensionality = RelationUtil.dimensionality(relation);
- StepProgress stepprog = logger.isVerbose() ? new StepProgress(dimensionality) : null;
+ StepProgress stepprog = LOG.isVerbose() ? new StepProgress(dimensionality) : null;
// Generate all 1-dimensional clusters
if(stepprog != null) {
- stepprog.beginStep(1, "Generate all 1-dimensional clusters.", logger);
+ stepprog.beginStep(1, "Generate all 1-dimensional clusters.", LOG);
}
// mapping of dimensionality to set of subspaces
- HashMap<Integer, List<Subspace<V>>> subspaceMap = new HashMap<Integer, List<Subspace<V>>>();
+ HashMap<Integer, List<Subspace>> subspaceMap = new HashMap<Integer, List<Subspace>>();
// list of 1-dimensional subspaces containing clusters
- List<Subspace<V>> s_1 = new ArrayList<Subspace<V>>();
+ List<Subspace> s_1 = new ArrayList<Subspace>();
subspaceMap.put(0, s_1);
// mapping of subspaces to list of clusters
- TreeMap<Subspace<V>, List<Cluster<Model>>> clusterMap = new TreeMap<Subspace<V>, List<Cluster<Model>>>(new Subspace.DimensionComparator());
+ TreeMap<Subspace, List<Cluster<Model>>> clusterMap = new TreeMap<Subspace, List<Cluster<Model>>>(new Subspace.DimensionComparator());
for(int d = 0; d < dimensionality; d++) {
- Subspace<V> currentSubspace = new Subspace<V>(d);
+ Subspace currentSubspace = new Subspace(d);
List<Cluster<Model>> clusters = runDBSCAN(relation, null, currentSubspace);
- if(logger.isDebuggingFiner()) {
- StringBuffer msg = new StringBuffer();
- msg.append("\n").append(clusters.size()).append(" clusters in subspace ").append(currentSubspace.dimensonsToString()).append(": \n");
+ if(LOG.isDebuggingFiner()) {
+ StringBuilder msg = new StringBuilder();
+ msg.append('\n').append(clusters.size()).append(" clusters in subspace ").append(currentSubspace.dimensonsToString()).append(": \n");
for(Cluster<Model> cluster : clusters) {
msg.append(" " + cluster.getIDs() + "\n");
}
- logger.debugFiner(msg.toString());
+ LOG.debugFiner(msg.toString());
}
if(!clusters.isEmpty()) {
@@ -203,26 +204,26 @@ public class SUBCLU<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Clus
// Generate (d+1)-dimensional clusters from d-dimensional clusters
for(int d = 0; d < dimensionality - 1; d++) {
if(stepprog != null) {
- stepprog.beginStep(d + 2, "Generate " + (d + 2) + "-dimensional clusters from " + (d + 1) + "-dimensional clusters.", logger);
+ stepprog.beginStep(d + 2, "Generate " + (d + 2) + "-dimensional clusters from " + (d + 1) + "-dimensional clusters.", LOG);
}
- List<Subspace<V>> subspaces = subspaceMap.get(d);
+ List<Subspace> subspaces = subspaceMap.get(d);
if(subspaces == null || subspaces.isEmpty()) {
if(stepprog != null) {
for(int dim = d + 1; dim < dimensionality - 1; dim++) {
- stepprog.beginStep(dim + 2, "Generation of" + (dim + 2) + "-dimensional clusters not applicable, because no more " + (d + 2) + "-dimensional subspaces found.", logger);
+ stepprog.beginStep(dim + 2, "Generation of" + (dim + 2) + "-dimensional clusters not applicable, because no more " + (d + 2) + "-dimensional subspaces found.", LOG);
}
}
break;
}
- List<Subspace<V>> candidates = generateSubspaceCandidates(subspaces);
- List<Subspace<V>> s_d = new ArrayList<Subspace<V>>();
+ List<Subspace> candidates = generateSubspaceCandidates(subspaces);
+ List<Subspace> s_d = new ArrayList<Subspace>();
- for(Subspace<V> candidate : candidates) {
- Subspace<V> bestSubspace = bestSubspace(subspaces, candidate, clusterMap);
- if(logger.isDebuggingFine()) {
- logger.debugFine("best subspace of " + candidate.dimensonsToString() + ": " + bestSubspace.dimensonsToString());
+ for(Subspace candidate : candidates) {
+ Subspace bestSubspace = bestSubspace(subspaces, candidate, clusterMap);
+ if(LOG.isDebuggingFine()) {
+ LOG.debugFine("best subspace of " + candidate.dimensonsToString() + ": " + bestSubspace.dimensonsToString());
}
List<Cluster<Model>> bestSubspaceClusters = clusterMap.get(bestSubspace);
@@ -234,13 +235,13 @@ public class SUBCLU<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Clus
}
}
- if(logger.isDebuggingFine()) {
- StringBuffer msg = new StringBuffer();
+ if(LOG.isDebuggingFine()) {
+ StringBuilder msg = new StringBuilder();
msg.append(clusters.size() + " cluster(s) in subspace " + candidate + ": \n");
for(Cluster<Model> c : clusters) {
msg.append(" " + c.getIDs() + "\n");
}
- logger.debugFine(msg.toString());
+ LOG.debugFine(msg.toString());
}
if(!clusters.isEmpty()) {
@@ -257,18 +258,18 @@ public class SUBCLU<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Clus
// build result
int numClusters = 1;
result = new Clustering<SubspaceModel<V>>("SUBCLU clustering", "subclu-clustering");
- for(Subspace<V> subspace : clusterMap.descendingKeySet()) {
+ for(Subspace subspace : clusterMap.descendingKeySet()) {
List<Cluster<Model>> clusters = clusterMap.get(subspace);
for(Cluster<Model> cluster : clusters) {
Cluster<SubspaceModel<V>> newCluster = new Cluster<SubspaceModel<V>>(cluster.getIDs());
- newCluster.setModel(new SubspaceModel<V>(subspace, DatabaseUtil.centroid(relation, cluster.getIDs())));
+ newCluster.setModel(new SubspaceModel<V>(subspace, Centroid.make(relation, cluster.getIDs()).toVector(relation)));
newCluster.setName("cluster_" + numClusters++);
result.addCluster(newCluster);
}
}
if(stepprog != null) {
- stepprog.setCompleted(logger);
+ stepprog.setCompleted(LOG);
}
return result;
}
@@ -294,7 +295,7 @@ public class SUBCLU<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Clus
* @param subspace the subspace to run DBSCAN on
* @return the clustering result of the DBSCAN run
*/
- private List<Cluster<Model>> runDBSCAN(Relation<V> relation, DBIDs ids, Subspace<V> subspace) {
+ private List<Cluster<Model>> runDBSCAN(Relation<V> relation, DBIDs ids, Subspace subspace) {
// distance function
distanceFunction.setSelectedDimensions(subspace.getDimensions());
@@ -309,8 +310,8 @@ public class SUBCLU<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Clus
DBSCAN<V, DoubleDistance> dbscan = new DBSCAN<V, DoubleDistance>(distanceFunction, epsilon, minpts);
// run DBSCAN
- if(logger.isVerbose()) {
- logger.verbose("\nRun DBSCAN on subspace " + subspace.dimensonsToString());
+ if(LOG.isVerbose()) {
+ LOG.verbose("\nRun DBSCAN on subspace " + subspace.dimensonsToString());
}
Clustering<Model> dbsres = dbscan.run(proxy);
@@ -332,8 +333,8 @@ public class SUBCLU<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Clus
* @param subspaces the {@code d}-dimensional subspaces
* @return the {@code d+1}-dimensional subspace candidates
*/
- private List<Subspace<V>> generateSubspaceCandidates(List<Subspace<V>> subspaces) {
- List<Subspace<V>> candidates = new ArrayList<Subspace<V>>();
+ private List<Subspace> generateSubspaceCandidates(List<Subspace> subspaces) {
+ List<Subspace> candidates = new ArrayList<Subspace>();
if(subspaces.isEmpty()) {
return candidates;
@@ -342,28 +343,28 @@ public class SUBCLU<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Clus
// Generate (d+1)-dimensional candidate subspaces
int d = subspaces.get(0).dimensionality();
- StringBuffer msgFine = new StringBuffer("\n");
- if(logger.isDebuggingFiner()) {
- msgFine.append("subspaces ").append(subspaces).append("\n");
+ StringBuilder msgFine = new StringBuilder("\n");
+ if(LOG.isDebuggingFiner()) {
+ msgFine.append("subspaces ").append(subspaces).append('\n');
}
for(int i = 0; i < subspaces.size(); i++) {
- Subspace<V> s1 = subspaces.get(i);
+ Subspace s1 = subspaces.get(i);
for(int j = i + 1; j < subspaces.size(); j++) {
- Subspace<V> s2 = subspaces.get(j);
- Subspace<V> candidate = s1.join(s2);
+ Subspace s2 = subspaces.get(j);
+ Subspace candidate = s1.join(s2);
if(candidate != null) {
- if(logger.isDebuggingFiner()) {
- msgFine.append("candidate: ").append(candidate.dimensonsToString()).append("\n");
+ if(LOG.isDebuggingFiner()) {
+ msgFine.append("candidate: ").append(candidate.dimensonsToString()).append('\n');
}
// prune irrelevant candidate subspaces
- List<Subspace<V>> lowerSubspaces = lowerSubspaces(candidate);
- if(logger.isDebuggingFiner()) {
- msgFine.append("lowerSubspaces: ").append(lowerSubspaces).append("\n");
+ List<Subspace> lowerSubspaces = lowerSubspaces(candidate);
+ if(LOG.isDebuggingFiner()) {
+ msgFine.append("lowerSubspaces: ").append(lowerSubspaces).append('\n');
}
boolean irrelevantCandidate = false;
- for(Subspace<V> s : lowerSubspaces) {
+ for(Subspace s : lowerSubspaces) {
if(!subspaces.contains(s)) {
irrelevantCandidate = true;
break;
@@ -376,16 +377,16 @@ public class SUBCLU<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Clus
}
}
- if(logger.isDebuggingFiner()) {
- logger.debugFiner(msgFine.toString());
+ if(LOG.isDebuggingFiner()) {
+ LOG.debugFiner(msgFine.toString());
}
- if(logger.isDebugging()) {
- StringBuffer msg = new StringBuffer();
+ if(LOG.isDebugging()) {
+ StringBuilder msg = new StringBuilder();
msg.append(d + 1).append("-dimensional candidate subspaces: ");
- for(Subspace<V> candidate : candidates) {
- msg.append(candidate.dimensonsToString()).append(" ");
+ for(Subspace candidate : candidates) {
+ msg.append(candidate.dimensonsToString()).append(' ');
}
- logger.debug(msg.toString());
+ LOG.debug(msg.toString());
}
return candidates;
@@ -398,19 +399,19 @@ public class SUBCLU<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Clus
* @param subspace the {@code d}-dimensional subspace
* @return a list of all {@code (d-1)}-dimensional subspaces
*/
- private List<Subspace<V>> lowerSubspaces(Subspace<V> subspace) {
+ private List<Subspace> lowerSubspaces(Subspace subspace) {
int dimensionality = subspace.dimensionality();
if(dimensionality <= 1) {
return null;
}
// order result according to the dimensions
- List<Subspace<V>> result = new ArrayList<Subspace<V>>();
+ List<Subspace> result = new ArrayList<Subspace>();
BitSet dimensions = subspace.getDimensions();
for(int dim = dimensions.nextSetBit(0); dim >= 0; dim = dimensions.nextSetBit(dim + 1)) {
BitSet newDimensions = (BitSet) dimensions.clone();
newDimensions.set(dim, false);
- result.add(new Subspace<V>(newDimensions));
+ result.add(new Subspace(newDimensions));
}
return result;
@@ -428,10 +429,10 @@ public class SUBCLU<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Clus
* -dimensional candidate with minimal number of objects in the
* cluster
*/
- private Subspace<V> bestSubspace(List<Subspace<V>> subspaces, Subspace<V> candidate, TreeMap<Subspace<V>, List<Cluster<Model>>> clusterMap) {
- Subspace<V> bestSubspace = null;
+ private Subspace bestSubspace(List<Subspace> subspaces, Subspace candidate, TreeMap<Subspace, List<Cluster<Model>>> clusterMap) {
+ Subspace bestSubspace = null;
- for(Subspace<V> subspace : subspaces) {
+ for(Subspace subspace : subspaces) {
int min = Integer.MAX_VALUE;
if(subspace.isSubspace(candidate)) {
@@ -456,7 +457,7 @@ public class SUBCLU<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Clus
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -466,7 +467,7 @@ public class SUBCLU<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Clus
*
* @apiviz.exclude
*/
- public static class Parameterizer<V extends NumberVector<V, ?>> extends AbstractParameterizer {
+ public static class Parameterizer<V extends NumberVector<?>> extends AbstractParameterizer {
protected int minpts = 0;
protected DoubleDistance epsilon = null;
@@ -486,7 +487,8 @@ public class SUBCLU<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Clus
epsilon = epsilonP.getValue();
}
- IntParameter minptsP = new IntParameter(MINPTS_ID, new GreaterConstraint(0));
+ IntParameter minptsP = new IntParameter(MINPTS_ID);
+ minptsP.addConstraint(new GreaterConstraint(0));
if(config.grab(minptsP)) {
minpts = minptsP.getValue();
}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/subspace/clique/CLIQUESubspace.java b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/subspace/clique/CLIQUESubspace.java
index eff71a35..6b22b233 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/subspace/clique/CLIQUESubspace.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/subspace/clique/CLIQUESubspace.java
@@ -46,7 +46,7 @@ import de.lmu.ifi.dbs.elki.utilities.pairs.Pair;
*
* @param <V> the type of NumberVector this subspace contains
*/
-public class CLIQUESubspace<V extends NumberVector<V, ?>> extends Subspace<V> {
+public class CLIQUESubspace<V extends NumberVector<?>> extends Subspace {
/**
* The dense units belonging to this subspace.
*/
@@ -103,14 +103,14 @@ public class CLIQUESubspace<V extends NumberVector<V, ?>> extends Subspace<V> {
*
* @return the clusters in this subspace and the corresponding cluster models
*/
- public List<Pair<Subspace<V>, ModifiableDBIDs>> determineClusters() {
- List<Pair<Subspace<V>, ModifiableDBIDs>> clusters = new ArrayList<Pair<Subspace<V>, ModifiableDBIDs>>();
+ public List<Pair<Subspace, ModifiableDBIDs>> determineClusters() {
+ List<Pair<Subspace, ModifiableDBIDs>> clusters = new ArrayList<Pair<Subspace, ModifiableDBIDs>>();
for(CLIQUEUnit<V> unit : getDenseUnits()) {
if(!unit.isAssigned()) {
ModifiableDBIDs cluster = DBIDUtil.newHashSet();
CLIQUESubspace<V> model = new CLIQUESubspace<V>(getDimensions());
- clusters.add(new Pair<Subspace<V>, ModifiableDBIDs>(model, cluster));
+ clusters.add(new Pair<Subspace, ModifiableDBIDs>(model, cluster));
dfs(unit, cluster, model);
}
}
@@ -151,8 +151,8 @@ public class CLIQUESubspace<V extends NumberVector<V, ?>> extends Subspace<V> {
* @param dim the dimension
* @return the left neighbor of the given unit in the specified dimension
*/
- public CLIQUEUnit<V> leftNeighbor(CLIQUEUnit<V> unit, Integer dim) {
- Interval i = unit.getInterval(dim);
+ public CLIQUEUnit<V> leftNeighbor(CLIQUEUnit<V> unit, int dim) {
+ Interval i = unit.getInterval(Integer.valueOf(dim));
for(CLIQUEUnit<V> u : getDenseUnits()) {
if(u.containsLeftNeighbor(i)) {
@@ -238,10 +238,10 @@ public class CLIQUESubspace<V extends NumberVector<V, ?>> extends Subspace<V> {
*/
@Override
public String toString(String pre) {
- StringBuffer result = new StringBuffer();
+ StringBuilder result = new StringBuilder();
result.append(super.toString(pre));
- result.append("\n").append(pre).append("Coverage: ").append(coverage);
- result.append("\n").append(pre).append("Units: " + "\n");
+ result.append('\n').append(pre).append("Coverage: ").append(coverage);
+ result.append('\n').append(pre).append("Units: " + "\n");
for(CLIQUEUnit<V> denseUnit : getDenseUnits()) {
result.append(pre).append(" ").append(denseUnit.toString()).append(" ").append(denseUnit.getIds().size()).append(" objects\n");
}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/subspace/clique/CLIQUEUnit.java b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/subspace/clique/CLIQUEUnit.java
index db687567..70f251c9 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/subspace/clique/CLIQUEUnit.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/subspace/clique/CLIQUEUnit.java
@@ -23,15 +23,15 @@ package de.lmu.ifi.dbs.elki.algorithm.clustering.subspace.clique;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.util.HashMap;
+import gnu.trove.map.hash.TIntObjectHashMap;
+
import java.util.Iterator;
-import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
import de.lmu.ifi.dbs.elki.data.Interval;
import de.lmu.ifi.dbs.elki.data.NumberVector;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.ids.HashSetModifiableDBIDs;
@@ -46,7 +46,7 @@ import de.lmu.ifi.dbs.elki.database.ids.ModifiableDBIDs;
*
* @param <V> the type of NumberVector this unit contains
*/
-public class CLIQUEUnit<V extends NumberVector<V, ?>> {
+public class CLIQUEUnit<V extends NumberVector<?>> {
/**
* The one-dimensional intervals of which this unit is build.
*/
@@ -56,7 +56,7 @@ public class CLIQUEUnit<V extends NumberVector<V, ?>> {
* Provides a mapping of particular dimensions to the intervals of which this
* unit is build.
*/
- private Map<Integer, Interval> dimensionToInterval;
+ private TIntObjectHashMap<Interval> dimensionToInterval;
/**
* The ids of the feature vectors this unit contains.
@@ -77,7 +77,7 @@ public class CLIQUEUnit<V extends NumberVector<V, ?>> {
public CLIQUEUnit(SortedSet<Interval> intervals, ModifiableDBIDs ids) {
this.intervals = intervals;
- dimensionToInterval = new HashMap<Integer, Interval>();
+ dimensionToInterval = new TIntObjectHashMap<Interval>();
for(Interval interval : intervals) {
dimensionToInterval.put(interval.getDimension(), interval);
}
@@ -96,7 +96,7 @@ public class CLIQUEUnit<V extends NumberVector<V, ?>> {
intervals = new TreeSet<Interval>();
intervals.add(interval);
- dimensionToInterval = new HashMap<Integer, Interval>();
+ dimensionToInterval = new TIntObjectHashMap<Interval>();
dimensionToInterval.put(interval.getDimension(), interval);
ids = DBIDUtil.newHashSet();
@@ -114,7 +114,7 @@ public class CLIQUEUnit<V extends NumberVector<V, ?>> {
*/
public boolean contains(V vector) {
for(Interval interval : intervals) {
- double value = vector.doubleValue(interval.getDimension() + 1);
+ final double value = vector.doubleValue(interval.getDimension());
if(interval.getMin() > value || value >= interval.getMax()) {
return false;
}
@@ -131,7 +131,7 @@ public class CLIQUEUnit<V extends NumberVector<V, ?>> {
* @return true, if this unit contains the specified feature vector, false
* otherwise
*/
- public boolean addFeatureVector(DBID id, V vector) {
+ public boolean addFeatureVector(DBIDRef id, V vector) {
if(contains(vector)) {
ids.add(id);
return true;
@@ -284,9 +284,9 @@ public class CLIQUEUnit<V extends NumberVector<V, ?>> {
*/
@Override
public String toString() {
- StringBuffer result = new StringBuffer();
+ StringBuilder result = new StringBuilder();
for(Interval interval : intervals) {
- result.append(interval).append(" ");
+ result.append(interval).append(' ');
}
return result.toString();
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/trivial/ByLabelClustering.java b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/trivial/ByLabelClustering.java
index ee42a59f..af8fb1ea 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/trivial/ByLabelClustering.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/trivial/ByLabelClustering.java
@@ -79,19 +79,19 @@ public class ByLabelClustering extends AbstractAlgorithm<Clustering<Model>> impl
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(ByLabelClustering.class);
+ private static final Logging LOG = Logging.getLogger(ByLabelClustering.class);
/**
* Flag to indicate that multiple cluster assignment is possible. If an
* assignment to multiple clusters is desired, the labels indicating the
* clusters need to be separated by blanks.
*/
- public static final OptionID MULTIPLE_ID = OptionID.getOrCreateOptionID("bylabelclustering.multiple", "Flag to indicate that only subspaces with large coverage " + "(i.e. the fraction of the database that is covered by the dense units) " + "are selected, the rest will be pruned.");
+ public static final OptionID MULTIPLE_ID = new OptionID("bylabelclustering.multiple", "Flag to indicate that only subspaces with large coverage " + "(i.e. the fraction of the database that is covered by the dense units) " + "are selected, the rest will be pruned.");
/**
* Pattern to recognize noise clusters by.
*/
- public static final OptionID NOISE_ID = OptionID.getOrCreateOptionID("bylabelclustering.noise", "Pattern to recognize noise classes by their label.");
+ public static final OptionID NOISE_ID = new OptionID("bylabelclustering.noise", "Pattern to recognize noise classes by their label.");
/**
* Holds the value of {@link #MULTIPLE_ID}.
@@ -226,7 +226,7 @@ public class ByLabelClustering extends AbstractAlgorithm<Clustering<Model>> impl
}
}
else {
- labelMap.put(label, id.getDBID());
+ labelMap.put(label, DBIDUtil.deref(id));
}
}
@@ -237,7 +237,7 @@ public class ByLabelClustering extends AbstractAlgorithm<Clustering<Model>> impl
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -260,7 +260,8 @@ public class ByLabelClustering extends AbstractAlgorithm<Clustering<Model>> impl
multiple = multipleF.getValue();
}
- PatternParameter noisepatP = new PatternParameter(NOISE_ID, true);
+ PatternParameter noisepatP = new PatternParameter(NOISE_ID);
+ noisepatP.setOptional(true);
if(config.grab(noisepatP)) {
noisepat = noisepatP.getValue();
}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/trivial/ByLabelHierarchicalClustering.java b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/trivial/ByLabelHierarchicalClustering.java
index 26bf525a..dfb7d37f 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/trivial/ByLabelHierarchicalClustering.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/trivial/ByLabelHierarchicalClustering.java
@@ -73,7 +73,7 @@ public class ByLabelHierarchicalClustering extends AbstractAlgorithm<Clustering<
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(ByLabelHierarchicalClustering.class);
+ private static final Logging LOG = Logging.getLogger(ByLabelHierarchicalClustering.class);
/**
* Constructor without parameters
@@ -178,7 +178,7 @@ public class ByLabelHierarchicalClustering extends AbstractAlgorithm<Clustering<
}
}
else {
- labelMap.put(label, id.getDBID());
+ labelMap.put(label, DBIDUtil.deref(id));
}
}
@@ -189,6 +189,6 @@ public class ByLabelHierarchicalClustering extends AbstractAlgorithm<Clustering<
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/trivial/ByModelClustering.java b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/trivial/ByModelClustering.java
index 90ca3625..2114ac16 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/trivial/ByModelClustering.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/trivial/ByModelClustering.java
@@ -65,12 +65,12 @@ public class ByModelClustering extends AbstractAlgorithm<Clustering<Model>> impl
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(ByModelClustering.class);
+ private static final Logging LOG = Logging.getLogger(ByModelClustering.class);
/**
* Pattern to recognize noise clusters with
*/
- public static final OptionID NOISE_ID = OptionID.getOrCreateOptionID("bymodel.noise", "Pattern to recognize noise models by their label.");
+ public static final OptionID NOISE_ID = new OptionID("bymodel.noise", "Pattern to recognize noise models by their label.");
/**
* Holds the value of {@link #NOISE_ID}.
@@ -133,7 +133,7 @@ public class ByModelClustering extends AbstractAlgorithm<Clustering<Model>> impl
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -149,7 +149,8 @@ public class ByModelClustering extends AbstractAlgorithm<Clustering<Model>> impl
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- PatternParameter noisepatP = new PatternParameter(NOISE_ID, true);
+ PatternParameter noisepatP = new PatternParameter(NOISE_ID);
+ noisepatP.setOptional(true);
if(config.grab(noisepatP)) {
noisepat = noisepatP.getValue();
}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/trivial/TrivialAllInOne.java b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/trivial/TrivialAllInOne.java
index 2e7d006d..eaa5d2b2 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/trivial/TrivialAllInOne.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/trivial/TrivialAllInOne.java
@@ -51,7 +51,7 @@ public class TrivialAllInOne extends AbstractAlgorithm<Clustering<Model>> implem
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(TrivialAllInOne.class);
+ private static final Logging LOG = Logging.getLogger(TrivialAllInOne.class);
/**
* Constructor, adhering to
@@ -76,6 +76,6 @@ public class TrivialAllInOne extends AbstractAlgorithm<Clustering<Model>> implem
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/trivial/TrivialAllNoise.java b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/trivial/TrivialAllNoise.java
index c497632c..dd0f94a5 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/clustering/trivial/TrivialAllNoise.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/clustering/trivial/TrivialAllNoise.java
@@ -50,7 +50,7 @@ public class TrivialAllNoise extends AbstractAlgorithm<Clustering<Model>> implem
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(TrivialAllNoise.class);
+ private static final Logging LOG = Logging.getLogger(TrivialAllNoise.class);
/**
* Constructor, adhering to
@@ -75,6 +75,6 @@ public class TrivialAllNoise extends AbstractAlgorithm<Clustering<Model>> implem
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/ABOD.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/ABOD.java
index 88a62e38..d52a81fd 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/ABOD.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/ABOD.java
@@ -25,13 +25,11 @@ package de.lmu.ifi.dbs.elki.algorithm.outlier;
import java.util.Collections;
import java.util.HashMap;
-import java.util.Iterator;
import de.lmu.ifi.dbs.elki.algorithm.AbstractDistanceBasedAlgorithm;
import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.data.type.TypeInformation;
import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
-import de.lmu.ifi.dbs.elki.database.Database;
import de.lmu.ifi.dbs.elki.database.QueryUtil;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreFactory;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreUtil;
@@ -42,13 +40,13 @@ import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
+import de.lmu.ifi.dbs.elki.database.ids.DoubleDBIDPair;
import de.lmu.ifi.dbs.elki.database.ids.ModifiableDBIDs;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
import de.lmu.ifi.dbs.elki.database.query.knn.KNNQuery;
-import de.lmu.ifi.dbs.elki.database.query.knn.KNNResult;
import de.lmu.ifi.dbs.elki.database.relation.MaterializedRelation;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
import de.lmu.ifi.dbs.elki.distance.similarityfunction.PrimitiveSimilarityFunction;
import de.lmu.ifi.dbs.elki.distance.similarityfunction.kernel.KernelMatrix;
@@ -66,11 +64,11 @@ import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
import de.lmu.ifi.dbs.elki.utilities.documentation.Title;
import de.lmu.ifi.dbs.elki.utilities.exceptions.AbortException;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterConstraint;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterEqualConstraint;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ObjectParameter;
-import de.lmu.ifi.dbs.elki.utilities.pairs.DoubleObjPair;
/**
* Angle-Based Outlier Detection
@@ -92,39 +90,39 @@ import de.lmu.ifi.dbs.elki.utilities.pairs.DoubleObjPair;
@Title("ABOD: Angle-Based Outlier Detection")
@Description("Outlier detection using variance analysis on angles, especially for high dimensional data sets.")
@Reference(authors = "H.-P. Kriegel, M. Schubert, and A. Zimek", title = "Angle-Based Outlier Detection in High-dimensional Data", booktitle = "Proc. 14th ACM SIGKDD Int. Conf. on Knowledge Discovery and Data Mining (KDD '08), Las Vegas, NV, 2008", url = "http://dx.doi.org/10.1145/1401890.1401946")
-public class ABOD<V extends NumberVector<V, ?>> extends AbstractDistanceBasedAlgorithm<V, DoubleDistance, OutlierResult> implements OutlierAlgorithm {
+public class ABOD<V extends NumberVector<?>> extends AbstractDistanceBasedAlgorithm<V, DoubleDistance, OutlierResult> implements OutlierAlgorithm {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(ABOD.class);
+ private static final Logging LOG = Logging.getLogger(ABOD.class);
/**
* Parameter for k, the number of neighbors used in kNN queries.
*/
- public static final OptionID K_ID = OptionID.getOrCreateOptionID("abod.k", "Parameter k for kNN queries.");
+ public static final OptionID K_ID = new OptionID("abod.k", "Parameter k for kNN queries.");
/**
* Parameter for sample size to be used in fast mode.
*/
- public static final OptionID FAST_SAMPLE_ID = OptionID.getOrCreateOptionID("abod.samplesize", "Sample size to enable fast mode.");
+ public static final OptionID FAST_SAMPLE_ID = new OptionID("abod.samplesize", "Sample size to enable fast mode.");
/**
* Parameter for the kernel function.
*/
- public static final OptionID KERNEL_FUNCTION_ID = OptionID.getOrCreateOptionID("abod.kernelfunction", "Kernel function to use.");
+ public static final OptionID KERNEL_FUNCTION_ID = new OptionID("abod.kernelfunction", "Kernel function to use.");
/**
* The preprocessor used to materialize the kNN neighborhoods.
*/
- public static final OptionID PREPROCESSOR_ID = OptionID.getOrCreateOptionID("abod.knnquery", "Processor to compute the kNN neighborhoods.");
+ public static final OptionID PREPROCESSOR_ID = new OptionID("abod.knnquery", "Processor to compute the kNN neighborhoods.");
/**
- * use alternate code below
+ * use alternate code below.
*/
- private static final boolean useRNDSample = false;
+ private static final boolean USE_RND_SAMPLE = false;
/**
- * k parameter
+ * k parameter.
*/
private int k;
@@ -134,10 +132,13 @@ public class ABOD<V extends NumberVector<V, ?>> extends AbstractDistanceBasedAlg
int sampleSize = 0;
/**
- * Store the configured Kernel version
+ * Store the configured Kernel version.
*/
private PrimitiveSimilarityFunction<? super V, DoubleDistance> primitiveKernelFunction;
+ /**
+ * Static DBID map.
+ */
private ArrayModifiableDBIDs staticids = null;
/**
@@ -173,41 +174,32 @@ public class ABOD<V extends NumberVector<V, ?>> extends AbstractDistanceBasedAlg
* Main part of the algorithm. Exact version.
*
* @param relation Relation to query
- * @param k k for kNN queries
* @return result
*/
- public OutlierResult getRanking(Relation<V> relation, int k) {
+ public OutlierResult getRanking(Relation<V> relation) {
// Fix a static set of IDs
staticids = DBIDUtil.newArray(relation.getDBIDs());
staticids.sort();
KernelMatrix kernelMatrix = new KernelMatrix(primitiveKernelFunction, relation, staticids);
- Heap<DoubleObjPair<DBID>> pq = new Heap<DoubleObjPair<DBID>>(relation.size(), Collections.reverseOrder());
+ Heap<DoubleDBIDPair> pq = new Heap<DoubleDBIDPair>(relation.size(), Collections.reverseOrder());
// preprocess kNN neighborhoods
- assert (k == this.k);
KNNQuery<V, DoubleDistance> knnQuery = QueryUtil.getKNNQuery(relation, getDistanceFunction(), k);
MeanVariance s = new MeanVariance();
- for(DBIDIter objKey = relation.iterDBIDs(); objKey.valid(); objKey.advance()) {
+ for (DBIDIter objKey = relation.iterDBIDs(); objKey.valid(); objKey.advance()) {
s.reset();
- // System.out.println("Processing: " +objKey);
KNNResult<DoubleDistance> neighbors = knnQuery.getKNNForDBID(objKey, k);
- Iterator<DistanceResultPair<DoubleDistance>> iter = neighbors.iterator();
- while(iter.hasNext()) {
- DistanceResultPair<DoubleDistance> key1 = iter.next();
- // Iterator iter2 = data.keyIterator();
- Iterator<DistanceResultPair<DoubleDistance>> iter2 = neighbors.iterator();
- // PriorityQueue best = new PriorityQueue(false, k);
- while(iter2.hasNext()) {
- DistanceResultPair<DoubleDistance> key2 = iter2.next();
- if(key2.sameDBID(key1) || key1.sameDBID(objKey) || key2.sameDBID(objKey)) {
+ for (DBIDIter key1 = neighbors.iter(); key1.valid(); key1.advance()) {
+ for (DBIDIter key2 = neighbors.iter(); key2.valid(); key2.advance()) {
+ if (DBIDUtil.equal(key2, key1) || DBIDUtil.equal(key1, objKey) || DBIDUtil.equal(key2, objKey)) {
continue;
}
double nenner = calcDenominator(kernelMatrix, objKey, key1, key2);
- if(nenner != 0) {
+ if (nenner != 0) {
double sqrtnenner = Math.sqrt(nenner);
double tmp = calcNumerator(kernelMatrix, objKey, key1, key2) / nenner;
s.put(tmp, 1 / sqrtnenner);
@@ -217,14 +209,14 @@ public class ABOD<V extends NumberVector<V, ?>> extends AbstractDistanceBasedAlg
}
// Sample variance probably would be correct, however the numerical
// instabilities can actually break ABOD here.
- pq.add(new DoubleObjPair<DBID>(s.getNaiveVariance(), objKey.getDBID()));
+ pq.add(DBIDUtil.newPair(s.getNaiveVariance(), objKey));
}
DoubleMinMax minmaxabod = new DoubleMinMax();
WritableDoubleDataStore abodvalues = DataStoreUtil.makeDoubleStorage(relation.getDBIDs(), DataStoreFactory.HINT_STATIC);
- for(DoubleObjPair<DBID> pair : pq) {
- abodvalues.putDouble(pair.getSecond(), pair.first);
- minmaxabod.put(pair.first);
+ for (DoubleDBIDPair pair : pq) {
+ abodvalues.putDouble(pair, pair.doubleValue());
+ minmaxabod.put(pair.doubleValue());
}
// Build result representation.
Relation<Double> scoreResult = new MaterializedRelation<Double>("Angle-based Outlier Degree", "abod-outlier", TypeUtil.DOUBLE, abodvalues, relation.getDBIDs());
@@ -236,11 +228,9 @@ public class ABOD<V extends NumberVector<V, ?>> extends AbstractDistanceBasedAlg
* Main part of the algorithm. Fast version.
*
* @param relation Relation to use
- * @param k k for kNN queries
- * @param sampleSize Sample size
* @return result
*/
- public OutlierResult getFastRanking(Relation<V> relation, int k, int sampleSize) {
+ public OutlierResult getFastRanking(Relation<V> relation) {
final DBIDs ids = relation.getDBIDs();
// Fix a static set of IDs
// TODO: add a DBIDUtil.ensureSorted?
@@ -249,92 +239,72 @@ public class ABOD<V extends NumberVector<V, ?>> extends AbstractDistanceBasedAlg
KernelMatrix kernelMatrix = new KernelMatrix(primitiveKernelFunction, relation, staticids);
- Heap<DoubleObjPair<DBID>> pq = new Heap<DoubleObjPair<DBID>>(relation.size(), Collections.reverseOrder());
+ Heap<DoubleDBIDPair> pq = new Heap<DoubleDBIDPair>(relation.size(), Collections.reverseOrder());
// get Candidate Ranking
- for(DBIDIter aKey = relation.iterDBIDs(); aKey.valid(); aKey.advance()) {
+ for (DBIDIter aKey = relation.iterDBIDs(); aKey.valid(); aKey.advance()) {
WritableDoubleDataStore dists = DataStoreUtil.makeDoubleStorage(ids, DataStoreFactory.HINT_TEMP | DataStoreFactory.HINT_HOT);
// determine kNearestNeighbors and pairwise distances
- Heap<DoubleObjPair<DBID>> nn;
- if(!useRNDSample) {
+ Heap<DoubleDBIDPair> nn;
+ if (!USE_RND_SAMPLE) {
nn = calcDistsandNN(relation, kernelMatrix, sampleSize, aKey, dists);
- }
- else {
+ } else {
// alternative:
nn = calcDistsandRNDSample(relation, kernelMatrix, sampleSize, aKey, dists);
}
// get normalization
double[] counter = calcFastNormalization(aKey, dists, staticids);
- // System.out.println(counter[0] + " " + counter2[0] + " " + counter[1] +
- // " " + counter2[1]);
// umsetzen von Pq zu list
ModifiableDBIDs neighbors = DBIDUtil.newArray(nn.size());
- while(!nn.isEmpty()) {
- neighbors.add(nn.remove().getSecond());
+ while (!nn.isEmpty()) {
+ neighbors.add(nn.poll());
}
// getFilter
double var = getAbofFilter(kernelMatrix, aKey, dists, counter[1], counter[0], neighbors);
- pq.add(new DoubleObjPair<DBID>(var, aKey.getDBID()));
- // System.out.println("prog "+(prog++));
+ pq.add(DBIDUtil.newPair(var, aKey));
}
// refine Candidates
- Heap<DoubleObjPair<DBID>> resqueue = new Heap<DoubleObjPair<DBID>>(k);
- // System.out.println(pq.size() + " objects ordered into candidate list.");
- // int v = 0;
+ Heap<DoubleDBIDPair> resqueue = new Heap<DoubleDBIDPair>(k);
MeanVariance s = new MeanVariance();
- while(!pq.isEmpty()) {
- if(resqueue.size() == k && pq.peek().first > resqueue.peek().first) {
+ while (!pq.isEmpty()) {
+ if (resqueue.size() == k && pq.peek().doubleValue() > resqueue.peek().doubleValue()) {
break;
}
// double approx = pq.peek().getFirst();
- DBID aKey = pq.remove().getSecond();
- // if(!result.isEmpty()) {
- // System.out.println("Best Candidate " + aKey+" : " + pq.firstPriority()
- // + " worst result: " + result.firstPriority());
- // } else {
- // System.out.println("Best Candidate " + aKey+" : " + pq.firstPriority()
- // + " worst result: " + Double.MAX_VALUE);
- // }
- // v++;
+ DBIDRef aKey = pq.poll();
s.reset();
- for(DBIDIter bKey = relation.iterDBIDs(); bKey.valid(); bKey.advance()) {
- if(bKey.sameDBID(aKey)) {
+ for (DBIDIter bKey = relation.iterDBIDs(); bKey.valid(); bKey.advance()) {
+ if (DBIDUtil.equal(bKey, aKey)) {
continue;
}
- for(DBIDIter cKey = relation.iterDBIDs(); cKey.valid(); cKey.advance()) {
- if(cKey.sameDBID(aKey)) {
+ for (DBIDIter cKey = relation.iterDBIDs(); cKey.valid(); cKey.advance()) {
+ if (DBIDUtil.equal(cKey, aKey)) {
continue;
}
// double nenner = dists[y]*dists[z];
double nenner = calcDenominator(kernelMatrix, aKey, bKey, cKey);
- if(nenner != 0) {
+ if (nenner != 0) {
double tmp = calcNumerator(kernelMatrix, aKey, bKey, cKey) / nenner;
double sqrtNenner = Math.sqrt(nenner);
s.put(tmp, 1 / sqrtNenner);
}
}
}
- // System.out.println( aKey + "Sum " + sum + " SQRSum " +sqrSum +
- // " Counter " + counter);
double var = s.getSampleVariance();
- // System.out.println(aKey+ " : " + approx +" " + var);
- if(resqueue.size() < k) {
- resqueue.add(new DoubleObjPair<DBID>(var, aKey));
- }
- else {
- if(resqueue.peek().first > var) {
- resqueue.remove();
- resqueue.add(new DoubleObjPair<DBID>(var, aKey));
+ if (resqueue.size() < k) {
+ resqueue.add(DBIDUtil.newPair(var, aKey));
+ } else {
+ if (resqueue.peek().doubleValue() > var) {
+ resqueue.replaceTopElement(DBIDUtil.newPair(var, aKey));
}
}
}
- // System.out.println(v + " Punkte von " + data.size() + " verfeinert !!");
DoubleMinMax minmaxabod = new DoubleMinMax();
WritableDoubleDataStore abodvalues = DataStoreUtil.makeDoubleStorage(ids, DataStoreFactory.HINT_STATIC);
- for(DoubleObjPair<DBID> pair : pq) {
- abodvalues.putDouble(pair.getSecond(), pair.first);
- minmaxabod.put(pair.first);
+ for (DoubleDBIDPair pair : pq) {
+ abodvalues.putDouble(pair, pair.doubleValue());
+ minmaxabod.put(pair.doubleValue());
}
// Build result representation.
Relation<Double> scoreResult = new MaterializedRelation<Double>("Angle-based Outlier Detection", "abod-outlier", TypeUtil.DOUBLE, abodvalues, ids);
@@ -348,7 +318,7 @@ public class ABOD<V extends NumberVector<V, ?>> extends AbstractDistanceBasedAlg
double sum = 0;
double sumF = 0;
for (DBIDIter yKey = ids.iter(); yKey.valid(); yKey.advance()) {
- if(dists.doubleValue(yKey) != 0) {
+ if (dists.doubleValue(yKey) != 0) {
double tmp = 1 / Math.sqrt(dists.doubleValue(yKey));
sum += tmp;
sumF += (1 / dists.doubleValue(yKey)) * tmp;
@@ -357,7 +327,7 @@ public class ABOD<V extends NumberVector<V, ?>> extends AbstractDistanceBasedAlg
double sofar = 0;
double sofarF = 0;
for (DBIDIter zKey = ids.iter(); zKey.valid(); zKey.advance()) {
- if(dists.doubleValue(zKey) != 0) {
+ if (dists.doubleValue(zKey) != 0) {
double tmp = 1 / Math.sqrt(dists.doubleValue(zKey));
sofar += tmp;
double rest = sum - sofar;
@@ -375,17 +345,17 @@ public class ABOD<V extends NumberVector<V, ?>> extends AbstractDistanceBasedAlg
double sum = 0.0;
double sqrSum = 0.0;
double partCounter = 0;
- for(DBIDIter bKey = neighbors.iter(); bKey.valid(); bKey.advance()) {
- if(bKey.sameDBID(aKey)) {
+ for (DBIDIter bKey = neighbors.iter(); bKey.valid(); bKey.advance()) {
+ if (DBIDUtil.equal(bKey, aKey)) {
continue;
}
- for(DBIDIter cKey = neighbors.iter(); cKey.valid(); cKey.advance()) {
- if(cKey.sameDBID(aKey)) {
+ for (DBIDIter cKey = neighbors.iter(); cKey.valid(); cKey.advance()) {
+ if (DBIDUtil.equal(cKey, aKey)) {
continue;
}
- if(bKey.compareDBID(cKey) > 0) {
+ if (DBIDUtil.compare(bKey, cKey) > 0) {
double nenner = dists.doubleValue(bKey) * dists.doubleValue(cKey);
- if(nenner != 0) {
+ if (nenner != 0) {
double tmp = calcNumerator(kernelMatrix, aKey, bKey, cKey) / nenner;
double sqrtNenner = Math.sqrt(nenner);
sum += tmp * (1 / sqrtNenner);
@@ -417,7 +387,7 @@ public class ABOD<V extends NumberVector<V, ?>> extends AbstractDistanceBasedAlg
private int mapDBID(DBIDRef aKey) {
// TODO: this is not the most efficient...
int off = staticids.binarySearch(aKey);
- if(off < 0) {
+ if (off < 0) {
throw new AbortException("Did not find id " + aKey.toString() + " in staticids. " + staticids.contains(aKey));
}
return off + 1;
@@ -434,33 +404,31 @@ public class ABOD<V extends NumberVector<V, ?>> extends AbstractDistanceBasedAlg
return (kernelMatrix.getDistance(ai, ai) + kernelMatrix.getDistance(bi, ci) - kernelMatrix.getDistance(ai, ci) - kernelMatrix.getDistance(ai, bi));
}
- private Heap<DoubleObjPair<DBID>> calcDistsandNN(Relation<V> data, KernelMatrix kernelMatrix, int sampleSize, DBIDRef aKey, WritableDoubleDataStore dists) {
- Heap<DoubleObjPair<DBID>> nn = new Heap<DoubleObjPair<DBID>>(sampleSize);
- for(DBIDIter bKey = data.iterDBIDs(); bKey.valid(); bKey.advance()) {
+ private Heap<DoubleDBIDPair> calcDistsandNN(Relation<V> data, KernelMatrix kernelMatrix, int sampleSize, DBIDRef aKey, WritableDoubleDataStore dists) {
+ Heap<DoubleDBIDPair> nn = new Heap<DoubleDBIDPair>(sampleSize);
+ for (DBIDIter bKey = data.iterDBIDs(); bKey.valid(); bKey.advance()) {
double val = calcCos(kernelMatrix, aKey, bKey);
dists.putDouble(bKey, val);
- if(nn.size() < sampleSize) {
- nn.add(new DoubleObjPair<DBID>(val, bKey.getDBID()));
- }
- else {
- if(val < nn.peek().first) {
- nn.remove();
- nn.add(new DoubleObjPair<DBID>(val, bKey.getDBID()));
+ if (nn.size() < sampleSize) {
+ nn.add(DBIDUtil.newPair(val, bKey));
+ } else {
+ if (val < nn.peek().doubleValue()) {
+ nn.replaceTopElement(DBIDUtil.newPair(val, bKey));
}
}
}
return nn;
}
- private Heap<DoubleObjPair<DBID>> calcDistsandRNDSample(Relation<V> data, KernelMatrix kernelMatrix, int sampleSize, DBIDRef aKey, WritableDoubleDataStore dists) {
- Heap<DoubleObjPair<DBID>> nn = new Heap<DoubleObjPair<DBID>>(sampleSize);
+ private Heap<DoubleDBIDPair> calcDistsandRNDSample(Relation<V> data, KernelMatrix kernelMatrix, int sampleSize, DBIDRef aKey, WritableDoubleDataStore dists) {
+ Heap<DoubleDBIDPair> nn = new Heap<DoubleDBIDPair>(sampleSize);
int step = (int) ((double) data.size() / (double) sampleSize);
int counter = 0;
- for(DBIDIter bKey = data.iterDBIDs(); bKey.valid(); bKey.advance()) {
+ for (DBIDIter bKey = data.iterDBIDs(); bKey.valid(); bKey.advance()) {
double val = calcCos(kernelMatrix, aKey, bKey);
dists.putDouble(bKey, val);
- if(counter % step == 0) {
- nn.add(new DoubleObjPair<DBID>(val, bKey.getDBID()));
+ if (counter % step == 0) {
+ nn.add(DBIDUtil.newPair(val, bKey));
}
counter++;
}
@@ -471,112 +439,108 @@ public class ABOD<V extends NumberVector<V, ?>> extends AbstractDistanceBasedAlg
* Get explanations for points in the database.
*
* @param data to get explanations for
+ * @return String explanation
*/
// TODO: this should be done by the result classes.
- public void getExplanations(Relation<V> data) {
+ public String getExplanations(Relation<V> data) {
KernelMatrix kernelMatrix = new KernelMatrix(primitiveKernelFunction, data, staticids);
// PQ for Outlier Ranking
- Heap<DoubleObjPair<DBID>> pq = new Heap<DoubleObjPair<DBID>>(data.size(), Collections.reverseOrder());
+ Heap<DoubleDBIDPair> pq = new Heap<DoubleDBIDPair>(data.size(), Collections.reverseOrder());
HashMap<DBID, DBIDs> explaintab = new HashMap<DBID, DBIDs>();
// test all objects
MeanVariance s = new MeanVariance(), s2 = new MeanVariance();
- for(DBIDIter objKey = data.iterDBIDs(); objKey.valid(); objKey.advance()) {
+ for (DBIDIter objKey = data.iterDBIDs(); objKey.valid(); objKey.advance()) {
s.reset();
// Queue for the best explanation
- Heap<DoubleObjPair<DBID>> explain = new Heap<DoubleObjPair<DBID>>();
+ Heap<DoubleDBIDPair> explain = new Heap<DoubleDBIDPair>();
// determine Object
// for each pair of other objects
for (DBIDIter key1 = data.iterDBIDs(); key1.valid(); key1.advance()) {
- // Collect Explanation Vectors
+ // Collect Explanation Vectors
s2.reset();
- if(objKey.sameDBID(key1)) {
+ if (DBIDUtil.equal(objKey, key1)) {
continue;
}
for (DBIDIter key2 = data.iterDBIDs(); key2.valid(); key2.advance()) {
- if(key2.sameDBID(key1) || objKey.sameDBID(key2)) {
+ if (DBIDUtil.equal(key2, key1) || DBIDUtil.equal(objKey, key2)) {
continue;
}
double nenner = calcDenominator(kernelMatrix, objKey, key1, key2);
- if(nenner != 0) {
+ if (nenner != 0) {
double tmp = calcNumerator(kernelMatrix, objKey, key1, key2) / nenner;
double sqr = Math.sqrt(nenner);
s2.put(tmp, 1 / sqr);
}
}
- explain.add(new DoubleObjPair<DBID>(s2.getSampleVariance(), key1.getDBID()));
+ explain.add(DBIDUtil.newPair(s2.getSampleVariance(), key1));
s.put(s2);
}
// build variance of the observed vectors
- pq.add(new DoubleObjPair<DBID>(s.getSampleVariance(), objKey.getDBID()));
+ pq.add(DBIDUtil.newPair(s.getSampleVariance(), objKey));
//
ModifiableDBIDs expList = DBIDUtil.newArray();
- expList.add(explain.remove().getSecond());
- while(!explain.isEmpty()) {
- DBID nextKey = explain.remove().getSecond();
- if(nextKey.sameDBID(objKey)) {
+ expList.add(explain.poll());
+ while (!explain.isEmpty()) {
+ DBIDRef nextKey = explain.poll();
+ if (DBIDUtil.equal(nextKey, objKey)) {
continue;
}
double max = Double.MIN_VALUE;
- for(DBIDIter exp = expList.iter(); exp.valid(); exp.advance()) {
- if(exp.sameDBID(objKey) || nextKey.sameDBID(exp)) {
+ for (DBIDIter exp = expList.iter(); exp.valid(); exp.advance()) {
+ if (DBIDUtil.equal(exp, objKey) || DBIDUtil.equal(nextKey, exp)) {
continue;
}
double nenner = Math.sqrt(calcCos(kernelMatrix, objKey, nextKey)) * Math.sqrt(calcCos(kernelMatrix, objKey, exp));
double angle = calcNumerator(kernelMatrix, objKey, nextKey, exp) / nenner;
max = Math.max(angle, max);
}
- if(max < 0.5) {
+ if (max < 0.5) {
expList.add(nextKey);
}
}
- explaintab.put(objKey.getDBID(), expList);
+ explaintab.put(DBIDUtil.deref(objKey), expList);
}
- System.out.println("--------------------------------------------");
- System.out.println("Result: ABOD");
+ StringBuilder buf = new StringBuilder();
+ buf.append("Result: ABOD\n");
int count = 0;
- while(!pq.isEmpty()) {
- if(count > 10) {
+ while (!pq.isEmpty()) {
+ if (count > 10) {
break;
}
- double factor = pq.peek().first;
- DBID key = pq.remove().getSecond();
- System.out.print(data.get(key) + " ");
- System.out.println(count + " Factor=" + factor + " " + key);
+ double factor = pq.peek().doubleValue();
+ DBIDRef key = pq.poll();
+ buf.append(data.get(key)).append(' ');
+ buf.append(count).append(" Factor=").append(factor).append(' ').append(key).append('\n');
DBIDs expList = explaintab.get(key);
- generateExplanation(data, key, expList);
+ generateExplanation(buf, data, key, expList);
count++;
}
- System.out.println("--------------------------------------------");
+ return buf.toString();
}
- private void generateExplanation(Relation<V> data, DBID key, DBIDs expList) {
+ private void generateExplanation(StringBuilder buf, Relation<V> data, DBIDRef key, DBIDs expList) {
Vector vect1 = data.get(key).getColumnVector();
- for(DBIDIter iter = expList.iter(); iter.valid(); iter.advance()) {
- System.out.println("Outlier: " + vect1);
+ for (DBIDIter iter = expList.iter(); iter.valid(); iter.advance()) {
+ buf.append("Outlier: ").append(vect1).append('\n');
Vector exp = data.get(iter).getColumnVector();
- System.out.println("Most common neighbor: " + exp);
+ buf.append("Most common neighbor: ").append(exp).append('\n');
// determine difference Vector
Vector vals = exp.minus(vect1);
- System.out.println(vals);
- // System.out.println(new FeatureVector(
- // "Diff-"+vect1.getPrimaryKey(),vals ));
+ buf.append(vals).append('\n');
}
- System.out.println();
}
/**
- * Run ABOD on the data set
+ * Run ABOD on the data set.
*
- * @param database
- * @param relation
+ * @param relation Relation to process
* @return Outlier detection result
*/
- public OutlierResult run(Database database, Relation<V> relation) {
- if(sampleSize > 0) {
- return getFastRanking(relation, k, sampleSize);
- }
- else {
- return getRanking(relation, k);
+ public OutlierResult run(Relation<V> relation) {
+ if (sampleSize > 0) {
+ return getFastRanking(relation);
+ } else {
+ return getRanking(relation);
}
}
@@ -587,7 +551,7 @@ public class ABOD<V extends NumberVector<V, ?>> extends AbstractDistanceBasedAlg
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -597,26 +561,38 @@ public class ABOD<V extends NumberVector<V, ?>> extends AbstractDistanceBasedAlg
*
* @apiviz.exclude
*/
- public static class Parameterizer<V extends NumberVector<V, ?>> extends AbstractDistanceBasedAlgorithm.Parameterizer<V, DoubleDistance> {
+ public static class Parameterizer<V extends NumberVector<?>> extends AbstractDistanceBasedAlgorithm.Parameterizer<V, DoubleDistance> {
+ /**
+ * k Parameter.
+ */
protected int k = 0;
+ /**
+ * Sample size.
+ */
protected int sampleSize = 0;
+ /**
+ * Distance function.
+ */
protected PrimitiveSimilarityFunction<V, DoubleDistance> primitiveKernelFunction = null;
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- final IntParameter kP = new IntParameter(K_ID, new GreaterEqualConstraint(1), 30);
- if(config.grab(kP)) {
+ final IntParameter kP = new IntParameter(K_ID, 30);
+ kP.addConstraint(new GreaterEqualConstraint(1));
+ if (config.grab(kP)) {
k = kP.getValue();
}
- final IntParameter sampleSizeP = new IntParameter(FAST_SAMPLE_ID, new GreaterEqualConstraint(1), true);
- if(config.grab(sampleSizeP)) {
+ final IntParameter sampleSizeP = new IntParameter(FAST_SAMPLE_ID);
+ sampleSizeP.addConstraint(new GreaterEqualConstraint(1));
+ sampleSizeP.setOptional(true);
+ if (config.grab(sampleSizeP)) {
sampleSize = sampleSizeP.getValue();
}
final ObjectParameter<PrimitiveSimilarityFunction<V, DoubleDistance>> param = new ObjectParameter<PrimitiveSimilarityFunction<V, DoubleDistance>>(KERNEL_FUNCTION_ID, PrimitiveSimilarityFunction.class, PolynomialKernelFunction.class);
- if(config.grab(param)) {
+ if (config.grab(param)) {
primitiveKernelFunction = param.instantiateClass(config);
}
}
@@ -626,4 +602,4 @@ public class ABOD<V extends NumberVector<V, ?>> extends AbstractDistanceBasedAlg
return new ABOD<V>(k, sampleSize, primitiveKernelFunction, distanceFunction);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/ALOCI.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/ALOCI.java
index 39c3db60..41da687f 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/ALOCI.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/ALOCI.java
@@ -36,10 +36,12 @@ import de.lmu.ifi.dbs.elki.database.datastore.DataStoreFactory;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreUtil;
import de.lmu.ifi.dbs.elki.database.datastore.WritableDoubleDataStore;
import de.lmu.ifi.dbs.elki.database.ids.ArrayModifiableDBIDs;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDArrayIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.relation.MaterializedRelation;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.distance.distancefunction.EuclideanDistanceFunction;
import de.lmu.ifi.dbs.elki.distance.distancefunction.NumberVectorDistanceFunction;
import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance;
@@ -51,6 +53,7 @@ import de.lmu.ifi.dbs.elki.result.outlier.OutlierResult;
import de.lmu.ifi.dbs.elki.result.outlier.OutlierScoreMeta;
import de.lmu.ifi.dbs.elki.result.outlier.QuotientOutlierScoreMeta;
import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
+import de.lmu.ifi.dbs.elki.utilities.RandomFactory;
import de.lmu.ifi.dbs.elki.utilities.documentation.Description;
import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
import de.lmu.ifi.dbs.elki.utilities.documentation.Title;
@@ -58,8 +61,8 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.LongParameter;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ObjectParameter;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.RandomParameter;
import de.lmu.ifi.dbs.elki.utilities.pairs.Pair;
/**
@@ -78,17 +81,19 @@ import de.lmu.ifi.dbs.elki.utilities.pairs.Pair;
* @author Jonathan von Brünken
* @author Erich Schubert
*
+ * @apiviz.composedOf ALOCIQuadTree
+ *
* @param <O> Object type
* @param <D> Distance type
*/
@Title("LOCI: Fast Outlier Detection Using the Local Correlation Integral")
@Description("Algorithm to compute outliers based on the Local Correlation Integral")
@Reference(authors = "S. Papadimitriou, H. Kitagawa, P. B. Gibbons, C. Faloutsos", title = "LOCI: Fast Outlier Detection Using the Local Correlation Integral", booktitle = "Proc. 19th IEEE Int. Conf. on Data Engineering (ICDE '03), Bangalore, India, 2003", url = "http://dx.doi.org/10.1109/ICDE.2003.1260802")
-public class ALOCI<O extends NumberVector<O, ?>, D extends NumberDistance<D, ?>> extends AbstractAlgorithm<OutlierResult> implements OutlierAlgorithm {
+public class ALOCI<O extends NumberVector<?>, D extends NumberDistance<D, ?>> extends AbstractAlgorithm<OutlierResult> implements OutlierAlgorithm {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(ALOCI.class);
+ private static final Logging LOG = Logging.getLogger(ALOCI.class);
/**
* Minimum size for a leaf.
@@ -108,7 +113,7 @@ public class ALOCI<O extends NumberVector<O, ?>, D extends NumberDistance<D, ?>>
/**
* Random generator
*/
- private Random random;
+ private RandomFactory rnd;
/**
* Distance function
@@ -122,20 +127,21 @@ public class ALOCI<O extends NumberVector<O, ?>, D extends NumberDistance<D, ?>>
* @param nmin Minimum neighborhood size
* @param alpha Alpha value
* @param g Number of grids to use
- * @param seed Random generator seed.
+ * @param rnd Random generator.
*/
- public ALOCI(NumberVectorDistanceFunction<D> distanceFunction, int nmin, int alpha, int g, Long seed) {
+ public ALOCI(NumberVectorDistanceFunction<D> distanceFunction, int nmin, int alpha, int g, RandomFactory rnd) {
super();
this.distFunc = distanceFunction;
this.nmin = nmin;
this.alpha = alpha;
this.g = g;
- this.random = (seed != null) ? new Random(seed) : new Random(0);
+ this.rnd = rnd;
}
public OutlierResult run(Database database, Relation<O> relation) {
- final int dim = DatabaseUtil.dimensionality(relation);
- FiniteProgress progressPreproc = logger.isVerbose() ? new FiniteProgress("Build aLOCI quadtress", g, logger) : null;
+ final int dim = RelationUtil.dimensionality(relation);
+ final Random random = rnd.getRandom();
+ FiniteProgress progressPreproc = LOG.isVerbose() ? new FiniteProgress("Build aLOCI quadtress", g, LOG) : null;
// Compute extend of dataset.
double[] min, max;
@@ -145,13 +151,13 @@ public class ALOCI<O extends NumberVector<O, ?>, D extends NumberDistance<D, ?>>
min = new double[dim];
max = new double[dim];
for(int i = 0; i < dim; i++) {
- min[i] = hbbs.first.doubleValue(i + 1);
- max[i] = hbbs.second.doubleValue(i + 1);
+ min[i] = hbbs.first.doubleValue(i);
+ max[i] = hbbs.second.doubleValue(i);
maxd = Math.max(maxd, max[i] - min[i]);
}
// Enlarge bounding box to have equal lengths.
for(int i = 0; i < dim; i++) {
- double diff = (maxd - (max[i] - min[i])) / 2;
+ double diff = (maxd - (max[i] - min[i])) * .5;
min[i] -= diff;
max[i] += diff;
}
@@ -163,7 +169,7 @@ public class ALOCI<O extends NumberVector<O, ?>, D extends NumberDistance<D, ?>>
ALOCIQuadTree qt = new ALOCIQuadTree(min, max, nshift, nmin, relation);
qts.add(qt);
if(progressPreproc != null) {
- progressPreproc.incrementProcessed(logger);
+ progressPreproc.incrementProcessed(LOG);
}
/*
* create the remaining g-1 shifted QuadTrees. This not clearly described in
@@ -178,19 +184,19 @@ public class ALOCI<O extends NumberVector<O, ?>, D extends NumberDistance<D, ?>>
qt = new ALOCIQuadTree(min, max, svec, nmin, relation);
qts.add(qt);
if(progressPreproc != null) {
- progressPreproc.incrementProcessed(logger);
+ progressPreproc.incrementProcessed(LOG);
}
}
if(progressPreproc != null) {
- progressPreproc.ensureCompleted(logger);
+ progressPreproc.ensureCompleted(LOG);
}
// aLOCI main loop: evaluate
- FiniteProgress progressLOCI = logger.isVerbose() ? new FiniteProgress("Compute aLOCI scores", relation.size(), logger) : null;
+ FiniteProgress progressLOCI = LOG.isVerbose() ? new FiniteProgress("Compute aLOCI scores", relation.size(), LOG) : null;
WritableDoubleDataStore mdef_norm = DataStoreUtil.makeDoubleStorage(relation.getDBIDs(), DataStoreFactory.HINT_STATIC);
DoubleMinMax minmax = new DoubleMinMax();
- for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
+ for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
final O obj = relation.get(iditer);
double maxmdefnorm = 0;
@@ -239,11 +245,11 @@ public class ALOCI<O extends NumberVector<O, ?>, D extends NumberDistance<D, ?>>
mdef_norm.putDouble(iditer, maxmdefnorm);
minmax.put(maxmdefnorm);
if(progressLOCI != null) {
- progressLOCI.incrementProcessed(logger);
+ progressLOCI.incrementProcessed(LOG);
}
}
if(progressLOCI != null) {
- progressLOCI.ensureCompleted(logger);
+ progressLOCI.ensureCompleted(LOG);
}
Relation<Double> scoreResult = new MaterializedRelation<Double>("aLOCI normalized MDEF", "aloci-mdef-outlier", TypeUtil.DOUBLE, mdef_norm, relation.getDBIDs());
OutlierScoreMeta scoreMeta = new QuotientOutlierScoreMeta(minmax.getMin(), minmax.getMax(), 0.0, Double.POSITIVE_INFINITY);
@@ -291,7 +297,7 @@ public class ALOCI<O extends NumberVector<O, ?>, D extends NumberDistance<D, ?>>
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
@Override
@@ -329,7 +335,7 @@ public class ALOCI<O extends NumberVector<O, ?>, D extends NumberDistance<D, ?>>
/**
* Relation indexed.
*/
- private Relation<? extends NumberVector<?, ?>> relation;
+ private Relation<? extends NumberVector<?>> relation;
/**
* Constructor.
@@ -340,7 +346,7 @@ public class ALOCI<O extends NumberVector<O, ?>, D extends NumberDistance<D, ?>>
* @param nmin Maximum size for a page to split
* @param relation Relation to index
*/
- public ALOCIQuadTree(double[] min, double[] max, double[] shift, int nmin, Relation<? extends NumberVector<?, ?>> relation) {
+ public ALOCIQuadTree(double[] min, double[] max, double[] shift, int nmin, Relation<? extends NumberVector<?>> relation) {
super();
assert (min.length <= 32) : "Quadtrees are only supported for up to 32 dimensions";
this.shift = shift;
@@ -386,11 +392,14 @@ public class ALOCI<O extends NumberVector<O, ?>, D extends NumberDistance<D, ?>>
// logger.warning(FormatUtil.format(lmin)+" "+FormatUtil.format(lmax)+" "+start+"->"+end+" "+(end-start));
// Hack: Check degenerate cases that won't split
if(dim == 0) {
- NumberVector<?, ?> first = relation.get(ids.get(start));
+ DBIDArrayIter iter = ids.iter();
+ iter.seek(start);
+ NumberVector<?> first = relation.get(iter);
+ iter.advance();
boolean degenerate = true;
- loop: for(int pos = start + 1; pos < end; pos++) {
- NumberVector<?, ?> other = relation.get(ids.get(pos));
- for(int d = 1; d <= lmin.length; d++) {
+ loop: for(; iter.getOffset() < end; iter.advance()) {
+ NumberVector<?> other = relation.get(iter);
+ for(int d = 0; d < lmin.length; d++) {
if(Math.abs(first.doubleValue(d) - other.doubleValue(d)) > 1E-15) {
degenerate = false;
break loop;
@@ -431,20 +440,23 @@ public class ALOCI<O extends NumberVector<O, ?>, D extends NumberDistance<D, ?>>
}
else {
// Partially sort data, by dimension dim < mid
- int spos = start, epos = end;
- while(spos < epos) {
- if(getShiftedDim(relation.get(ids.get(spos)), dim, level) <= .5) {
- spos++;
+ DBIDArrayIter siter = ids.iter(), eiter = ids.iter();
+ siter.seek(start);
+ eiter.seek(end - 1);
+ while(siter.getOffset() < eiter.getOffset()) {
+ if(getShiftedDim(relation.get(siter), dim, level) <= .5) {
+ siter.advance();
continue;
}
- if(getShiftedDim(relation.get(ids.get(epos - 1)), dim, level) > 0.5) {
- epos--;
+ if(getShiftedDim(relation.get(eiter), dim, level) > 0.5) {
+ eiter.retract();
continue;
}
- ids.swap(spos, epos - 1);
- spos++;
- epos--;
+ ids.swap(siter.getOffset(), eiter.getOffset() - 1);
+ siter.advance();
+ eiter.retract();
}
+ final int spos = siter.getOffset();
if(start < spos) {
final double tmp = lmax[dim];
lmax[dim] = lmax[dim] * .5 + lmin[dim] * .5;
@@ -468,8 +480,8 @@ public class ALOCI<O extends NumberVector<O, ?>, D extends NumberDistance<D, ?>>
* @param level Level (controls scaling/wraping!)
* @return Shifted position
*/
- private double getShiftedDim(NumberVector<?, ?> obj, int dim, int level) {
- double pos = obj.doubleValue(dim + 1) + shift[dim];
+ private double getShiftedDim(NumberVector<?> obj, int dim, int level) {
+ double pos = obj.doubleValue(dim) + shift[dim];
pos = (pos - min[dim]) / width[dim] * (1 + level);
return pos - Math.floor(pos);
}
@@ -482,7 +494,7 @@ public class ALOCI<O extends NumberVector<O, ?>, D extends NumberDistance<D, ?>>
* @param tlevel Target level
* @return Node
*/
- public Node findClosestNode(NumberVector<?, ?> vec, int tlevel) {
+ public Node findClosestNode(NumberVector<?> vec, int tlevel) {
Node cur = root;
for(int level = 0; level <= tlevel; level++) {
if(cur.children == null) {
@@ -637,26 +649,26 @@ public class ALOCI<O extends NumberVector<O, ?>, D extends NumberDistance<D, ?>>
*
* @apiviz.exclude
*/
- public static class Parameterizer<O extends NumberVector<O, ?>, D extends NumberDistance<D, ?>> extends AbstractParameterizer {
+ public static class Parameterizer<O extends NumberVector<?>, D extends NumberDistance<D, ?>> extends AbstractParameterizer {
/**
* Parameter to specify the minimum neighborhood size
*/
- public static final OptionID NMIN_ID = OptionID.getOrCreateOptionID("loci.nmin", "Minimum neighborhood size to be considered.");
+ public static final OptionID NMIN_ID = new OptionID("loci.nmin", "Minimum neighborhood size to be considered.");
/**
* Parameter to specify the averaging neighborhood scaling.
*/
- public static final OptionID ALPHA_ID = OptionID.getOrCreateOptionID("loci.alpha", "Scaling factor for averaging neighborhood");
+ public static final OptionID ALPHA_ID = new OptionID("loci.alpha", "Scaling factor for averaging neighborhood");
/**
* Parameter to specify the number of Grids to use.
*/
- public static final OptionID GRIDS_ID = OptionID.getOrCreateOptionID("loci.g", "The number of Grids to use.");
+ public static final OptionID GRIDS_ID = new OptionID("loci.g", "The number of Grids to use.");
/**
* Parameter to specify the seed to initialize Random.
*/
- public static final OptionID SEED_ID = OptionID.getOrCreateOptionID("loci.seed", "The seed to use for initializing Random.");
+ public static final OptionID SEED_ID = new OptionID("loci.seed", "The seed to use for initializing Random.");
/**
* Neighborhood minimum size
@@ -674,9 +686,9 @@ public class ALOCI<O extends NumberVector<O, ?>, D extends NumberDistance<D, ?>>
protected int g = 1;
/**
- * Random generator seed
+ * Random generator
*/
- protected Long seed = null;
+ protected RandomFactory rnd;
/**
* The distance function
@@ -702,9 +714,9 @@ public class ALOCI<O extends NumberVector<O, ?>, D extends NumberDistance<D, ?>>
this.g = g.getValue();
}
- final LongParameter seedP = new LongParameter(SEED_ID, true);
- if(config.grab(seedP)) {
- this.seed = seedP.getValue();
+ final RandomParameter rndP = new RandomParameter(SEED_ID);
+ if(config.grab(rndP)) {
+ this.rnd = rndP.getValue();
}
final IntParameter alphaP = new IntParameter(ALPHA_ID, 4);
@@ -718,7 +730,7 @@ public class ALOCI<O extends NumberVector<O, ?>, D extends NumberDistance<D, ?>>
@Override
protected ALOCI<O, D> makeInstance() {
- return new ALOCI<O, D>(distanceFunction, nmin, alpha, g, seed);
+ return new ALOCI<O, D>(distanceFunction, nmin, alpha, g, rnd);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/AbstractAggarwalYuOutlier.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/AbstractAggarwalYuOutlier.java
index 9c1a216a..2a4885dc 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/AbstractAggarwalYuOutlier.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/AbstractAggarwalYuOutlier.java
@@ -25,28 +25,26 @@ package de.lmu.ifi.dbs.elki.algorithm.outlier;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.Vector;
import de.lmu.ifi.dbs.elki.algorithm.AbstractAlgorithm;
import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.data.type.TypeInformation;
import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
import de.lmu.ifi.dbs.elki.database.ids.ArrayModifiableDBIDs;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
+import de.lmu.ifi.dbs.elki.database.ids.DoubleDBIDPair;
import de.lmu.ifi.dbs.elki.database.ids.HashSetModifiableDBIDs;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.result.outlier.OutlierResult;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterEqualConstraint;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
-import de.lmu.ifi.dbs.elki.utilities.pairs.DoubleObjPair;
import de.lmu.ifi.dbs.elki.utilities.pairs.IntIntPair;
/**
@@ -64,19 +62,11 @@ import de.lmu.ifi.dbs.elki.utilities.pairs.IntIntPair;
*
* @author Ahmed Hettab
* @author Erich Schubert
+ *
+ * @param <V> Vector type
*/
@Reference(authors = "C.C. Aggarwal, P. S. Yu", title = "Outlier detection for high dimensional data", booktitle = "Proc. ACM SIGMOD Int. Conf. on Management of Data (SIGMOD 2001), Santa Barbara, CA, 2001", url = "http://dx.doi.org/10.1145/375663.375668")
-public abstract class AbstractAggarwalYuOutlier<V extends NumberVector<?, ?>> extends AbstractAlgorithm<OutlierResult> implements OutlierAlgorithm {
- /**
- * OptionID for the grid size
- */
- public static final OptionID PHI_ID = OptionID.getOrCreateOptionID("ay.phi", "The number of equi-depth grid ranges to use in each dimension.");
-
- /**
- * OptionID for the target dimensionality
- */
- public static final OptionID K_ID = OptionID.getOrCreateOptionID("ay.k", "Subspace dimensionality to search for.");
-
+public abstract class AbstractAggarwalYuOutlier<V extends NumberVector<?>> extends AbstractAlgorithm<OutlierResult> implements OutlierAlgorithm {
/**
* Symbolic value for subspaces not in use.
*
@@ -86,7 +76,7 @@ public abstract class AbstractAggarwalYuOutlier<V extends NumberVector<?, ?>> ex
public static final int DONT_CARE = 0;
/**
- * The number of partitions for each dimension
+ * The number of partitions for each dimension.
*/
protected int phi;
@@ -112,33 +102,32 @@ public abstract class AbstractAggarwalYuOutlier<V extends NumberVector<?, ?>> ex
* Each attribute of data is divided into phi equi-depth ranges.<br />
* Each range contains a fraction f=1/phi of the records.
*
- * @param database
+ * @param relation Relation to process
* @return range map
*/
- protected ArrayList<ArrayList<DBIDs>> buildRanges(Relation<V> database) {
- final int dim = DatabaseUtil.dimensionality(database);
- final int size = database.size();
- final DBIDs allids = database.getDBIDs();
+ protected ArrayList<ArrayList<DBIDs>> buildRanges(Relation<V> relation) {
+ final int dim = RelationUtil.dimensionality(relation);
+ final int size = relation.size();
+ final DBIDs allids = relation.getDBIDs();
final ArrayList<ArrayList<DBIDs>> ranges = new ArrayList<ArrayList<DBIDs>>();
// Temporary projection storage of the database
- final ArrayList<ArrayList<DoubleObjPair<DBID>>> dbAxis = new ArrayList<ArrayList<DoubleObjPair<DBID>>>(dim);
+ final ArrayList<ArrayList<DoubleDBIDPair>> dbAxis = new ArrayList<ArrayList<DoubleDBIDPair>>(dim);
for(int i = 0; i < dim; i++) {
- ArrayList<DoubleObjPair<DBID>> axis = new ArrayList<DoubleObjPair<DBID>>(size);
+ ArrayList<DoubleDBIDPair> axis = new ArrayList<DoubleDBIDPair>(size);
dbAxis.add(i, axis);
}
// Project
for(DBIDIter iter = allids.iter(); iter.valid(); iter.advance()) {
- DBID id = iter.getDBID();
- final V obj = database.get(id);
- for(int d = 1; d <= dim; d++) {
- dbAxis.get(d - 1).add(new DoubleObjPair<DBID>(obj.doubleValue(d), id));
+ final V obj = relation.get(iter);
+ for(int d = 0; d < dim; d++) {
+ dbAxis.get(d).add(DBIDUtil.newPair(obj.doubleValue(d), iter));
}
}
// Split into cells
final double part = size * 1.0 / phi;
- for(int d = 1; d <= dim; d++) {
- ArrayList<DoubleObjPair<DBID>> axis = dbAxis.get(d - 1);
+ for(int d = 0; d < dim; d++) {
+ ArrayList<DoubleDBIDPair> axis = dbAxis.get(d);
Collections.sort(axis);
ArrayList<DBIDs> dimranges = new ArrayList<DBIDs>(phi + 1);
dimranges.add(allids);
@@ -150,7 +139,7 @@ public abstract class AbstractAggarwalYuOutlier<V extends NumberVector<?, ?>> ex
}
ArrayModifiableDBIDs currange = DBIDUtil.newArray(phi + 1);
for(int i = start; i < end; i++) {
- currange.add(axis.get(i).second);
+ currange.add(axis.get(i));
}
start = end;
dimranges.add(currange);
@@ -161,14 +150,15 @@ public abstract class AbstractAggarwalYuOutlier<V extends NumberVector<?, ?>> ex
}
/**
- * Method to calculate the sparsity coefficient of
+ * Method to calculate the sparsity coefficient of.
*
* @param setsize Size of subset
* @param dbsize Size of database
* @param k Dimensionality
+ * @param phi Phi parameter
* @return sparsity coefficient
*/
- protected double sparsity(final int setsize, final int dbsize, final int k) {
+ protected static double sparsity(final int setsize, final int dbsize, final int k, final double phi) {
// calculate sparsity c
final double f = 1. / phi;
final double fK = Math.pow(f, k);
@@ -177,16 +167,17 @@ public abstract class AbstractAggarwalYuOutlier<V extends NumberVector<?, ?>> ex
}
/**
- * Method to get the ids in the given subspace
+ * Method to get the ids in the given subspace.
*
- * @param subspace
+ * @param subspace Subspace to process
+ * @param ranges List of DBID ranges
* @return ids
*/
- protected DBIDs computeSubspace(Vector<IntIntPair> subspace, ArrayList<ArrayList<DBIDs>> ranges) {
- HashSetModifiableDBIDs ids = DBIDUtil.newHashSet(ranges.get(subspace.get(0).first - 1).get(subspace.get(0).second));
+ protected DBIDs computeSubspace(ArrayList<IntIntPair> subspace, ArrayList<ArrayList<DBIDs>> ranges) {
+ HashSetModifiableDBIDs ids = DBIDUtil.newHashSet(ranges.get(subspace.get(0).first).get(subspace.get(0).second));
// intersect all selected dimensions
for(int i = 1; i < subspace.size(); i++) {
- DBIDs current = ranges.get(subspace.get(i).first - 1).get(subspace.get(i).second);
+ DBIDs current = ranges.get(subspace.get(i).first).get(subspace.get(i).second);
ids.retainAll(current);
if(ids.size() == 0) {
break;
@@ -226,19 +217,37 @@ public abstract class AbstractAggarwalYuOutlier<V extends NumberVector<?, ?>> ex
*
* @apiviz.exclude
*/
- public static abstract class Parameterizer extends AbstractParameterizer {
- protected Integer phi;
+ public abstract static class Parameterizer extends AbstractParameterizer {
+ /**
+ * OptionID for the grid size.
+ */
+ public static final OptionID PHI_ID = new OptionID("ay.phi", "The number of equi-depth grid ranges to use in each dimension.");
+
+ /**
+ * OptionID for the target dimensionality.
+ */
+ public static final OptionID K_ID = new OptionID("ay.k", "Subspace dimensionality to search for.");
+
+ /**
+ * Phi parameter.
+ */
+ protected int phi;
- protected Integer k;
+ /**
+ * k Parameter.
+ */
+ protected int k;
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- final IntParameter kP = new IntParameter(K_ID, new GreaterEqualConstraint(2));
+ final IntParameter kP = new IntParameter(K_ID);
+ kP.addConstraint(new GreaterEqualConstraint(2));
if(config.grab(kP)) {
k = kP.getValue();
}
- final IntParameter phiP = new IntParameter(PHI_ID, new GreaterEqualConstraint(2));
+ final IntParameter phiP = new IntParameter(PHI_ID);
+ phiP.addConstraint(new GreaterEqualConstraint(2));
if(config.grab(phiP)) {
phi = phiP.getValue();
}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/AbstractDBOutlier.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/AbstractDBOutlier.java
index a5ccce3a..0e6f502a 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/AbstractDBOutlier.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/AbstractDBOutlier.java
@@ -27,7 +27,7 @@ import de.lmu.ifi.dbs.elki.algorithm.AbstractDistanceBasedAlgorithm;
import de.lmu.ifi.dbs.elki.data.type.TypeInformation;
import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
import de.lmu.ifi.dbs.elki.database.Database;
-import de.lmu.ifi.dbs.elki.database.datastore.DataStore;
+import de.lmu.ifi.dbs.elki.database.datastore.DoubleDataStore;
import de.lmu.ifi.dbs.elki.database.relation.MaterializedRelation;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
@@ -56,7 +56,7 @@ public abstract class AbstractDBOutlier<O, D extends Distance<D>> extends Abstra
/**
* Parameter to specify the size of the D-neighborhood
*/
- public static final OptionID D_ID = OptionID.getOrCreateOptionID("dbod.d", "size of the D-neighborhood");
+ public static final OptionID D_ID = new OptionID("dbod.d", "size of the D-neighborhood");
/**
* Holds the value of {@link #D_ID}.
@@ -83,7 +83,7 @@ public abstract class AbstractDBOutlier<O, D extends Distance<D>> extends Abstra
*/
public OutlierResult run(Database database, Relation<O> relation) {
// Run the actual score process
- DataStore<Double> dbodscore = computeOutlierScores(database, relation, d);
+ DoubleDataStore dbodscore = computeOutlierScores(database, relation, d);
// Build result representation.
Relation<Double> scoreResult = new MaterializedRelation<Double>("Density-Based Outlier Detection", "db-outlier", TypeUtil.DOUBLE, dbodscore, relation.getDBIDs());
@@ -99,7 +99,7 @@ public abstract class AbstractDBOutlier<O, D extends Distance<D>> extends Abstra
* @param d distance
* @return computed scores
*/
- protected abstract DataStore<Double> computeOutlierScores(Database database, Relation<O> relation, D d);
+ protected abstract DoubleDataStore computeOutlierScores(Database database, Relation<O> relation, D d);
@Override
public TypeInformation[] getInputTypeRestriction() {
@@ -113,7 +113,7 @@ public abstract class AbstractDBOutlier<O, D extends Distance<D>> extends Abstra
*
* @apiviz.exclude
*/
- public static abstract class Parameterizer<O, D extends Distance<D>> extends AbstractDistanceBasedAlgorithm.Parameterizer<O, D> {
+ public abstract static class Parameterizer<O, D extends Distance<D>> extends AbstractDistanceBasedAlgorithm.Parameterizer<O, D> {
/**
* Query radius
*/
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/AggarwalYuEvolutionary.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/AggarwalYuEvolutionary.java
index 1d02e865..c263cdfa 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/AggarwalYuEvolutionary.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/AggarwalYuEvolutionary.java
@@ -37,18 +37,18 @@ import de.lmu.ifi.dbs.elki.database.Database;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreFactory;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreUtil;
import de.lmu.ifi.dbs.elki.database.datastore.WritableDoubleDataStore;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.relation.MaterializedRelation;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.math.DoubleMinMax;
import de.lmu.ifi.dbs.elki.result.outlier.InvertedOutlierScoreMeta;
import de.lmu.ifi.dbs.elki.result.outlier.OutlierResult;
import de.lmu.ifi.dbs.elki.result.outlier.OutlierScoreMeta;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.utilities.FormatUtil;
+import de.lmu.ifi.dbs.elki.utilities.RandomFactory;
import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.TopBoundedHeap;
import de.lmu.ifi.dbs.elki.utilities.documentation.Description;
import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
@@ -58,7 +58,7 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterEqualConstraint;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.LongParameter;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.RandomParameter;
import de.lmu.ifi.dbs.elki.utilities.pairs.FCPair;
import de.lmu.ifi.dbs.elki.utilities.pairs.Pair;
@@ -85,40 +85,26 @@ import de.lmu.ifi.dbs.elki.utilities.pairs.Pair;
@Title("EAFOD: the evolutionary outlier detection algorithm")
@Description("Outlier detection for high dimensional data")
@Reference(authors = "C.C. Aggarwal, P. S. Yu", title = "Outlier detection for high dimensional data", booktitle = "Proc. ACM SIGMOD Int. Conf. on Management of Data (SIGMOD 2001), Santa Barbara, CA, 2001", url = "http://dx.doi.org/10.1145/375663.375668")
-public class AggarwalYuEvolutionary<V extends NumberVector<?, ?>> extends AbstractAggarwalYuOutlier<V> {
+public class AggarwalYuEvolutionary<V extends NumberVector<?>> extends AbstractAggarwalYuOutlier<V> {
/**
* The logger for this class.
*/
- protected static final Logging logger = Logging.getLogger(AggarwalYuEvolutionary.class);
-
- /**
- * Parameter to specify the number of solutions must be an integer greater
- * than 1.
- * <p>
- * Key: {@code -eafod.m}
- * </p>
- */
- public static final OptionID M_ID = OptionID.getOrCreateOptionID("ay.m", "Population size for evolutionary algorithm.");
-
- /**
- * Parameter to specify the random generator seed.
- */
- public static final OptionID SEED_ID = OptionID.getOrCreateOptionID("ay.seed", "The random number generator seed.");
+ private static final Logging LOG = Logging.getLogger(AggarwalYuEvolutionary.class);
/**
* Maximum iteration count for evolutionary search.
*/
- protected final int MAX_ITERATIONS = 1000;
+ protected final static int MAX_ITERATIONS = 1000;
/**
- * Holds the value of {@link #M_ID}.
+ * Holds the value of {@link Parameterizer#M_ID}.
*/
private int m;
/**
- * Holds the value of {@link #SEED_ID}.
+ * Random generator.
*/
- private Long seed;
+ private RandomFactory rnd;
/**
* Constructor.
@@ -126,12 +112,12 @@ public class AggarwalYuEvolutionary<V extends NumberVector<?, ?>> extends Abstra
* @param k K
* @param phi Phi
* @param m M
- * @param seed Seed
+ * @param rnd Random generator
*/
- public AggarwalYuEvolutionary(int k, int phi, int m, Long seed) {
+ public AggarwalYuEvolutionary(int k, int phi, int m, RandomFactory rnd) {
super(k, phi);
this.m = m;
- this.seed = seed;
+ this.rnd = rnd;
}
/**
@@ -145,27 +131,25 @@ public class AggarwalYuEvolutionary<V extends NumberVector<?, ?>> extends Abstra
final int dbsize = relation.size();
ArrayList<ArrayList<DBIDs>> ranges = buildRanges(relation);
- Collection<Individuum> individuums = (new EvolutionarySearch(relation, ranges, m, seed)).run();
+ Iterable<Individuum> individuums = (new EvolutionarySearch(relation, ranges, m, rnd.getRandom())).run();
WritableDoubleDataStore outlierScore = DataStoreUtil.makeDoubleStorage(relation.getDBIDs(), DataStoreFactory.HINT_HOT | DataStoreFactory.HINT_STATIC);
for(Individuum ind : individuums) {
DBIDs ids = computeSubspaceForGene(ind.getGene(), ranges);
- double sparsityC = sparsity(ids.size(), dbsize, k);
+ double sparsityC = sparsity(ids.size(), dbsize, k, phi);
for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
- DBID id = iter.getDBID();
- double prev = outlierScore.doubleValue(id);
+ double prev = outlierScore.doubleValue(iter);
if(Double.isNaN(prev) || sparsityC < prev) {
- outlierScore.putDouble(id, sparsityC);
+ outlierScore.putDouble(iter, sparsityC);
}
}
}
DoubleMinMax minmax = new DoubleMinMax();
for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
- double val = outlierScore.doubleValue(id);
+ double val = outlierScore.doubleValue(iditer);
if(Double.isNaN(val)) {
- outlierScore.putDouble(id, 0.0);
+ outlierScore.putDouble(iditer, 0.0);
val = 0.0;
}
minmax.put(val);
@@ -177,7 +161,7 @@ public class AggarwalYuEvolutionary<V extends NumberVector<?, ?>> extends Abstra
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -189,17 +173,17 @@ public class AggarwalYuEvolutionary<V extends NumberVector<?, ?>> extends Abstra
*/
private class EvolutionarySearch {
/**
- * Database size
+ * Database size.
*/
final int dbsize;
/**
- * Database dimensionality
+ * Database dimensionality.
*/
final int dim;
/**
- * Database ranges
+ * Database ranges.
*/
final ArrayList<ArrayList<DBIDs>> ranges;
@@ -209,36 +193,34 @@ public class AggarwalYuEvolutionary<V extends NumberVector<?, ?>> extends Abstra
final int m;
/**
- * random generator
+ * random generator.
*/
final private Random random;
/**
* Constructor.
*
- * @param database Database to use
+ * @param relation Database to use
+ * @param ranges DBID ranges to process
* @param m Population size
- * @param seed Random generator seed
+ * @param random Random generator
*/
- public EvolutionarySearch(Relation<V> database, ArrayList<ArrayList<DBIDs>> ranges, int m, Long seed) {
+ public EvolutionarySearch(Relation<V> relation, ArrayList<ArrayList<DBIDs>> ranges, int m, Random random) {
super();
this.ranges = ranges;
this.m = m;
- this.dbsize = database.size();
- this.dim = DatabaseUtil.dimensionality(database);
- if(seed != null) {
- this.random = new Random(seed);
- }
- else {
- this.random = new Random();
- }
+ this.dbsize = relation.size();
+ this.dim = RelationUtil.dimensionality(relation);
+ this.random = random;
}
- public Collection<Individuum> run() {
+ public Iterable<Individuum> run() {
ArrayList<Individuum> pop = initialPopulation(m);
// best Population
TopBoundedHeap<Individuum> bestSol = new TopBoundedHeap<Individuum>(m, Collections.reverseOrder());
- bestSol.addAll(pop);
+ for (Individuum ind : pop) {
+ bestSol.add(ind);
+ }
int iterations = 0;
while(!checkConvergence(pop)) {
@@ -249,26 +231,29 @@ public class AggarwalYuEvolutionary<V extends NumberVector<?, ?>> extends Abstra
// Mutation with probability 0.25 , 0.25
pop = mutation(pop, 0.5, 0.5);
// Avoid duplicates
- for(Individuum ind : pop) {
- if(!bestSol.contains(ind)) {
- bestSol.add(ind);
+ ind: for(Individuum ind : pop) {
+ for (Individuum b : bestSol) {
+ if (b.equals(ind)) {
+ continue ind;
+ }
}
+ bestSol.add(ind);
}
- if(logger.isDebuggingFinest()) {
- StringBuffer buf = new StringBuffer();
+ if(LOG.isDebuggingFinest()) {
+ StringBuilder buf = new StringBuilder();
buf.append("Top solutions:\n");
for(Individuum ind : bestSol) {
- buf.append(ind.toString()).append("\n");
+ buf.append(ind.toString()).append('\n');
}
buf.append("Population:\n");
for(Individuum ind : pop) {
- buf.append(ind.toString()).append("\n");
+ buf.append(ind.toString()).append('\n');
}
- logger.debugFinest(buf.toString());
+ LOG.debugFinest(buf.toString());
}
iterations++;
if(iterations > MAX_ITERATIONS) {
- logger.warning("Maximum iterations reached.");
+ LOG.warning("Maximum iterations reached.");
break;
}
}
@@ -276,7 +261,10 @@ public class AggarwalYuEvolutionary<V extends NumberVector<?, ?>> extends Abstra
}
/**
- * check the termination criterion
+ * check the termination criterion.
+ *
+ * @param pop Population
+ * @return Convergence
*/
private boolean checkConvergence(Collection<Individuum> pop) {
if(pop.size() == 0) {
@@ -291,7 +279,7 @@ public class AggarwalYuEvolutionary<V extends NumberVector<?, ?>> extends Abstra
for(int d = 0; d < dim; d++) {
int val = gene[d] + DONT_CARE;
if(val < 0 || val >= phi + 1) {
- logger.warning("Invalid gene value encountered: " + val + " in " + ind.toString());
+ LOG.warning("Invalid gene value encountered: " + val + " in " + ind.toString());
continue;
}
occur[d][val] += 1;
@@ -299,8 +287,8 @@ public class AggarwalYuEvolutionary<V extends NumberVector<?, ?>> extends Abstra
}
int conv = (int) (pop.size() * 0.95);
- if(logger.isDebuggingFine()) {
- logger.debugFine("Convergence at " + conv + " of " + pop.size() + " individuums.");
+ if(LOG.isDebuggingFine()) {
+ LOG.debugFine("Convergence at " + conv + " of " + pop.size() + " individuums.");
}
for(int d = 0; d < dim; d++) {
boolean converged = false;
@@ -353,18 +341,21 @@ public class AggarwalYuEvolutionary<V extends NumberVector<?, ?>> extends Abstra
}
/**
+ * Select surviving individuums weighted by rank.
+ *
* the selection criterion for the genetic algorithm: <br>
* roulette wheel mechanism: <br>
* where the probability of sampling an individual of the population was
* proportional to p - r(i), where p is the size of population and r(i) the
* rank of i-th individual
*
- * @param population
+ * @param population Population
+ * @return Survivors
*/
private ArrayList<Individuum> rouletteRankSelection(ArrayList<Individuum> population) {
final int popsize = population.size();
// Relative weight := popsize - position => sum(1..popsize)
- int totalweight = popsize * (popsize + 1) / 2;
+ int totalweight = (popsize * (popsize + 1)) >> 1;
// Survivors
ArrayList<Individuum> survivors = new ArrayList<Individuum>(popsize);
@@ -392,7 +383,7 @@ public class AggarwalYuEvolutionary<V extends NumberVector<?, ?>> extends Abstra
}
/**
- * method implements the mutation algorithm
+ * Apply the mutation alogrithm.
*/
private ArrayList<Individuum> mutation(ArrayList<Individuum> population, double perc1, double perc2) {
// the Mutations
@@ -470,7 +461,7 @@ public class AggarwalYuEvolutionary<V extends NumberVector<?, ?>> extends Abstra
*/
private Individuum makeIndividuum(int[] gene) {
final DBIDs ids = computeSubspaceForGene(gene, ranges);
- final double fitness = (ids.size() > 0) ? sparsity(ids.size(), dbsize, k) : Double.MAX_VALUE;
+ final double fitness = (ids.size() > 0) ? sparsity(ids.size(), dbsize, k, phi) : Double.MAX_VALUE;
return new Individuum(fitness, gene);
}
@@ -543,8 +534,8 @@ public class AggarwalYuEvolutionary<V extends NumberVector<?, ?>> extends Abstra
l1[next] = parent1.getGene()[next];
l2[next] = parent2.getGene()[next];
- final double sparsityL1 = sparsity(computeSubspaceForGene(l1, ranges).size(), dbsize, k);
- final double sparsityL2 = sparsity(computeSubspaceForGene(l2, ranges).size(), dbsize, k);
+ final double sparsityL1 = sparsity(computeSubspaceForGene(l1, ranges).size(), dbsize, k, phi);
+ final double sparsityL2 = sparsity(computeSubspaceForGene(l2, ranges).size(), dbsize, k, phi);
if(sparsityL1 <= sparsityL2) {
b = l1.clone();
@@ -619,6 +610,8 @@ public class AggarwalYuEvolutionary<V extends NumberVector<?, ?>> extends Abstra
* Individuum for the evolutionary search.
*
* @author Erich Schubert
+ *
+ * @apiviz.exclude de.lmu.ifi.dbs.elki.utilities.pairs.FCPair
*/
private static class Individuum extends FCPair<Double, int[]> {
/**
@@ -691,27 +684,42 @@ public class AggarwalYuEvolutionary<V extends NumberVector<?, ?>> extends Abstra
*
* @apiviz.exclude
*/
- public static class Parameterizer<V extends NumberVector<?, ?>> extends AbstractAggarwalYuOutlier.Parameterizer {
+ public static class Parameterizer<V extends NumberVector<?>> extends AbstractAggarwalYuOutlier.Parameterizer {
+ /**
+ * Parameter to specify the number of solutions must be an integer greater
+ * than 1.
+ * <p>
+ * Key: {@code -eafod.m}
+ * </p>
+ */
+ public static final OptionID M_ID = new OptionID("ay.m", "Population size for evolutionary algorithm.");
+
+ /**
+ * Parameter to specify the random generator seed.
+ */
+ public static final OptionID SEED_ID = new OptionID("ay.seed", "The random number generator seed.");
+
protected int m = 0;
- protected Long seed = null;
+ protected RandomFactory rnd;
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- final IntParameter mP = new IntParameter(M_ID, new GreaterEqualConstraint(2));
+ final IntParameter mP = new IntParameter(M_ID);
+ mP.addConstraint(new GreaterEqualConstraint(2));
if(config.grab(mP)) {
m = mP.getValue();
}
- final LongParameter seedP = new LongParameter(SEED_ID, true);
- if(config.grab(seedP)) {
- seed = seedP.getValue();
+ final RandomParameter rndP = new RandomParameter(SEED_ID);
+ if(config.grab(rndP)) {
+ rnd = rndP.getValue();
}
}
@Override
protected AggarwalYuEvolutionary<V> makeInstance() {
- return new AggarwalYuEvolutionary<V>(k, phi, m, seed);
+ return new AggarwalYuEvolutionary<V>(k, phi, m, rnd);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/AggarwalYuNaive.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/AggarwalYuNaive.java
index 0bb73aba..9cd7d79f 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/AggarwalYuNaive.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/AggarwalYuNaive.java
@@ -24,7 +24,6 @@ package de.lmu.ifi.dbs.elki.algorithm.outlier;
*/
import java.util.ArrayList;
-import java.util.Vector;
import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
@@ -35,12 +34,12 @@ import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.relation.MaterializedRelation;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.math.DoubleMinMax;
import de.lmu.ifi.dbs.elki.result.outlier.InvertedOutlierScoreMeta;
import de.lmu.ifi.dbs.elki.result.outlier.OutlierResult;
import de.lmu.ifi.dbs.elki.result.outlier.OutlierScoreMeta;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.utilities.documentation.Description;
import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
import de.lmu.ifi.dbs.elki.utilities.documentation.Title;
@@ -65,16 +64,18 @@ import de.lmu.ifi.dbs.elki.utilities.pairs.IntIntPair;
*
* @author Ahmed Hettab
* @author Erich Schubert
+ *
+ * @param <V> Vector type
*/
// TODO: progress logging!
@Title("BruteForce: Outlier detection for high dimensional data")
@Description("Examines all possible sets of k dimensional projections")
@Reference(authors = "C.C. Aggarwal, P. S. Yu", title = "Outlier detection for high dimensional data", booktitle = "Proc. ACM SIGMOD Int. Conf. on Management of Data (SIGMOD 2001), Santa Barbara, CA, 2001", url = "http://dx.doi.org/10.1145/375663.375668")
-public class AggarwalYuNaive<V extends NumberVector<?, ?>> extends AbstractAggarwalYuOutlier<V> {
+public class AggarwalYuNaive<V extends NumberVector<?>> extends AbstractAggarwalYuOutlier<V> {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(AggarwalYuNaive.class);
+ private static final Logging LOG = Logging.getLogger(AggarwalYuNaive.class);
/**
* Constructor.
@@ -93,23 +94,23 @@ public class AggarwalYuNaive<V extends NumberVector<?, ?>> extends AbstractAggar
* @return Outlier detection result
*/
public OutlierResult run(Relation<V> relation) {
- final int dimensionality = DatabaseUtil.dimensionality(relation);
+ final int dimensionality = RelationUtil.dimensionality(relation);
final int size = relation.size();
ArrayList<ArrayList<DBIDs>> ranges = buildRanges(relation);
- ArrayList<Vector<IntIntPair>> Rk;
+ ArrayList<ArrayList<IntIntPair>> Rk;
// Build a list of all subspaces
{
// R1 initial one-dimensional subspaces.
- Rk = new ArrayList<Vector<IntIntPair>>();
+ Rk = new ArrayList<ArrayList<IntIntPair>>();
// Set of all dim*phi ranges
ArrayList<IntIntPair> q = new ArrayList<IntIntPair>();
- for(int i = 1; i <= dimensionality; i++) {
+ for(int i = 0; i < dimensionality; i++) {
for(int j = 1; j <= phi; j++) {
IntIntPair s = new IntIntPair(i, j);
q.add(s);
// Add to first Rk
- Vector<IntIntPair> v = new Vector<IntIntPair>();
+ ArrayList<IntIntPair> v = new ArrayList<IntIntPair>();
v.add(s);
Rk.add(v);
}
@@ -117,10 +118,10 @@ public class AggarwalYuNaive<V extends NumberVector<?, ?>> extends AbstractAggar
// build Ri
for(int i = 2; i <= k; i++) {
- ArrayList<Vector<IntIntPair>> Rnew = new ArrayList<Vector<IntIntPair>>();
+ ArrayList<ArrayList<IntIntPair>> Rnew = new ArrayList<ArrayList<IntIntPair>>();
for(int j = 0; j < Rk.size(); j++) {
- Vector<IntIntPair> c = Rk.get(j);
+ ArrayList<IntIntPair> c = Rk.get(j);
for(IntIntPair pair : q) {
boolean invalid = false;
for(int t = 0; t < c.size(); t++) {
@@ -130,7 +131,7 @@ public class AggarwalYuNaive<V extends NumberVector<?, ?>> extends AbstractAggar
}
}
if(!invalid) {
- Vector<IntIntPair> neu = new Vector<IntIntPair>(c);
+ ArrayList<IntIntPair> neu = new ArrayList<IntIntPair>(c);
neu.add(pair);
Rnew.add(neu);
}
@@ -142,9 +143,9 @@ public class AggarwalYuNaive<V extends NumberVector<?, ?>> extends AbstractAggar
WritableDoubleDataStore sparsity = DataStoreUtil.makeDoubleStorage(relation.getDBIDs(), DataStoreFactory.HINT_HOT | DataStoreFactory.HINT_STATIC);
// calculate the sparsity coefficient
- for(Vector<IntIntPair> sub : Rk) {
+ for(ArrayList<IntIntPair> sub : Rk) {
DBIDs ids = computeSubspace(sub, ranges);
- final double sparsityC = sparsity(ids.size(), size, k);
+ final double sparsityC = sparsity(ids.size(), size, k, phi);
if(sparsityC < 0) {
for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
@@ -171,7 +172,7 @@ public class AggarwalYuNaive<V extends NumberVector<?, ?>> extends AbstractAggar
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -181,7 +182,7 @@ public class AggarwalYuNaive<V extends NumberVector<?, ?>> extends AbstractAggar
*
* @apiviz.exclude
*/
- public static class Parameterizer<V extends NumberVector<?, ?>> extends AbstractAggarwalYuOutlier.Parameterizer {
+ public static class Parameterizer<V extends NumberVector<?>> extends AbstractAggarwalYuOutlier.Parameterizer {
@Override
protected AggarwalYuNaive<V> makeInstance() {
return new AggarwalYuNaive<V>(k, phi);
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/COP.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/COP.java
new file mode 100644
index 00000000..ac544b7f
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/COP.java
@@ -0,0 +1,385 @@
+package de.lmu.ifi.dbs.elki.algorithm.outlier;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.Arrays;
+
+import de.lmu.ifi.dbs.elki.algorithm.AbstractDistanceBasedAlgorithm;
+import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.data.type.TypeInformation;
+import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
+import de.lmu.ifi.dbs.elki.database.QueryUtil;
+import de.lmu.ifi.dbs.elki.database.datastore.DataStoreFactory;
+import de.lmu.ifi.dbs.elki.database.datastore.DataStoreUtil;
+import de.lmu.ifi.dbs.elki.database.datastore.WritableDataStore;
+import de.lmu.ifi.dbs.elki.database.datastore.WritableDoubleDataStore;
+import de.lmu.ifi.dbs.elki.database.datastore.WritableIntegerDataStore;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
+import de.lmu.ifi.dbs.elki.database.ids.ModifiableDBIDs;
+import de.lmu.ifi.dbs.elki.database.query.knn.KNNQuery;
+import de.lmu.ifi.dbs.elki.database.relation.MaterializedRelation;
+import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
+import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
+import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance;
+import de.lmu.ifi.dbs.elki.logging.Logging;
+import de.lmu.ifi.dbs.elki.logging.progress.FiniteProgress;
+import de.lmu.ifi.dbs.elki.math.linearalgebra.Centroid;
+import de.lmu.ifi.dbs.elki.math.linearalgebra.Matrix;
+import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector;
+import de.lmu.ifi.dbs.elki.math.linearalgebra.pca.PCAResult;
+import de.lmu.ifi.dbs.elki.math.linearalgebra.pca.PCARunner;
+import de.lmu.ifi.dbs.elki.math.statistics.distribution.ChiSquaredDistribution;
+import de.lmu.ifi.dbs.elki.math.statistics.distribution.GammaDistribution;
+import de.lmu.ifi.dbs.elki.result.outlier.OutlierResult;
+import de.lmu.ifi.dbs.elki.result.outlier.OutlierScoreMeta;
+import de.lmu.ifi.dbs.elki.result.outlier.ProbabilisticOutlierScore;
+import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
+import de.lmu.ifi.dbs.elki.utilities.documentation.Title;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.LessConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.DoubleParameter;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.EnumParameter;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ObjectParameter;
+
+/**
+ * Correlation outlier probability: Outlier Detection in Arbitrarily Oriented
+ * Subspaces
+ *
+ * <p>
+ * Hans-Peter Kriegel, Peer Kröger, Erich Schubert, Arthur Zimek<br />
+ * Outlier Detection in Arbitrarily Oriented Subspaces<br />
+ * in: Proc. IEEE International Conference on Data Mining (ICDM 2012)
+ * </p>
+ *
+ * @author Erich Schubert
+ *
+ * @param <V> the type of NumberVector handled by this Algorithm
+ * @param <D> Distance type
+ */
+@Title("COP: Correlation Outlier Probability")
+@Reference(authors = "Hans-Peter Kriegel, Peer Kröger, Erich Schubert, Arthur Zimek", title = "Outlier Detection in Arbitrarily Oriented Subspaces", booktitle = "Proc. IEEE International Conference on Data Mining (ICDM 2012)")
+public class COP<V extends NumberVector<?>, D extends NumberDistance<D, ?>> extends AbstractDistanceBasedAlgorithm<V, D, OutlierResult> implements OutlierAlgorithm {
+ /**
+ * The logger for this class.
+ */
+ private static final Logging LOG = Logging.getLogger(COP.class);
+
+ /**
+ * Result name for the COP outlier scores.
+ */
+ public static final String COP_SCORES = "cop-outlier";
+
+ /**
+ * Result name for the dimensionality.
+ */
+ public static final String COP_DIM = "cop-dim";
+
+ /**
+ * Result name for the error vectors.
+ */
+ public static final String COP_ERRORVEC = "cop-errorvec";
+
+ /**
+ * Number of neighbors to be considered.
+ */
+ int k;
+
+ /**
+ * Holds the PCA runner.
+ */
+ private PCARunner<V> pca;
+
+ /**
+ * Expected amount of outliers.
+ */
+ double expect = 0.0001;
+
+ /**
+ * Score type.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public enum DistanceDist {
+ /**
+ * Use chi^2 for score normalization.
+ */
+ CHISQUARED,
+ /**
+ * Use gamma distributions for score normalization.
+ */
+ GAMMA
+ }
+
+ /**
+ * Type of distribution to assume for distances.
+ */
+ DistanceDist dist = DistanceDist.CHISQUARED;
+
+ /**
+ * Constructor.
+ *
+ * @param distanceFunction distance function
+ * @param k number of neighbors
+ * @param pca PCA computation method
+ * @param expect Expected fraction of outliers (for score normalization)
+ * @param dist Distance distribution model (ChiSquared, Gamma)
+ */
+ public COP(DistanceFunction<? super V, D> distanceFunction, int k, PCARunner<V> pca, double expect, DistanceDist dist) {
+ super(distanceFunction);
+ this.k = k;
+ this.pca = pca;
+ this.expect = expect;
+ this.dist = dist;
+ }
+
+ /**
+ * Process a single relation.
+ *
+ * @param relation Relation to process
+ * @return Outlier detection result
+ */
+ public OutlierResult run(Relation<V> relation) {
+ final DBIDs ids = relation.getDBIDs();
+ KNNQuery<V, D> knnQuery = QueryUtil.getKNNQuery(relation, getDistanceFunction(), k + 1);
+
+ final int dim = RelationUtil.dimensionality(relation);
+ if (k <= dim + 1) {
+ LOG.warning("PCA is underspecified with a too low k! k should be at much larger than " + dim);
+ }
+
+ WritableDoubleDataStore cop_score = DataStoreUtil.makeDoubleStorage(ids, DataStoreFactory.HINT_HOT | DataStoreFactory.HINT_STATIC);
+ WritableDataStore<Vector> cop_err_v = DataStoreUtil.makeStorage(ids, DataStoreFactory.HINT_HOT | DataStoreFactory.HINT_STATIC, Vector.class);
+ WritableIntegerDataStore cop_dim = DataStoreUtil.makeIntegerStorage(ids, DataStoreFactory.HINT_HOT | DataStoreFactory.HINT_STATIC, -1);
+ // compute neighbors of each db object
+ FiniteProgress prog = LOG.isVerbose() ? new FiniteProgress("Correlation Outlier Probabilities", relation.size(), LOG) : null;
+
+ for (DBIDIter id = ids.iter(); id.valid(); id.advance()) {
+ KNNResult<D> neighbors = knnQuery.getKNNForDBID(id, k + 1);
+ ModifiableDBIDs nids = DBIDUtil.newHashSet(neighbors);
+ nids.remove(id); // Do not use query object
+
+ Vector centroid = Centroid.make(relation, nids).toVector(relation).getColumnVector();
+ Vector relative = relation.get(id).getColumnVector().minusEquals(centroid);
+
+ PCAResult pcares = pca.processIds(nids, relation);
+ Matrix evecs = pcares.getEigenvectors();
+ Vector projected = evecs.transposeTimes(relative);
+ double[] evs = pcares.getEigenvalues();
+
+ double min = Double.POSITIVE_INFINITY;
+ int vdim = dim;
+ switch(dist) {
+ case CHISQUARED: {
+ double sqdevs = 0;
+ for (int d = 0; d < dim; d++) {
+ // Scale with Stddev
+ double dev = projected.get(d);
+ // Accumulate
+ sqdevs += dev * dev / evs[d];
+ // Evaluate
+ double score = 1 - ChiSquaredDistribution.cdf(sqdevs, d + 1);
+ if (score < min) {
+ min = score;
+ vdim = d + 1;
+ }
+ }
+ break;
+ }
+ case GAMMA: {
+ double[][] dists = new double[dim][nids.size()];
+ int j = 0;
+ Vector srel = new Vector(dim);
+ for (DBIDIter s = nids.iter(); s.valid() && j < nids.size(); s.advance()) {
+ V vec = relation.get(s);
+ for (int d = 0; d < dim; d++) {
+ srel.set(d, vec.doubleValue(d) - centroid.get(d));
+ }
+ Vector serr = evecs.transposeTimes(srel);
+ double sqdist = 0.0;
+ for (int d = 0; d < dim; d++) {
+ sqdist += serr.get(d) * serr.get(d) / evs[d];
+ dists[d][j] = sqdist;
+ }
+ j++;
+ }
+ double sqdevs = 0;
+ for (int d = 0; d < dim; d++) {
+ // Scale with Stddev
+ final double dev = projected.get(d);
+ // Accumulate
+ sqdevs += dev * dev / evs[d];
+ // Sort, so we can trim the top 15% below.
+ Arrays.sort(dists[d]);
+ // Evaluate
+ double score = 1 - GammaDistribution.estimate(dists[d], (int) (.85 * dists[d].length)).cdf(sqdevs);
+ if (score < min) {
+ min = score;
+ vdim = d + 1;
+ }
+ }
+ break;
+ }
+ }
+ // Normalize the value
+ final double prob = expect * (1 - min) / (expect + min);
+ // Construct the error vector:
+ for (int d = vdim; d < dim; d++) {
+ projected.set(d, 0.0);
+ }
+ Vector ev = evecs.times(projected).timesEquals(-1 * prob);
+
+ cop_score.putDouble(id, prob);
+ cop_err_v.put(id, ev);
+ cop_dim.putInt(id, dim + 1 - vdim);
+
+ if (prog != null) {
+ prog.incrementProcessed(LOG);
+ }
+ }
+ if (prog != null) {
+ prog.ensureCompleted(LOG);
+ }
+
+ // combine results.
+ Relation<Double> scoreResult = new MaterializedRelation<Double>("Correlation Outlier Probabilities", COP_SCORES, TypeUtil.DOUBLE, cop_score, ids);
+ OutlierScoreMeta scoreMeta = new ProbabilisticOutlierScore();
+ OutlierResult result = new OutlierResult(scoreMeta, scoreResult);
+ result.addChildResult(new MaterializedRelation<Integer>("Local Dimensionality", COP_DIM, TypeUtil.INTEGER, cop_dim, ids));
+ result.addChildResult(new MaterializedRelation<Vector>("Error vectors", COP_ERRORVEC, TypeUtil.VECTOR, cop_err_v, ids));
+ return result;
+ }
+
+ @Override
+ public TypeInformation[] getInputTypeRestriction() {
+ return TypeUtil.array(TypeUtil.NUMBER_VECTOR_FIELD);
+ }
+
+ @Override
+ protected Logging getLogger() {
+ return LOG;
+ }
+
+ /**
+ * Parameterization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public static class Parameterizer<V extends NumberVector<?>, D extends NumberDistance<D, ?>> extends AbstractDistanceBasedAlgorithm.Parameterizer<V, D> {
+ /**
+ * Parameter to specify the number of nearest neighbors of an object to be
+ * considered for computing its COP_SCORE, must be an integer greater than
+ * 0.
+ * <p>
+ * Key: {@code -cop.k}
+ * </p>
+ */
+ public static final OptionID K_ID = new OptionID("cop.k", "The number of nearest neighbors of an object to be considered for computing its COP_SCORE.");
+
+ /**
+ * Distribution assumption for distances.
+ * <p>
+ * Key: {@code -cop.dist}
+ * </p>
+ */
+ public static final OptionID DIST_ID = new OptionID("cop.dist", "The assumed distribution of squared distances. ChiSquared is faster, Gamma expected to be more accurate but could also overfit.");
+
+ /**
+ * Class to compute the PCA with.
+ * <p>
+ * Key: {@code -cop.pcarunner}
+ * </p>
+ */
+ public static final OptionID PCARUNNER_ID = new OptionID("cop.pcarunner", "The class to compute (filtered) PCA.");
+
+ /**
+ * Expected share of outliers.
+ * <p>
+ * Key: {@code -cop.expect}
+ *
+ * Default: 0.001
+ * </p>
+ */
+ public static final OptionID EXPECT_ID = new OptionID("cop.expect", "Expected share of outliers. Only affect score normalization.");
+
+ /**
+ * Number of neighbors to be considered.
+ */
+ int k;
+
+ /**
+ * Holds the object performing the dependency derivation.
+ */
+ PCARunner<V> pca;
+
+ /**
+ * Distance distributution assumption.
+ */
+ DistanceDist dist;
+
+ /**
+ * Expected amount of outliers.
+ */
+ double expect;
+
+ @Override
+ protected void makeOptions(Parameterization config) {
+ super.makeOptions(config);
+ IntParameter kP = new IntParameter(K_ID);
+ kP.addConstraint(new GreaterConstraint(5));
+ if (config.grab(kP)) {
+ k = kP.intValue();
+ }
+ EnumParameter<DistanceDist> distP = new EnumParameter<DistanceDist>(DIST_ID, DistanceDist.class, DistanceDist.GAMMA);
+ if (config.grab(distP)) {
+ dist = distP.getValue();
+ }
+ DoubleParameter expectP = new DoubleParameter(EXPECT_ID, 0.001);
+ expectP.addConstraint(new GreaterConstraint(0));
+ expectP.addConstraint(new LessConstraint(1.0));
+ if (config.grab(expectP)) {
+ expect = expectP.doubleValue();
+ }
+ ObjectParameter<PCARunner<V>> pcaP = new ObjectParameter<PCARunner<V>>(PCARUNNER_ID, PCARunner.class, PCARunner.class);
+ if (config.grab(pcaP)) {
+ pca = pcaP.instantiateClass(config);
+ }
+ }
+
+ @Override
+ protected COP<V, D> makeInstance() {
+ return new COP<V, D>(distanceFunction, k, pca, expect, dist);
+ }
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/DBOutlierDetection.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/DBOutlierDetection.java
index dbaf8a5a..ba1fd841 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/DBOutlierDetection.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/DBOutlierDetection.java
@@ -24,17 +24,17 @@ package de.lmu.ifi.dbs.elki.algorithm.outlier;
*/
import de.lmu.ifi.dbs.elki.database.Database;
-import de.lmu.ifi.dbs.elki.database.datastore.DataStore;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreFactory;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreUtil;
+import de.lmu.ifi.dbs.elki.database.datastore.DoubleDataStore;
import de.lmu.ifi.dbs.elki.database.datastore.WritableDoubleDataStore;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.query.DatabaseQuery;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.database.query.knn.KNNQuery;
-import de.lmu.ifi.dbs.elki.database.query.knn.KNNResult;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.logging.progress.FiniteProgress;
@@ -72,13 +72,13 @@ public class DBOutlierDetection<O, D extends Distance<D>> extends AbstractDBOutl
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(DBOutlierDetection.class);
+ private static final Logging LOG = Logging.getLogger(DBOutlierDetection.class);
/**
* Parameter to specify the minimum fraction of objects that must be outside
* the D- neighborhood of an outlier
*/
- public static final OptionID P_ID = OptionID.getOrCreateOptionID("dbod.p", "minimum fraction of objects that must be outside the D-neighborhood of an outlier");
+ public static final OptionID P_ID = new OptionID("dbod.p", "minimum fraction of objects that must be outside the D-neighborhood of an outlier");
/**
* Holds the value of {@link #P_ID}.
@@ -98,7 +98,7 @@ public class DBOutlierDetection<O, D extends Distance<D>> extends AbstractDBOutl
}
@Override
- protected DataStore<Double> computeOutlierScores(Database database, Relation<O> relation, D neighborhoodSize) {
+ protected DoubleDataStore computeOutlierScores(Database database, Relation<O> relation, D neighborhoodSize) {
DistanceQuery<O, D> distFunc = database.getDistanceQuery(relation, getDistanceFunction());
KNNQuery<O, D> knnQuery = database.getKNNQuery(distFunc, DatabaseQuery.HINT_OPTIMIZED_ONLY);
@@ -106,11 +106,11 @@ public class DBOutlierDetection<O, D extends Distance<D>> extends AbstractDBOutl
int m = (int) ((distFunc.getRelation().size()) * (1 - p));
WritableDoubleDataStore scores = DataStoreUtil.makeDoubleStorage(distFunc.getRelation().getDBIDs(), DataStoreFactory.HINT_STATIC);
- if(logger.isVerbose()) {
- logger.verbose("computing outlier flag");
+ if(LOG.isVerbose()) {
+ LOG.verbose("computing outlier flag");
}
- FiniteProgress progressOFlags = logger.isVerbose() ? new FiniteProgress("DBOutlier for objects", distFunc.getRelation().size(), logger) : null;
+ FiniteProgress progressOFlags = LOG.isVerbose() ? new FiniteProgress("DBOutlier for objects", distFunc.getRelation().size(), LOG) : null;
int counter = 0;
// if index exists, kNN query. if the distance to the mth nearest neighbor
// is more than d -> object is outlier
@@ -118,8 +118,8 @@ public class DBOutlierDetection<O, D extends Distance<D>> extends AbstractDBOutl
for(DBIDIter iditer = distFunc.getRelation().iterDBIDs(); iditer.valid(); iditer.advance()) {
counter++;
final KNNResult<D> knns = knnQuery.getKNNForDBID(iditer, m);
- if(logger.isDebugging()) {
- logger.debugFine("distance to mth nearest neighbour" + knns.toString());
+ if(LOG.isDebugging()) {
+ LOG.debugFine("distance to mth nearest neighbour" + knns.toString());
}
if(knns.get(Math.min(m, knns.size()) - 1).getDistance().compareTo(neighborhoodSize) <= 0) {
// flag as outlier
@@ -131,7 +131,7 @@ public class DBOutlierDetection<O, D extends Distance<D>> extends AbstractDBOutl
}
}
if(progressOFlags != null) {
- progressOFlags.setProcessed(counter, logger);
+ progressOFlags.setProcessed(counter, LOG);
}
}
else {
@@ -149,18 +149,18 @@ public class DBOutlierDetection<O, D extends Distance<D>> extends AbstractDBOutl
}
if(progressOFlags != null) {
- progressOFlags.setProcessed(counter, logger);
+ progressOFlags.setProcessed(counter, LOG);
}
}
if(progressOFlags != null) {
- progressOFlags.ensureCompleted(logger);
+ progressOFlags.ensureCompleted(LOG);
}
return scores;
}
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/DBOutlierScore.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/DBOutlierScore.java
index 419b9a0e..a2d39130 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/DBOutlierScore.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/DBOutlierScore.java
@@ -24,9 +24,9 @@ package de.lmu.ifi.dbs.elki.algorithm.outlier;
*/
import de.lmu.ifi.dbs.elki.database.Database;
-import de.lmu.ifi.dbs.elki.database.datastore.DataStore;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreFactory;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreUtil;
+import de.lmu.ifi.dbs.elki.database.datastore.DoubleDataStore;
import de.lmu.ifi.dbs.elki.database.datastore.WritableDoubleDataStore;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
@@ -60,7 +60,7 @@ public class DBOutlierScore<O, D extends Distance<D>> extends AbstractDBOutlier<
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(DBOutlierScore.class);
+ private static final Logging LOG = Logging.getLogger(DBOutlierScore.class);
/**
* Constructor with parameters.
@@ -73,7 +73,7 @@ public class DBOutlierScore<O, D extends Distance<D>> extends AbstractDBOutlier<
}
@Override
- protected DataStore<Double> computeOutlierScores(Database database, Relation<O> relation, D d) {
+ protected DoubleDataStore computeOutlierScores(Database database, Relation<O> relation, D d) {
DistanceQuery<O, D> distFunc = database.getDistanceQuery(relation, getDistanceFunction());
RangeQuery<O, D> rangeQuery = database.getRangeQuery(distFunc);
final double size = distFunc.getRelation().size();
@@ -90,7 +90,7 @@ public class DBOutlierScore<O, D extends Distance<D>> extends AbstractDBOutlier<
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/EMOutlier.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/EMOutlier.java
index db4b7782..2d2a4466 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/EMOutlier.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/EMOutlier.java
@@ -62,11 +62,11 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameteriz
// TODO: re-use an existing EM when present?
@Title("EM Outlier: Outlier Detection based on the generic EM clustering")
@Description("The outlier score assigned is based on the highest cluster probability obtained from EM clustering.")
-public class EMOutlier<V extends NumberVector<V, ?>> extends AbstractAlgorithm<OutlierResult> implements OutlierAlgorithm {
+public class EMOutlier<V extends NumberVector<?>> extends AbstractAlgorithm<OutlierResult> implements OutlierAlgorithm {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(EMOutlier.class);
+ private static final Logging LOG = Logging.getLogger(EMOutlier.class);
/**
* Inner algorithm.
@@ -120,7 +120,7 @@ public class EMOutlier<V extends NumberVector<V, ?>> extends AbstractAlgorithm<O
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -130,7 +130,7 @@ public class EMOutlier<V extends NumberVector<V, ?>> extends AbstractAlgorithm<O
*
* @apiviz.exclude
*/
- public static class Parameterizer<V extends NumberVector<V, ?>> extends AbstractParameterizer {
+ public static class Parameterizer<V extends NumberVector<?>> extends AbstractParameterizer {
protected EM<V> em = null;
@Override
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/GaussianModel.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/GaussianModel.java
index 51833c8b..6aed60fe 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/GaussianModel.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/GaussianModel.java
@@ -29,10 +29,10 @@ import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreFactory;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreUtil;
import de.lmu.ifi.dbs.elki.database.datastore.WritableDoubleDataStore;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.relation.MaterializedRelation;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.math.DoubleMinMax;
import de.lmu.ifi.dbs.elki.math.MathUtil;
@@ -43,7 +43,6 @@ import de.lmu.ifi.dbs.elki.result.outlier.BasicOutlierScoreMeta;
import de.lmu.ifi.dbs.elki.result.outlier.InvertedOutlierScoreMeta;
import de.lmu.ifi.dbs.elki.result.outlier.OutlierResult;
import de.lmu.ifi.dbs.elki.result.outlier.OutlierScoreMeta;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.utilities.documentation.Description;
import de.lmu.ifi.dbs.elki.utilities.documentation.Title;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
@@ -61,16 +60,16 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.Flag;
*/
@Title("Gaussian Model Outlier Detection")
@Description("Fit a multivariate gaussian model onto the data, and use the PDF to compute an outlier score.")
-public class GaussianModel<V extends NumberVector<V, ?>> extends AbstractAlgorithm<OutlierResult> implements OutlierAlgorithm {
+public class GaussianModel<V extends NumberVector<?>> extends AbstractAlgorithm<OutlierResult> implements OutlierAlgorithm {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(GaussianModel.class);
+ private static final Logging LOG = Logging.getLogger(GaussianModel.class);
/**
* OptionID for inversion flag.
*/
- public static final OptionID INVERT_ID = OptionID.getOrCreateOptionID("gaussod.invert", "Invert the value range to [0:1], with 1 being outliers instead of 0.");
+ public static final OptionID INVERT_ID = new OptionID("gaussod.invert", "Invert the value range to [0:1], with 1 being outliers instead of 0.");
/**
* Small value to increment diagonally of a matrix in order to avoid
@@ -113,7 +112,7 @@ public class GaussianModel<V extends NumberVector<V, ?>> extends AbstractAlgorit
Matrix covarianceTransposed = covarianceMatrix.cheatToAvoidSingularity(SINGULARITY_CHEAT).inverse();
// Normalization factors for Gaussian PDF
- final double fakt = (1.0 / (Math.sqrt(Math.pow(MathUtil.TWOPI, DatabaseUtil.dimensionality(relation)) * covarianceMatrix.det())));
+ final double fakt = (1.0 / (Math.sqrt(Math.pow(MathUtil.TWOPI, RelationUtil.dimensionality(relation)) * covarianceMatrix.det())));
// for each object compute Mahalanobis distance
for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
@@ -130,8 +129,7 @@ public class GaussianModel<V extends NumberVector<V, ?>> extends AbstractAlgorit
if(invert) {
double max = mm.getMax() != 0 ? mm.getMax() : 1.;
for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
- oscores.putDouble(id, (max - oscores.doubleValue(id)) / max);
+ oscores.putDouble(iditer, (max - oscores.doubleValue(iditer)) / max);
}
meta = new BasicOutlierScoreMeta(0.0, 1.0);
}
@@ -149,7 +147,7 @@ public class GaussianModel<V extends NumberVector<V, ?>> extends AbstractAlgorit
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -159,7 +157,7 @@ public class GaussianModel<V extends NumberVector<V, ?>> extends AbstractAlgorit
*
* @apiviz.exclude
*/
- public static class Parameterizer<V extends NumberVector<V, ?>> extends AbstractParameterizer {
+ public static class Parameterizer<V extends NumberVector<?>> extends AbstractParameterizer {
protected boolean invert = false;
@Override
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/GaussianUniformMixture.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/GaussianUniformMixture.java
index 1cd31442..db53a3ef 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/GaussianUniformMixture.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/GaussianUniformMixture.java
@@ -32,13 +32,13 @@ import de.lmu.ifi.dbs.elki.database.datastore.DataStoreFactory;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreUtil;
import de.lmu.ifi.dbs.elki.database.datastore.WritableDoubleDataStore;
import de.lmu.ifi.dbs.elki.database.ids.ArrayDBIDs;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.ids.generic.MaskedDBIDs;
import de.lmu.ifi.dbs.elki.database.relation.MaterializedRelation;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.math.DoubleMinMax;
import de.lmu.ifi.dbs.elki.math.MathUtil;
@@ -48,7 +48,6 @@ import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector;
import de.lmu.ifi.dbs.elki.result.outlier.BasicOutlierScoreMeta;
import de.lmu.ifi.dbs.elki.result.outlier.OutlierResult;
import de.lmu.ifi.dbs.elki.result.outlier.OutlierScoreMeta;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.utilities.documentation.Description;
import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
import de.lmu.ifi.dbs.elki.utilities.documentation.Title;
@@ -79,21 +78,21 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.DoubleParameter;
@Title("Gaussian-Uniform Mixture Model Outlier Detection")
@Description("Fits a mixture model consisting of a Gaussian and a uniform distribution to the data.")
@Reference(prefix = "Generalization using the likelihood gain as outlier score of", authors = "Eskin, Eleazar", title = "Anomaly detection over noisy data using learned probability distributions", booktitle = "Proc. of the Seventeenth International Conference on Machine Learning (ICML-2000)")
-public class GaussianUniformMixture<V extends NumberVector<V, ?>> extends AbstractAlgorithm<OutlierResult> implements OutlierAlgorithm {
+public class GaussianUniformMixture<V extends NumberVector<?>> extends AbstractAlgorithm<OutlierResult> implements OutlierAlgorithm {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(GaussianUniformMixture.class);
+ private static final Logging LOG = Logging.getLogger(GaussianUniformMixture.class);
/**
* Parameter to specify the fraction of expected outliers.
*/
- public static final OptionID L_ID = OptionID.getOrCreateOptionID("mmo.l", "expected fraction of outliers");
+ public static final OptionID L_ID = new OptionID("mmo.l", "expected fraction of outliers");
/**
* Parameter to specify the cutoff.
*/
- public static final OptionID C_ID = OptionID.getOrCreateOptionID("mmo.c", "cutoff");
+ public static final OptionID C_ID = new OptionID("mmo.c", "cutoff");
/**
* Small value to increment diagonally of a matrix in order to avoid
@@ -154,20 +153,19 @@ public class GaussianUniformMixture<V extends NumberVector<V, ?>> extends Abstra
// logger.debugFine(logLike + " loglike beginning" +
// loglikelihoodNormal(normalObjs, database));
DoubleMinMax minmax = new DoubleMinMax();
- for(int i = 0; i < objids.size(); i++) {
+
+ DBIDIter iter = objids.iter();
+ for(int i = 0; i < objids.size(); i++, iter.advance()) {
// logger.debugFine("i " + i);
// Change mask to make the current object anomalous
bits.set(i);
// Compute new likelihoods
double currentLogLike = normalObjs.size() * logml + loglikelihoodNormal(normalObjs, relation) + anomalousObjs.size() * logl + loglikelihoodAnomalous(anomalousObjs);
- // Get the actual object id
- DBID curid = objids.get(i);
-
// if the loglike increases more than a threshold, object stays in
// anomalous set and is flagged as outlier
final double loglikeGain = currentLogLike - logLike;
- oscores.putDouble(curid, loglikeGain);
+ oscores.putDouble(iter, loglikeGain);
minmax.put(loglikeGain);
if(loglikeGain > c) {
@@ -221,7 +219,7 @@ public class GaussianUniformMixture<V extends NumberVector<V, ?>> extends Abstra
Matrix covInv = covarianceMatrix.cheatToAvoidSingularity(SINGULARITY_CHEAT).inverse();
double covarianceDet = covarianceMatrix.det();
- double fakt = 1.0 / Math.sqrt(Math.pow(MathUtil.TWOPI, DatabaseUtil.dimensionality(database)) * covarianceDet);
+ double fakt = 1.0 / Math.sqrt(Math.pow(MathUtil.TWOPI, RelationUtil.dimensionality(database)) * covarianceDet);
// for each object compute probability and sum
double prob = 0;
for (DBIDIter iter = objids.iter(); iter.valid(); iter.advance()) {
@@ -239,7 +237,7 @@ public class GaussianUniformMixture<V extends NumberVector<V, ?>> extends Abstra
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -249,7 +247,7 @@ public class GaussianUniformMixture<V extends NumberVector<V, ?>> extends Abstra
*
* @apiviz.exclude
*/
- public static class Parameterizer<V extends NumberVector<V, ?>> extends AbstractParameterizer {
+ public static class Parameterizer<V extends NumberVector<?>> extends AbstractParameterizer {
protected double l = 1E-7;
protected double c = 0;
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/HilOut.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/HilOut.java
index 4ed56e1a..15f6cbf3 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/HilOut.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/HilOut.java
@@ -36,13 +36,15 @@ import de.lmu.ifi.dbs.elki.database.datastore.DataStoreFactory;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreUtil;
import de.lmu.ifi.dbs.elki.database.datastore.WritableDoubleDataStore;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDFactory;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
+import de.lmu.ifi.dbs.elki.database.ids.DoubleDistanceDBIDPair;
import de.lmu.ifi.dbs.elki.database.ids.HashSetModifiableDBIDs;
-import de.lmu.ifi.dbs.elki.database.query.DoubleDistanceResultPair;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.database.relation.MaterializedRelation;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.distance.distancefunction.EuclideanDistanceFunction;
import de.lmu.ifi.dbs.elki.distance.distancefunction.LPNormDistanceFunction;
import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
@@ -91,11 +93,11 @@ import de.lmu.ifi.dbs.elki.utilities.pairs.Pair;
@Title("Fast Outlier Detection in High Dimensional Spaces")
@Description("Algorithm to compute outliers using Hilbert space filling curves")
@Reference(authors = "F. Angiulli, C. Pizzuti", title = "Fast Outlier Detection in High Dimensional Spaces", booktitle = "Proc. European Conference on Principles of Knowledge Discovery and Data Mining (PKDD'02)", url = "http://dx.doi.org/10.1145/375663.375668")
-public class HilOut<O extends NumberVector<O, ?>> extends AbstractDistanceBasedAlgorithm<O, DoubleDistance, OutlierResult> implements OutlierAlgorithm {
+public class HilOut<O extends NumberVector<?>> extends AbstractDistanceBasedAlgorithm<O, DoubleDistance, OutlierResult> implements OutlierAlgorithm {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(HilOut.class);
+ private static final Logging LOG = Logging.getLogger(HilOut.class);
/**
* Number of nearest neighbors
@@ -170,7 +172,7 @@ public class HilOut<O extends NumberVector<O, ?>> extends AbstractDistanceBasedA
public OutlierResult run(Database database, Relation<O> relation) {
distq = database.getDistanceQuery(relation, getDistanceFunction());
- d = DatabaseUtil.dimensionality(relation);
+ d = RelationUtil.dimensionality(relation);
WritableDoubleDataStore hilout_weight = DataStoreUtil.makeDoubleStorage(relation.getDBIDs(), DataStoreFactory.HINT_STATIC);
// Compute extend of dataset.
@@ -181,18 +183,18 @@ public class HilOut<O extends NumberVector<O, ?>> extends AbstractDistanceBasedA
min = new double[d];
double[] max = new double[d];
for(int i = 0; i < d; i++) {
- min[i] = hbbs.first.doubleValue(i + 1);
- max[i] = hbbs.second.doubleValue(i + 1);
+ min[i] = hbbs.first.doubleValue(i);
+ max[i] = hbbs.second.doubleValue(i);
diameter = Math.max(diameter, max[i] - min[i]);
}
// Enlarge bounding box to have equal lengths.
for(int i = 0; i < d; i++) {
- double diff = (diameter - (max[i] - min[i])) / 2;
+ double diff = (diameter - (max[i] - min[i])) * .5;
min[i] -= diff;
max[i] += diff;
}
- if(logger.isVerbose()) {
- logger.verbose("Rescaling dataset by " + (1 / diameter) + " to fit the unit cube.");
+ if(LOG.isVerbose()) {
+ LOG.verbose("Rescaling dataset by " + (1 / diameter) + " to fit the unit cube.");
}
}
@@ -200,8 +202,8 @@ public class HilOut<O extends NumberVector<O, ?>> extends AbstractDistanceBasedA
capital_n_star = capital_n = relation.size();
HilbertFeatures h = new HilbertFeatures(relation, min, diameter);
- FiniteProgress progressHilOut = logger.isVerbose() ? new FiniteProgress("HilOut iterations", d + 1, logger) : null;
- FiniteProgress progressTrueOut = logger.isVerbose() ? new FiniteProgress("True outliers found", n, logger) : null;
+ FiniteProgress progressHilOut = LOG.isVerbose() ? new FiniteProgress("HilOut iterations", d + 1, LOG) : null;
+ FiniteProgress progressTrueOut = LOG.isVerbose() ? new FiniteProgress("True outliers found", n, LOG) : null;
// Main part: 1. Phase max. d+1 loops
for(int j = 0; j <= d && n_star < n; j++) {
// initialize (clear) out and wlb - not 100% clear in the paper
@@ -214,7 +216,7 @@ public class HilOut<O extends NumberVector<O, ?>> extends AbstractDistanceBasedA
// determine the true outliers (n_star)
trueOutliers(h);
if(progressTrueOut != null) {
- progressTrueOut.setProcessed(n_star, logger);
+ progressTrueOut.setProcessed(n_star, LOG);
}
// Build the top Set as out + wlb
h.top.clear();
@@ -230,7 +232,7 @@ public class HilOut<O extends NumberVector<O, ?>> extends AbstractDistanceBasedA
}
}
if(progressHilOut != null) {
- progressHilOut.incrementProcessed(logger);
+ progressHilOut.incrementProcessed(LOG);
}
}
// 2. Phase: Additional Scan if less than n true outliers determined
@@ -241,12 +243,12 @@ public class HilOut<O extends NumberVector<O, ?>> extends AbstractDistanceBasedA
scan(h, capital_n);
}
if(progressHilOut != null) {
- progressHilOut.setProcessed(d, logger);
- progressHilOut.ensureCompleted(logger);
+ progressHilOut.setProcessed(d, LOG);
+ progressHilOut.ensureCompleted(LOG);
}
if(progressTrueOut != null) {
- progressTrueOut.setProcessed(n, logger);
- progressTrueOut.ensureCompleted(logger);
+ progressTrueOut.setProcessed(n, LOG);
+ progressTrueOut.ensureCompleted(LOG);
}
DoubleMinMax minmax = new DoubleMinMax();
// Return weights in out
@@ -281,8 +283,8 @@ public class HilOut<O extends NumberVector<O, ?>> extends AbstractDistanceBasedA
*/
private void scan(HilbertFeatures hf, int k0) {
final int mink0 = Math.min(2 * k0, capital_n - 1);
- if(logger.isDebuggingFine()) {
- logger.debugFine("Scanning with k0=" + k0 + " (" + mink0 + ")" + " N*=" + capital_n_star);
+ if(LOG.isDebuggingFine()) {
+ LOG.debugFine("Scanning with k0=" + k0 + " (" + mink0 + ")" + " N*=" + capital_n_star);
}
for(int i = 0; i < hf.pf.length; i++) {
if(hf.pf[i].ubound < omega_star) {
@@ -366,7 +368,7 @@ public class HilOut<O extends NumberVector<O, ?>> extends AbstractDistanceBasedA
if(mlevel < level) {
level = mlevel;
final double delta = hf.minDistLevel(hf.pf[i].id, level);
- if(delta >= hf.pf[i].nn.peek().getDoubleDistance()) {
+ if(delta >= hf.pf[i].nn.peek().doubleDistance()) {
break; // stop = true
}
}
@@ -376,10 +378,10 @@ public class HilOut<O extends NumberVector<O, ?>> extends AbstractDistanceBasedA
double br = hf.boxRadius(i, a - 1, b + 1);
double newlb = 0.0;
double newub = 0.0;
- for(DoubleDistanceResultPair entry : hf.pf[i].nn) {
- newub += entry.getDoubleDistance();
- if(entry.getDoubleDistance() <= br) {
- newlb += entry.getDoubleDistance();
+ for(DoubleDistanceDBIDPair entry : hf.pf[i].nn) {
+ newub += entry.doubleDistance();
+ if(entry.doubleDistance() <= br) {
+ newlb += entry.doubleDistance();
}
}
if(newlb > hf.pf[i].lbound) {
@@ -408,7 +410,7 @@ public class HilOut<O extends NumberVector<O, ?>> extends AbstractDistanceBasedA
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
@Override
@@ -482,7 +484,7 @@ public class HilOut<O extends NumberVector<O, ?>> extends AbstractDistanceBasedA
int pos = 0;
for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
- pf[pos++] = new HilFeature(iditer.getDBID(), new Heap<DoubleDistanceResultPair>(k, Collections.reverseOrder()));
+ pf[pos++] = new HilFeature(DBIDUtil.deref(iditer), new Heap<DoubleDistanceDBIDPair>(k, Collections.reverseOrder()));
}
this.out = new Heap<HilFeature>(n, new Comparator<HilFeature>() {
@Override
@@ -513,7 +515,7 @@ public class HilOut<O extends NumberVector<O, ?>> extends AbstractDistanceBasedA
if(h >= 32) { // 32 to 63 bit
final long scale = Long.MAX_VALUE; // = 63 bits
for(int i = 0; i < pf.length; i++) {
- NumberVector<?, ?> obj = relation.get(pf[i].id);
+ NumberVector<?> obj = relation.get(pf[i].id);
long[] coord = new long[d];
for(int dim = 0; dim < d; dim++) {
coord[dim] = (long) (getDimForObject(obj, dim) * .5 * scale);
@@ -524,7 +526,7 @@ public class HilOut<O extends NumberVector<O, ?>> extends AbstractDistanceBasedA
else if(h >= 16) { // 16-31 bit
final int scale = ~1 >>> 1;
for(int i = 0; i < pf.length; i++) {
- NumberVector<?, ?> obj = relation.get(pf[i].id);
+ NumberVector<?> obj = relation.get(pf[i].id);
int[] coord = new int[d];
for(int dim = 0; dim < d; dim++) {
coord[dim] = (int) (getDimForObject(obj, dim) * .5 * scale);
@@ -535,7 +537,7 @@ public class HilOut<O extends NumberVector<O, ?>> extends AbstractDistanceBasedA
else if(h >= 8) { // 8-15 bit
final int scale = ~1 >>> 16;
for(int i = 0; i < pf.length; i++) {
- NumberVector<?, ?> obj = relation.get(pf[i].id);
+ NumberVector<?> obj = relation.get(pf[i].id);
short[] coord = new short[d];
for(int dim = 0; dim < d; dim++) {
coord[dim] = (short) (getDimForObject(obj, dim) * .5 * scale);
@@ -546,7 +548,7 @@ public class HilOut<O extends NumberVector<O, ?>> extends AbstractDistanceBasedA
else { // 1-7 bit
final int scale = ~1 >>> 8;
for(int i = 0; i < pf.length; i++) {
- NumberVector<?, ?> obj = relation.get(pf[i].id);
+ NumberVector<?> obj = relation.get(pf[i].id);
byte[] coord = new byte[d];
for(int dim = 0; dim < d; dim++) {
coord[dim] = (byte) (getDimForObject(obj, dim) * .5 * scale);
@@ -575,15 +577,13 @@ public class HilOut<O extends NumberVector<O, ?>> extends AbstractDistanceBasedA
*/
private void updateOUT(int i) {
if(out.size() < n) {
- out.offer(pf[i]);
+ out.add(pf[i]);
}
else {
HilFeature head = out.peek();
if(pf[i].ubound > head.ubound) {
// replace smallest
- out.poll();
- // assert (out.peek().ubound >= head.ubound);
- out.offer(pf[i]);
+ out.replaceTopElement(pf[i]);
}
}
}
@@ -595,15 +595,13 @@ public class HilOut<O extends NumberVector<O, ?>> extends AbstractDistanceBasedA
*/
private void updateWLB(int i) {
if(wlb.size() < n) {
- wlb.offer(pf[i]);
+ wlb.add(pf[i]);
}
else {
HilFeature head = wlb.peek();
if(pf[i].lbound > head.lbound) {
// replace smallest
- wlb.poll();
- // assert (wlb.peek().lbound >= head.lbound);
- wlb.offer(pf[i]);
+ wlb.replaceTopElement(pf[i]);
}
}
}
@@ -639,7 +637,7 @@ public class HilOut<O extends NumberVector<O, ?>> extends AbstractDistanceBasedA
* @param level Level of the corresponding r-region
*/
private double minDistLevel(DBID id, int level) {
- final NumberVector<?, ?> obj = relation.get(id);
+ final NumberVector<?> obj = relation.get(id);
// level 1 is supposed to have r=1 as in the original publication
// 2 ^ - (level - 1)
final double r = 1.0 / (1 << (level - 1));
@@ -659,7 +657,7 @@ public class HilOut<O extends NumberVector<O, ?>> extends AbstractDistanceBasedA
* @param level Level of the corresponding r-region
*/
private double maxDistLevel(DBID id, int level) {
- final NumberVector<?, ?> obj = relation.get(id);
+ final NumberVector<?> obj = relation.get(id);
// level 1 is supposed to have r=1 as in the original publication
final double r = 1.0 / (1 << (level - 1));
double dist;
@@ -780,8 +778,8 @@ public class HilOut<O extends NumberVector<O, ?>> extends AbstractDistanceBasedA
* @param dim Dimension
* @return Projected and shifted position
*/
- private double getDimForObject(NumberVector<?, ?> obj, int dim) {
- return (obj.doubleValue(dim + 1) - min[dim]) / diameter + shift;
+ private double getDimForObject(NumberVector<?> obj, int dim) {
+ return (obj.doubleValue(dim) - min[dim]) / diameter + shift;
}
}
@@ -824,7 +822,7 @@ public class HilOut<O extends NumberVector<O, ?>> extends AbstractDistanceBasedA
/**
* Heap with the nearest known neighbors
*/
- public Heap<DoubleDistanceResultPair> nn;
+ public Heap<DoubleDistanceDBIDPair> nn;
/**
* Set representation of the nearest neighbors for faster lookups
@@ -842,7 +840,7 @@ public class HilOut<O extends NumberVector<O, ?>> extends AbstractDistanceBasedA
* @param id Object ID
* @param nn Heap for neighbors
*/
- public HilFeature(DBID id, Heap<DoubleDistanceResultPair> nn) {
+ public HilFeature(DBID id, Heap<DoubleDistanceDBIDPair> nn) {
super();
this.id = id;
this.nn = nn;
@@ -864,27 +862,26 @@ public class HilOut<O extends NumberVector<O, ?>> extends AbstractDistanceBasedA
protected void insert(DBID id, double dt, int k) {
// assert (!nn_keys.contains(id));
if(nn.size() < k) {
- DoubleDistanceResultPair entry = new DoubleDistanceResultPair(dt, id);
- nn.offer(entry);
+ DoubleDistanceDBIDPair entry = DBIDFactory.FACTORY.newDistancePair(dt, id);
+ nn.add(entry);
nn_keys.add(id);
sum_nn += dt;
}
else {
- DoubleDistanceResultPair head = nn.peek();
- if(dt < head.getDoubleDistance()) {
+ DoubleDistanceDBIDPair head = nn.peek();
+ if(dt < head.doubleDistance()) {
head = nn.poll(); // Remove worst
- sum_nn -= head.getDoubleDistance();
- nn_keys.remove(head.getDBID());
+ sum_nn -= head.doubleDistance();
+ nn_keys.remove(head);
- // assert (nn.peek().getDoubleDistance() <= head.getDoubleDistance());
+ // assert (nn.peek().doubleDistance() <= head.doubleDistance());
- DoubleDistanceResultPair entry = new DoubleDistanceResultPair(dt, id);
- nn.offer(entry);
+ DoubleDistanceDBIDPair entry = DBIDFactory.FACTORY.newDistancePair(dt, id);
+ nn.add(entry);
nn_keys.add(id);
sum_nn += dt;
}
}
-
}
}
@@ -897,33 +894,33 @@ public class HilOut<O extends NumberVector<O, ?>> extends AbstractDistanceBasedA
*
* @param <O> Vector type
*/
- public static class Parameterizer<O extends NumberVector<O, ?>> extends AbstractParameterizer {
+ public static class Parameterizer<O extends NumberVector<?>> extends AbstractParameterizer {
/**
* Parameter to specify how many next neighbors should be used in the
* computation
*/
- public static final OptionID K_ID = OptionID.getOrCreateOptionID("HilOut.k", "Compute up to k next neighbors");
+ public static final OptionID K_ID = new OptionID("HilOut.k", "Compute up to k next neighbors");
/**
* Parameter to specify how many outliers should be computed
*/
- public static final OptionID N_ID = OptionID.getOrCreateOptionID("HilOut.n", "Compute n outliers");
+ public static final OptionID N_ID = new OptionID("HilOut.n", "Compute n outliers");
/**
* Parameter to specify the maximum Hilbert-Level
*/
- public static final OptionID H_ID = OptionID.getOrCreateOptionID("HilOut.h", "Max. Hilbert-Level");
+ public static final OptionID H_ID = new OptionID("HilOut.h", "Max. Hilbert-Level");
/**
* Parameter to specify p of LP-NormDistance
*/
- public static final OptionID T_ID = OptionID.getOrCreateOptionID("HilOut.t", "t of Lt Metric");
+ public static final OptionID T_ID = new OptionID("HilOut.t", "t of Lt Metric");
/**
* Parameter to specify if only the Top n, or also approximations for the
* other elements, should be returned
*/
- public static final OptionID TN_ID = OptionID.getOrCreateOptionID("HilOut.tn", "output of Top n or all elements");
+ public static final OptionID TN_ID = new OptionID("HilOut.tn", "output of Top n or all elements");
/**
* Neighborhood size
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/INFLO.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/INFLO.java
index 1fe5fe71..655a0910 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/INFLO.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/INFLO.java
@@ -1,26 +1,27 @@
package de.lmu.ifi.dbs.elki.algorithm.outlier;
-/*
-This file is part of ELKI:
-Environment for Developing KDD-Applications Supported by Index-Structures
-
-Copyright (C) 2012
-Ludwig-Maximilians-Universität München
-Lehr- und Forschungseinheit für Datenbanksysteme
-ELKI Development Team
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
import de.lmu.ifi.dbs.elki.algorithm.AbstractDistanceBasedAlgorithm;
import de.lmu.ifi.dbs.elki.data.type.TypeInformation;
@@ -36,10 +37,10 @@ import de.lmu.ifi.dbs.elki.database.ids.ModifiableDBIDs;
import de.lmu.ifi.dbs.elki.database.query.DatabaseQuery;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.database.query.knn.KNNQuery;
-import de.lmu.ifi.dbs.elki.database.query.knn.KNNResult;
import de.lmu.ifi.dbs.elki.database.relation.MaterializedRelation;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.math.DoubleMinMax;
@@ -81,7 +82,7 @@ public class INFLO<O, D extends NumberDistance<D, ?>> extends AbstractDistanceBa
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(INFLO.class);
+ private static final Logging LOG = Logging.getLogger(INFLO.class);
/**
* Parameter to specify if any object is a Core Object must be a double
@@ -89,7 +90,7 @@ public class INFLO<O, D extends NumberDistance<D, ?>> extends AbstractDistanceBa
* <p>
* see paper "Two-way search method" 3.2
*/
- public static final OptionID M_ID = OptionID.getOrCreateOptionID("inflo.m", "The threshold");
+ public static final OptionID M_ID = new OptionID("inflo.m", "The threshold");
/**
* Holds the value of {@link #M_ID}.
@@ -101,7 +102,7 @@ public class INFLO<O, D extends NumberDistance<D, ?>> extends AbstractDistanceBa
* considered for computing its INFLO_SCORE. must be an integer greater than
* 1.
*/
- public static final OptionID K_ID = OptionID.getOrCreateOptionID("inflo.k", "The number of nearest neighbors of an object to be considered for computing its INFLO_SCORE.");
+ public static final OptionID K_ID = new OptionID("inflo.k", "The number of nearest neighbors of an object to be considered for computing its INFLO_SCORE.");
/**
* Holds the value of {@link #K_ID}.
@@ -140,7 +141,7 @@ public class INFLO<O, D extends NumberDistance<D, ?>> extends AbstractDistanceBa
// density
WritableDoubleDataStore density = DataStoreUtil.makeDoubleStorage(relation.getDBIDs(), DataStoreFactory.HINT_TEMP | DataStoreFactory.HINT_HOT);
// init knns and rnns
- for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
+ for (DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
knns.put(iditer, DBIDUtil.newArray());
rnns.put(iditer, DBIDUtil.newArray());
}
@@ -148,38 +149,34 @@ public class INFLO<O, D extends NumberDistance<D, ?>> extends AbstractDistanceBa
// TODO: use kNN preprocessor?
KNNQuery<O, D> knnQuery = database.getKNNQuery(distFunc, k, DatabaseQuery.HINT_HEAVY_USE);
- for(DBIDIter id = relation.iterDBIDs(); id.valid(); id.advance()) {
+ for (DBIDIter id = relation.iterDBIDs(); id.valid(); id.advance()) {
// if not visited count=0
int count = rnns.get(id).size();
- ModifiableDBIDs s;
- if(!processedIDs.contains(id)) {
+ if (!processedIDs.contains(id)) {
// TODO: use exactly k neighbors?
KNNResult<D> list = knnQuery.getKNNForDBID(id, k);
- knns.get(id).addDBIDs(list.asDBIDs());
+ knns.get(id).addDBIDs(list);
processedIDs.add(id);
- s = knns.get(id);
- density.putDouble(id, 1 / list.get(k - 1).getDistance().doubleValue());
+ density.putDouble(id, 1 / list.getKNNDistance().doubleValue());
}
- else {
- s = knns.get(id);
- }
- for (DBIDIter q = s.iter(); q.valid(); q.advance()) {
- if(!processedIDs.contains(q)) {
+ ModifiableDBIDs s = knns.get(id);
+ for (DBIDIter q = knns.get(id).iter(); q.valid(); q.advance()) {
+ if (!processedIDs.contains(q)) {
// TODO: use exactly k neighbors?
KNNResult<D> listQ = knnQuery.getKNNForDBID(q, k);
- knns.get(q).addDBIDs(listQ.asDBIDs());
+ knns.get(q).addDBIDs(listQ);
density.putDouble(q, 1 / listQ.getKNNDistance().doubleValue());
processedIDs.add(q);
}
- if(knns.get(q).contains(id)) {
+ if (knns.get(q).contains(id)) {
rnns.get(q).add(id);
rnns.get(id).add(q);
count++;
}
}
- if(count >= s.size() * m) {
+ if (count >= s.size() * m) {
pruned.add(id);
}
}
@@ -188,8 +185,8 @@ public class INFLO<O, D extends NumberDistance<D, ?>> extends AbstractDistanceBa
// IF Object is pruned INFLO=1.0
DoubleMinMax inflominmax = new DoubleMinMax();
WritableDoubleDataStore inflos = DataStoreUtil.makeDoubleStorage(relation.getDBIDs(), DataStoreFactory.HINT_STATIC);
- for(DBIDIter id = relation.iterDBIDs(); id.valid(); id.advance()) {
- if(!pruned.contains(id)) {
+ for (DBIDIter id = relation.iterDBIDs(); id.valid(); id.advance()) {
+ if (!pruned.contains(id)) {
ModifiableDBIDs knn = knns.get(id);
ModifiableDBIDs rnn = rnns.get(id);
@@ -205,7 +202,7 @@ public class INFLO<O, D extends NumberDistance<D, ?>> extends AbstractDistanceBa
inflominmax.put(den);
}
- if(pruned.contains(id)) {
+ if (pruned.contains(id)) {
inflos.putDouble(id, 1.0);
inflominmax.put(1.0);
}
@@ -224,15 +221,15 @@ public class INFLO<O, D extends NumberDistance<D, ?>> extends AbstractDistanceBa
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
- * Parameterization class.
- *
- * @author Erich Schubert
- *
- * @apiviz.exclude
+ * Parameterization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
*/
public static class Parameterizer<O, D extends NumberDistance<D, ?>> extends AbstractDistanceBasedAlgorithm.Parameterizer<O, D> {
protected double m = 1.0;
@@ -242,14 +239,16 @@ public class INFLO<O, D extends NumberDistance<D, ?>> extends AbstractDistanceBa
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- final DoubleParameter mP = new DoubleParameter(M_ID, new GreaterConstraint(0.0), 1.0);
- if(config.grab(mP)) {
- m = mP.getValue();
+ final DoubleParameter mP = new DoubleParameter(M_ID, 1.0);
+ mP.addConstraint(new GreaterConstraint(0.0));
+ if (config.grab(mP)) {
+ m = mP.doubleValue();
}
- final IntParameter kP = new IntParameter(K_ID, new GreaterConstraint(1));
- if(config.grab(kP)) {
- k = kP.getValue();
+ final IntParameter kP = new IntParameter(K_ID);
+ kP.addConstraint(new GreaterConstraint(1));
+ if (config.grab(kP)) {
+ k = kP.intValue();
}
}
@@ -258,4 +257,4 @@ public class INFLO<O, D extends NumberDistance<D, ?>> extends AbstractDistanceBa
return new INFLO<O, D>(distanceFunction, m, k);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/KNNOutlier.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/KNNOutlier.java
index 08be944a..4c4873dd 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/KNNOutlier.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/KNNOutlier.java
@@ -1,26 +1,27 @@
package de.lmu.ifi.dbs.elki.algorithm.outlier;
-/*
-This file is part of ELKI:
-Environment for Developing KDD-Applications Supported by Index-Structures
-
-Copyright (C) 2012
-Ludwig-Maximilians-Universität München
-Lehr- und Forschungseinheit für Datenbanksysteme
-ELKI Development Team
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
import de.lmu.ifi.dbs.elki.algorithm.AbstractDistanceBasedAlgorithm;
import de.lmu.ifi.dbs.elki.data.type.TypeInformation;
@@ -32,10 +33,11 @@ import de.lmu.ifi.dbs.elki.database.datastore.WritableDoubleDataStore;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.database.query.knn.KNNQuery;
-import de.lmu.ifi.dbs.elki.database.query.knn.KNNResult;
import de.lmu.ifi.dbs.elki.database.relation.MaterializedRelation;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DoubleDistanceKNNList;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.logging.progress.FiniteProgress;
@@ -77,12 +79,12 @@ public class KNNOutlier<O, D extends NumberDistance<D, ?>> extends AbstractDista
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(KNNOutlier.class);
+ private static final Logging LOG = Logging.getLogger(KNNOutlier.class);
/**
* Parameter to specify the k nearest neighbor
*/
- public static final OptionID K_ID = OptionID.getOrCreateOptionID("knno.k", "k nearest neighbor");
+ public static final OptionID K_ID = new OptionID("knno.k", "k nearest neighbor");
/**
* The parameter k
@@ -107,28 +109,34 @@ public class KNNOutlier<O, D extends NumberDistance<D, ?>> extends AbstractDista
final DistanceQuery<O, D> distanceQuery = database.getDistanceQuery(relation, getDistanceFunction());
KNNQuery<O, D> knnQuery = database.getKNNQuery(distanceQuery, k);
- if(logger.isVerbose()) {
- logger.verbose("Computing the kNN outlier degree (distance to the k nearest neighbor)");
+ if(LOG.isVerbose()) {
+ LOG.verbose("Computing the kNN outlier degree (distance to the k nearest neighbor)");
}
- FiniteProgress progressKNNDistance = logger.isVerbose() ? new FiniteProgress("kNN distance for objects", relation.size(), logger) : null;
+ FiniteProgress progressKNNDistance = LOG.isVerbose() ? new FiniteProgress("kNN distance for objects", relation.size(), LOG) : null;
DoubleMinMax minmax = new DoubleMinMax();
WritableDoubleDataStore knno_score = DataStoreUtil.makeDoubleStorage(relation.getDBIDs(), DataStoreFactory.HINT_STATIC);
// compute distance to the k nearest neighbor.
- for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
+ for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
// distance to the kth nearest neighbor
final KNNResult<D> knns = knnQuery.getKNNForDBID(iditer, k);
- double dkn = knns.getKNNDistance().doubleValue();
- knno_score.putDouble(iditer, dkn);
+ final double dkn;
+ if(knns instanceof DoubleDistanceKNNList) {
+ dkn = ((DoubleDistanceKNNList) knns).doubleKNNDistance();
+ }
+ else {
+ dkn = knns.getKNNDistance().doubleValue();
+ }
+ knno_score.putDouble(iditer, dkn);
minmax.put(dkn);
if(progressKNNDistance != null) {
- progressKNNDistance.incrementProcessed(logger);
+ progressKNNDistance.incrementProcessed(LOG);
}
}
if(progressKNNDistance != null) {
- progressKNNDistance.ensureCompleted(logger);
+ progressKNNDistance.ensureCompleted(LOG);
}
Relation<Double> scoreres = new MaterializedRelation<Double>("kNN Outlier Score", "knn-outlier", TypeUtil.DOUBLE, knno_score, relation.getDBIDs());
OutlierScoreMeta meta = new BasicOutlierScoreMeta(minmax.getMin(), minmax.getMax(), 0.0, Double.POSITIVE_INFINITY, 0.0);
@@ -142,15 +150,15 @@ public class KNNOutlier<O, D extends NumberDistance<D, ?>> extends AbstractDista
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
- * Parameterization class.
- *
- * @author Erich Schubert
- *
- * @apiviz.exclude
+ * Parameterization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
*/
public static class Parameterizer<O, D extends NumberDistance<D, ?>> extends AbstractDistanceBasedAlgorithm.Parameterizer<O, D> {
protected int k = 0;
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/KNNWeightOutlier.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/KNNWeightOutlier.java
index cb3ca2f1..e7eeeb9c 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/KNNWeightOutlier.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/KNNWeightOutlier.java
@@ -31,13 +31,15 @@ import de.lmu.ifi.dbs.elki.database.datastore.DataStoreFactory;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreUtil;
import de.lmu.ifi.dbs.elki.database.datastore.WritableDoubleDataStore;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.database.query.knn.KNNQuery;
-import de.lmu.ifi.dbs.elki.database.query.knn.KNNResult;
import de.lmu.ifi.dbs.elki.database.relation.MaterializedRelation;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResultIter;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DoubleDistanceDBIDResultIter;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DoubleDistanceKNNList;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.logging.progress.FiniteProgress;
@@ -74,17 +76,17 @@ public class KNNWeightOutlier<O, D extends NumberDistance<D, ?>> extends Abstrac
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(KNNWeightOutlier.class);
+ private static final Logging LOG = Logging.getLogger(KNNWeightOutlier.class);
/**
* Parameter to specify the k nearest neighbor
*/
- public static final OptionID K_ID = OptionID.getOrCreateOptionID("knnwod.k", "k nearest neighbor");
+ public static final OptionID K_ID = new OptionID("knnwod.k", "k nearest neighbor");
/**
* The kNN query used.
*/
- public static final OptionID KNNQUERY_ID = OptionID.getOrCreateOptionID("knnwod.knnquery", "kNN query to use");
+ public static final OptionID KNNQUERY_ID = new OptionID("knnwod.knnquery", "kNN query to use");
/**
* Holds the value of {@link #K_ID}.
@@ -109,33 +111,40 @@ public class KNNWeightOutlier<O, D extends NumberDistance<D, ?>> extends Abstrac
final DistanceQuery<O, D> distanceQuery = database.getDistanceQuery(relation, getDistanceFunction());
KNNQuery<O, D> knnQuery = database.getKNNQuery(distanceQuery, k);
- if(logger.isVerbose()) {
- logger.verbose("computing outlier degree(sum of the distances to the k nearest neighbors");
+ if(LOG.isVerbose()) {
+ LOG.verbose("computing outlier degree(sum of the distances to the k nearest neighbors");
}
- FiniteProgress progressKNNWeight = logger.isVerbose() ? new FiniteProgress("KNNWOD_KNNWEIGHT for objects", relation.size(), logger) : null;
+ FiniteProgress progressKNNWeight = LOG.isVerbose() ? new FiniteProgress("KNNWOD_KNNWEIGHT for objects", relation.size(), LOG) : null;
DoubleMinMax minmax = new DoubleMinMax();
// compute distance to the k nearest neighbor. n objects with the highest
// distance are flagged as outliers
WritableDoubleDataStore knnw_score = DataStoreUtil.makeDoubleStorage(relation.getDBIDs(), DataStoreFactory.HINT_STATIC);
- for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
+ for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
// compute sum of the distances to the k nearest neighbors
final KNNResult<D> knn = knnQuery.getKNNForDBID(iditer, k);
double skn = 0;
- for(DistanceResultPair<D> r : knn) {
- skn += r.getDistance().doubleValue();
+ if(knn instanceof DoubleDistanceKNNList) {
+ for(DoubleDistanceDBIDResultIter neighbor = ((DoubleDistanceKNNList) knn).iter(); neighbor.valid(); neighbor.advance()) {
+ skn += neighbor.doubleDistance();
+ }
+ }
+ else {
+ for(DistanceDBIDResultIter<D> neighbor = knn.iter(); neighbor.valid(); neighbor.advance()) {
+ skn += neighbor.getDistance().doubleValue();
+ }
}
knnw_score.putDouble(iditer, skn);
minmax.put(skn);
if(progressKNNWeight != null) {
- progressKNNWeight.incrementProcessed(logger);
+ progressKNNWeight.incrementProcessed(LOG);
}
}
if(progressKNNWeight != null) {
- progressKNNWeight.ensureCompleted(logger);
+ progressKNNWeight.ensureCompleted(LOG);
}
Relation<Double> res = new MaterializedRelation<Double>("Weighted kNN Outlier Score", "knnw-outlier", TypeUtil.DOUBLE, knnw_score, relation.getDBIDs());
@@ -150,7 +159,7 @@ public class KNNWeightOutlier<O, D extends NumberDistance<D, ?>> extends Abstrac
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/LDF.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/LDF.java
new file mode 100644
index 00000000..4ce0313e
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/LDF.java
@@ -0,0 +1,342 @@
+package de.lmu.ifi.dbs.elki.algorithm.outlier;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import de.lmu.ifi.dbs.elki.algorithm.AbstractDistanceBasedAlgorithm;
+import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.data.type.CombinedTypeInformation;
+import de.lmu.ifi.dbs.elki.data.type.TypeInformation;
+import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
+import de.lmu.ifi.dbs.elki.database.QueryUtil;
+import de.lmu.ifi.dbs.elki.database.datastore.DataStoreFactory;
+import de.lmu.ifi.dbs.elki.database.datastore.DataStoreUtil;
+import de.lmu.ifi.dbs.elki.database.datastore.WritableDoubleDataStore;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
+import de.lmu.ifi.dbs.elki.database.query.DatabaseQuery;
+import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
+import de.lmu.ifi.dbs.elki.database.query.knn.KNNQuery;
+import de.lmu.ifi.dbs.elki.database.query.knn.PreprocessorKNNQuery;
+import de.lmu.ifi.dbs.elki.database.relation.MaterializedRelation;
+import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
+import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResultIter;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DoubleDistanceDBIDResultIter;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DoubleDistanceKNNList;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
+import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance;
+import de.lmu.ifi.dbs.elki.index.preprocessed.knn.MaterializeKNNPreprocessor;
+import de.lmu.ifi.dbs.elki.logging.Logging;
+import de.lmu.ifi.dbs.elki.logging.progress.FiniteProgress;
+import de.lmu.ifi.dbs.elki.logging.progress.StepProgress;
+import de.lmu.ifi.dbs.elki.math.DoubleMinMax;
+import de.lmu.ifi.dbs.elki.math.statistics.GaussianKernelDensityFunction;
+import de.lmu.ifi.dbs.elki.math.statistics.KernelDensityFunction;
+import de.lmu.ifi.dbs.elki.result.outlier.BasicOutlierScoreMeta;
+import de.lmu.ifi.dbs.elki.result.outlier.OutlierResult;
+import de.lmu.ifi.dbs.elki.result.outlier.OutlierScoreMeta;
+import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.DoubleParameter;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ObjectParameter;
+
+/**
+ * Outlier Detection with Kernel Density Functions.
+ *
+ * A variation of LOF which uses kernel density estimation, but in contrast to
+ * {@link SimpleKernelDensityLOF} also uses the reachability concept of LOF.
+ *
+ * Reference:
+ * <p>
+ * Outlier Detection with Kernel Density Functions.<br/>
+ * L. J. Latecki, A. Lazarevic, D. Pokrajac<br />
+ * Machine Learning and Data Mining in Pattern Recognition 2007
+ * </p>
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.has KNNQuery
+ * @apiviz.has KernelDensityFunction
+ *
+ * @param <O> the type of objects handled by this Algorithm
+ * @param <D> Distance type
+ */
+@Reference(authors = "L. J. Latecki, A. Lazarevic, D. Pokrajac", title = "Outlier Detection with Kernel Density Functions", booktitle = "Machine Learning and Data Mining in Pattern Recognition", url = "http://dx.doi.org/10.1007/978-3-540-73499-4_6")
+public class LDF<O extends NumberVector<?>, D extends NumberDistance<D, ?>> extends AbstractDistanceBasedAlgorithm<O, D, OutlierResult> implements OutlierAlgorithm {
+ /**
+ * The logger for this class.
+ */
+ private static final Logging LOG = Logging.getLogger(LDF.class);
+
+ /**
+ * Parameter k.
+ */
+ protected int k;
+
+ /**
+ * Bandwidth scaling factor.
+ */
+ protected double h = 1;
+
+ /**
+ * Scaling constant, to limit value range to 1/c
+ */
+ protected double c = 0.1;
+
+ /**
+ * Kernel density function
+ */
+ private KernelDensityFunction kernel;
+
+ /**
+ * Constructor.
+ *
+ * @param k the value of k
+ * @param kernel Kernel function
+ * @param h Kernel bandwidth scaling
+ * @param c Score scaling parameter
+ */
+ public LDF(int k, DistanceFunction<? super O, D> distance, KernelDensityFunction kernel, double h, double c) {
+ super(distance);
+ this.k = k + 1;
+ this.kernel = kernel;
+ this.h = h;
+ this.c = c;
+ }
+
+ /**
+ * Run the naive kernel density LOF algorithm.
+ *
+ * @param relation Data to process
+ * @return LOF outlier result
+ */
+ public OutlierResult run(Relation<O> relation) {
+ StepProgress stepprog = LOG.isVerbose() ? new StepProgress("LDF", 3) : null;
+
+ final int dim = RelationUtil.dimensionality(relation);
+
+ DBIDs ids = relation.getDBIDs();
+
+ // "HEAVY" flag for KNN Query since it is used more than once
+ KNNQuery<O, D> knnq = QueryUtil.getKNNQuery(relation, getDistanceFunction(), k, DatabaseQuery.HINT_HEAVY_USE, DatabaseQuery.HINT_OPTIMIZED_ONLY, DatabaseQuery.HINT_NO_CACHE);
+ // No optimized kNN query - use a preprocessor!
+ if (!(knnq instanceof PreprocessorKNNQuery)) {
+ if (stepprog != null) {
+ stepprog.beginStep(1, "Materializing neighborhoods w.r.t. distance function.", LOG);
+ }
+ MaterializeKNNPreprocessor<O, D> preproc = new MaterializeKNNPreprocessor<O, D>(relation, getDistanceFunction(), k);
+ relation.getDatabase().addIndex(preproc);
+ DistanceQuery<O, D> rdq = relation.getDatabase().getDistanceQuery(relation, getDistanceFunction());
+ knnq = preproc.getKNNQuery(rdq, k);
+ }
+
+ // Compute LRDs
+ if (stepprog != null) {
+ stepprog.beginStep(2, "Computing LDEs.", LOG);
+ }
+ WritableDoubleDataStore ldes = DataStoreUtil.makeDoubleStorage(ids, DataStoreFactory.HINT_HOT | DataStoreFactory.HINT_TEMP);
+ FiniteProgress densProgress = LOG.isVerbose() ? new FiniteProgress("Densities", ids.size(), LOG) : null;
+ for (DBIDIter it = ids.iter(); it.valid(); it.advance()) {
+ final KNNResult<D> neighbors = knnq.getKNNForDBID(it, k);
+ double sum = 0.0;
+ int count = 0;
+ if (neighbors instanceof DoubleDistanceKNNList) {
+ // Fast version for double distances
+ for (DoubleDistanceDBIDResultIter neighbor = ((DoubleDistanceKNNList) neighbors).iter(); neighbor.valid(); neighbor.advance()) {
+ if (DBIDUtil.equal(neighbor, it)) {
+ continue;
+ }
+ double nkdist = ((DoubleDistanceKNNList) knnq.getKNNForDBID(neighbor, k)).doubleKNNDistance();
+
+ final double v = Math.max(nkdist, neighbor.doubleDistance()) / (h * nkdist);
+ sum += kernel.density(v) / Math.pow(h * nkdist, dim);
+ count++;
+ }
+ } else {
+ for (DistanceDBIDResultIter<D> neighbor = neighbors.iter(); neighbor.valid(); neighbor.advance()) {
+ if (DBIDUtil.equal(neighbor, it)) {
+ continue;
+ }
+ double nkdist = knnq.getKNNForDBID(neighbor, k).getKNNDistance().doubleValue();
+ final double v = Math.max(nkdist, neighbor.getDistance().doubleValue()) / (h * nkdist);
+ sum += kernel.density(v) / Math.pow(h * nkdist, dim);
+ count++;
+ }
+ }
+ ldes.putDouble(it, sum / count);
+ if (densProgress != null) {
+ densProgress.incrementProcessed(LOG);
+ }
+ }
+ if (densProgress != null) {
+ densProgress.ensureCompleted(LOG);
+ }
+
+ // Compute local density factors.
+ if (stepprog != null) {
+ stepprog.beginStep(3, "Computing LDFs.", LOG);
+ }
+ WritableDoubleDataStore ldfs = DataStoreUtil.makeDoubleStorage(ids, DataStoreFactory.HINT_STATIC);
+ // track the maximum value for normalization.
+ DoubleMinMax lofminmax = new DoubleMinMax();
+
+ FiniteProgress progressLOFs = LOG.isVerbose() ? new FiniteProgress("Local Density Factors", ids.size(), LOG) : null;
+ for (DBIDIter it = ids.iter(); it.valid(); it.advance()) {
+ final double lrdp = ldes.doubleValue(it);
+ final KNNResult<D> neighbors = knnq.getKNNForDBID(it, k);
+ double sum = 0.0;
+ int count = 0;
+ for (DBIDIter neighbor = neighbors.iter(); neighbor.valid(); neighbor.advance()) {
+ // skip the point itself
+ if (DBIDUtil.equal(neighbor, it)) {
+ continue;
+ }
+ sum += ldes.doubleValue(neighbor);
+ count++;
+ }
+ sum /= count;
+ final double div = lrdp + c * sum;
+ double ldf = (div > 0) ? sum / div : 0;
+ ldfs.putDouble(it, ldf);
+ // update minimum and maximum
+ lofminmax.put(ldf);
+
+ if (progressLOFs != null) {
+ progressLOFs.incrementProcessed(LOG);
+ }
+ }
+ if (progressLOFs != null) {
+ progressLOFs.ensureCompleted(LOG);
+ }
+
+ if (stepprog != null) {
+ stepprog.setCompleted(LOG);
+ }
+
+ // Build result representation.
+ Relation<Double> scoreResult = new MaterializedRelation<Double>("Local Density Factor", "ldf-outlier", TypeUtil.DOUBLE, ldfs, ids);
+ OutlierScoreMeta scoreMeta = new BasicOutlierScoreMeta(lofminmax.getMin(), lofminmax.getMax(), 0.0, 1. / c, 1 / (1 + c));
+ OutlierResult result = new OutlierResult(scoreMeta, scoreResult);
+
+ return result;
+ }
+
+ @Override
+ public TypeInformation[] getInputTypeRestriction() {
+ return TypeUtil.array(new CombinedTypeInformation(getDistanceFunction().getInputTypeRestriction(), TypeUtil.NUMBER_VECTOR_FIELD));
+ }
+
+ @Override
+ protected Logging getLogger() {
+ return LOG;
+ }
+
+ /**
+ * Parameterization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ *
+ * @param <O> vector type
+ * @param <D> distance type
+ */
+ public static class Parameterizer<O extends NumberVector<?>, D extends NumberDistance<D, ?>> extends AbstractDistanceBasedAlgorithm.Parameterizer<O, D> {
+ /**
+ * Option ID for kernel.
+ */
+ public static final OptionID KERNEL_ID = new OptionID("ldf.kernel", "Kernel to use for LDF.");
+
+ /**
+ * Option ID for k
+ */
+ public static final OptionID K_ID = new OptionID("ldf.k", "Number of neighbors to use for LDF.");
+
+ /**
+ * Option ID for h - kernel bandwidth scaling
+ */
+ public static final OptionID H_ID = new OptionID("ldf.h", "Kernel bandwidth multiplier for LDF.");
+
+ /**
+ * Option ID for c
+ */
+ public static final OptionID C_ID = new OptionID("ldf.c", "Score scaling parameter for LDF.");
+
+ /**
+ * The neighborhood size to use.
+ */
+ protected int k = 2;
+
+ /**
+ * Kernel density function parameter
+ */
+ KernelDensityFunction kernel;
+
+ /**
+ * Bandwidth scaling factor.
+ */
+ protected double h = 1;
+
+ /**
+ * Scaling constant, to limit value range to 1/c
+ */
+ protected double c = 0.1;
+
+ @Override
+ protected void makeOptions(Parameterization config) {
+ super.makeOptions(config);
+
+ final IntParameter pK = new IntParameter(K_ID);
+ pK.addConstraint(new GreaterConstraint(1));
+ if (config.grab(pK)) {
+ k = pK.getValue();
+ }
+
+ ObjectParameter<KernelDensityFunction> kernelP = new ObjectParameter<KernelDensityFunction>(KERNEL_ID, KernelDensityFunction.class, GaussianKernelDensityFunction.class);
+ if (config.grab(kernelP)) {
+ kernel = kernelP.instantiateClass(config);
+ }
+
+ DoubleParameter hP = new DoubleParameter(H_ID);
+ if (config.grab(hP)) {
+ h = hP.doubleValue();
+ }
+
+ DoubleParameter cP = new DoubleParameter(C_ID, 0.1);
+ if (config.grab(cP)) {
+ c = cP.doubleValue();
+ }
+ }
+
+ @Override
+ protected LDF<O, D> makeInstance() {
+ return new LDF<O, D>(k, distanceFunction, kernel, h, c);
+ }
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/LDOF.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/LDOF.java
index 84f5dcc6..fbbfe484 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/LDOF.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/LDOF.java
@@ -31,13 +31,14 @@ import de.lmu.ifi.dbs.elki.database.datastore.DataStoreFactory;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreUtil;
import de.lmu.ifi.dbs.elki.database.datastore.WritableDoubleDataStore;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.database.query.knn.KNNQuery;
-import de.lmu.ifi.dbs.elki.database.query.knn.KNNResult;
import de.lmu.ifi.dbs.elki.database.relation.MaterializedRelation;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResultIter;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.logging.progress.FiniteProgress;
@@ -81,13 +82,13 @@ public class LDOF<O, D extends NumberDistance<D, ?>> extends AbstractDistanceBas
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(LDOF.class);
+ private static final Logging LOG = Logging.getLogger(LDOF.class);
/**
* Parameter to specify the number of nearest neighbors of an object to be
* considered for computing its LDOF_SCORE, must be an integer greater than 1.
*/
- public static final OptionID K_ID = OptionID.getOrCreateOptionID("ldof.k", "The number of nearest neighbors of an object to be considered for computing its LDOF_SCORE.");
+ public static final OptionID K_ID = new OptionID("ldof.k", "The number of nearest neighbors of an object to be considered for computing its LDOF_SCORE.");
/**
* The baseline for LDOF values. The paper gives 0.5 for uniform
@@ -128,21 +129,22 @@ public class LDOF<O, D extends NumberDistance<D, ?>> extends AbstractDistanceBas
WritableDoubleDataStore ldofs = DataStoreUtil.makeDoubleStorage(relation.getDBIDs(), DataStoreFactory.HINT_HOT | DataStoreFactory.HINT_TEMP);
// compute LOF_SCORE of each db object
- if(logger.isVerbose()) {
- logger.verbose("Computing LDOFs");
+ if(LOG.isVerbose()) {
+ LOG.verbose("Computing LDOFs");
}
- FiniteProgress progressLDOFs = logger.isVerbose() ? new FiniteProgress("LDOF_SCORE for objects", relation.size(), logger) : null;
+ FiniteProgress progressLDOFs = LOG.isVerbose() ? new FiniteProgress("LDOF_SCORE for objects", relation.size(), LOG) : null;
Mean dxp = new Mean(), Dxp = new Mean();
for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
KNNResult<D> neighbors = knnQuery.getKNNForDBID(iditer, k);
// skip the point itself
dxp.reset(); Dxp.reset();
- for(DistanceResultPair<D> neighbor1 : neighbors) {
- if(!neighbor1.sameDBID(iditer)) {
+ // TODO: optimize for double distances
+ for (DistanceDBIDResultIter<D> neighbor1 = neighbors.iter(); neighbor1.valid(); neighbor1.advance()) {
+ if(!DBIDUtil.equal(neighbor1, iditer)) {
dxp.put(neighbor1.getDistance().doubleValue());
- for(DistanceResultPair<D> neighbor2 : neighbors) {
- if(!neighbor1.sameDBID(neighbor2) && !neighbor2.sameDBID(iditer)) {
+ for (DistanceDBIDResultIter<D> neighbor2 = neighbors.iter(); neighbor2.valid(); neighbor2.advance()) {
+ if(!DBIDUtil.equal(neighbor1, neighbor2) && !DBIDUtil.equal(neighbor2, iditer)) {
Dxp.put(distFunc.distance(neighbor1, neighbor2).doubleValue());
}
}
@@ -157,11 +159,11 @@ public class LDOF<O, D extends NumberDistance<D, ?>> extends AbstractDistanceBas
ldofminmax.put(ldof);
if(progressLDOFs != null) {
- progressLDOFs.incrementProcessed(logger);
+ progressLDOFs.incrementProcessed(LOG);
}
}
if(progressLDOFs != null) {
- progressLDOFs.ensureCompleted(logger);
+ progressLDOFs.ensureCompleted(LOG);
}
// Build result representation.
@@ -177,7 +179,7 @@ public class LDOF<O, D extends NumberDistance<D, ?>> extends AbstractDistanceBas
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -193,7 +195,8 @@ public class LDOF<O, D extends NumberDistance<D, ?>> extends AbstractDistanceBas
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- final IntParameter kP = new IntParameter(K_ID, new GreaterConstraint(1));
+ final IntParameter kP = new IntParameter(K_ID);
+ kP.addConstraint(new GreaterConstraint(1));
if(config.grab(kP)) {
k = kP.getValue();
}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/LOCI.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/LOCI.java
index a04aa041..ba9ad20e 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/LOCI.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/LOCI.java
@@ -36,13 +36,14 @@ import de.lmu.ifi.dbs.elki.database.datastore.DataStoreUtil;
import de.lmu.ifi.dbs.elki.database.datastore.WritableDataStore;
import de.lmu.ifi.dbs.elki.database.datastore.WritableDoubleDataStore;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
-import de.lmu.ifi.dbs.elki.database.query.DistanceDBIDResult;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
+import de.lmu.ifi.dbs.elki.database.ids.DistanceDBIDPair;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.database.query.range.RangeQuery;
import de.lmu.ifi.dbs.elki.database.relation.MaterializedRelation;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResultIter;
import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.logging.progress.FiniteProgress;
@@ -64,9 +65,7 @@ import de.lmu.ifi.dbs.elki.utilities.pairs.DoubleIntPair;
/**
* Fast Outlier Detection Using the "Local Correlation Integral".
*
- * Exact implementation only, not aLOCI.
- *
- * TODO: add aLOCI
+ * Exact implementation only, not aLOCI. See {@link ALOCI}
*
* Outlier detection using multiple epsilon neighborhoods.
*
@@ -88,23 +87,23 @@ public class LOCI<O, D extends NumberDistance<D, ?>> extends AbstractDistanceBas
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(LOCI.class);
+ private static final Logging LOG = Logging.getLogger(LOCI.class);
/**
* Parameter to specify the maximum radius of the neighborhood to be
* considered, must be suitable to the distance function specified.
*/
- public static final OptionID RMAX_ID = OptionID.getOrCreateOptionID("loci.rmax", "The maximum radius of the neighborhood to be considered.");
+ public static final OptionID RMAX_ID = new OptionID("loci.rmax", "The maximum radius of the neighborhood to be considered.");
/**
* Parameter to specify the minimum neighborhood size
*/
- public static final OptionID NMIN_ID = OptionID.getOrCreateOptionID("loci.nmin", "Minimum neighborhood size to be considered.");
+ public static final OptionID NMIN_ID = new OptionID("loci.nmin", "Minimum neighborhood size to be considered.");
/**
* Parameter to specify the averaging neighborhood scaling.
*/
- public static final OptionID ALPHA_ID = OptionID.getOrCreateOptionID("loci.alpha", "Scaling factor for averaging neighborhood");
+ public static final OptionID ALPHA_ID = new OptionID("loci.alpha", "Scaling factor for averaging neighborhood");
/**
* Holds the value of {@link #RMAX_ID}.
@@ -147,16 +146,16 @@ public class LOCI<O, D extends NumberDistance<D, ?>> extends AbstractDistanceBas
DistanceQuery<O, D> distFunc = database.getDistanceQuery(relation, getDistanceFunction());
RangeQuery<O, D> rangeQuery = database.getRangeQuery(distFunc);
- FiniteProgress progressPreproc = logger.isVerbose() ? new FiniteProgress("LOCI preprocessing", relation.size(), logger) : null;
+ FiniteProgress progressPreproc = LOG.isVerbose() ? new FiniteProgress("LOCI preprocessing", relation.size(), LOG) : null;
// LOCI preprocessing step
WritableDataStore<ArrayList<DoubleIntPair>> interestingDistances = DataStoreUtil.makeStorage(relation.getDBIDs(), DataStoreFactory.HINT_TEMP | DataStoreFactory.HINT_SORTED, ArrayList.class);
for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
DistanceDBIDResult<D> neighbors = rangeQuery.getRangeForDBID(iditer, rmax);
// build list of critical distances
- ArrayList<DoubleIntPair> cdist = new ArrayList<DoubleIntPair>(neighbors.size() * 2);
+ ArrayList<DoubleIntPair> cdist = new ArrayList<DoubleIntPair>(neighbors.size() << 1);
{
for(int i = 0; i < neighbors.size(); i++) {
- DistanceResultPair<D> r = neighbors.get(i);
+ DistanceDBIDPair<D> r = neighbors.get(i);
if(i + 1 < neighbors.size() && r.getDistance().compareTo(neighbors.get(i + 1).getDistance()) == 0) {
continue;
}
@@ -182,14 +181,14 @@ public class LOCI<O, D extends NumberDistance<D, ?>> extends AbstractDistanceBas
interestingDistances.put(iditer, cdist);
if(progressPreproc != null) {
- progressPreproc.incrementProcessed(logger);
+ progressPreproc.incrementProcessed(LOG);
}
}
if(progressPreproc != null) {
- progressPreproc.ensureCompleted(logger);
+ progressPreproc.ensureCompleted(LOG);
}
// LOCI main step
- FiniteProgress progressLOCI = logger.isVerbose() ? new FiniteProgress("LOCI scores", relation.size(), logger) : null;
+ FiniteProgress progressLOCI = LOG.isVerbose() ? new FiniteProgress("LOCI scores", relation.size(), LOG) : null;
WritableDoubleDataStore mdef_norm = DataStoreUtil.makeDoubleStorage(relation.getDBIDs(), DataStoreFactory.HINT_STATIC);
WritableDoubleDataStore mdef_radius = DataStoreUtil.makeDoubleStorage(relation.getDBIDs(), DataStoreFactory.HINT_STATIC);
DoubleMinMax minmax = new DoubleMinMax();
@@ -204,9 +203,8 @@ public class LOCI<O, D extends NumberDistance<D, ?>> extends AbstractDistanceBas
if(maxneig >= nmin) {
D range = distFunc.getDistanceFactory().fromDouble(maxdist);
// Compute the largest neighborhood we will need.
- List<DistanceResultPair<D>> maxneighbors = rangeQuery.getRangeForDBID(iditer, range);
- // Ensure the set is sorted. Should be a no-op with most indexes.
- Collections.sort(maxneighbors);
+ DistanceDBIDResult<D> maxneighbors = rangeQuery.getRangeForDBID(iditer, range);
+ // TODO: Ensure the set is sorted. Should be a no-op with most indexes.
// For any critical distance, compute the normalized MDEF score.
for(DoubleIntPair c : cdist) {
// Only start when minimum size is fulfilled
@@ -219,12 +217,13 @@ public class LOCI<O, D extends NumberDistance<D, ?>> extends AbstractDistanceBas
final int n_alphar = elementsAtRadius(cdist, alpha_r);
// compute \hat{n}(p_i, r, \alpha) and the corresponding \simga_{MDEF}
MeanVariance mv_n_r_alpha = new MeanVariance();
- for(DistanceResultPair<D> ne : maxneighbors) {
+ // TODO: optimize for double distances
+ for (DistanceDBIDResultIter<D> neighbor = maxneighbors.iter(); neighbor.valid(); neighbor.advance()) {
// Stop at radius r
- if(ne.getDistance().doubleValue() > r) {
+ if(neighbor.getDistance().doubleValue() > r) {
break;
}
- int rn_alphar = elementsAtRadius(interestingDistances.get(ne), alpha_r);
+ int rn_alphar = elementsAtRadius(interestingDistances.get(neighbor), alpha_r);
mv_n_r_alpha.put(rn_alphar);
}
// We only use the average and standard deviation
@@ -251,11 +250,11 @@ public class LOCI<O, D extends NumberDistance<D, ?>> extends AbstractDistanceBas
mdef_radius.putDouble(iditer, maxnormr);
minmax.put(maxmdefnorm);
if(progressLOCI != null) {
- progressLOCI.incrementProcessed(logger);
+ progressLOCI.incrementProcessed(LOG);
}
}
if(progressLOCI != null) {
- progressLOCI.ensureCompleted(logger);
+ progressLOCI.ensureCompleted(LOG);
}
Relation<Double> scoreResult = new MaterializedRelation<Double>("LOCI normalized MDEF", "loci-mdef-outlier", TypeUtil.DOUBLE, mdef_norm, relation.getDBIDs());
OutlierScoreMeta scoreMeta = new QuotientOutlierScoreMeta(minmax.getMin(), minmax.getMax(), 0.0, Double.POSITIVE_INFINITY, 0.0);
@@ -293,7 +292,7 @@ public class LOCI<O, D extends NumberDistance<D, ?>> extends AbstractDistanceBas
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -335,4 +334,4 @@ public class LOCI<O, D extends NumberDistance<D, ?>> extends AbstractDistanceBas
return new LOCI<O, D>(distanceFunction, rmax, nmin, alpha);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/LOF.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/LOF.java
index 5aba41ec..66bed47a 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/LOF.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/LOF.java
@@ -29,29 +29,31 @@ import de.lmu.ifi.dbs.elki.data.type.CombinedTypeInformation;
import de.lmu.ifi.dbs.elki.data.type.TypeInformation;
import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
import de.lmu.ifi.dbs.elki.database.QueryUtil;
-import de.lmu.ifi.dbs.elki.database.datastore.DataStore;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreFactory;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreUtil;
+import de.lmu.ifi.dbs.elki.database.datastore.DoubleDataStore;
import de.lmu.ifi.dbs.elki.database.datastore.WritableDoubleDataStore;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.query.DatabaseQuery;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.database.query.knn.KNNQuery;
-import de.lmu.ifi.dbs.elki.database.query.knn.KNNResult;
import de.lmu.ifi.dbs.elki.database.query.knn.PreprocessorKNNQuery;
import de.lmu.ifi.dbs.elki.database.query.rknn.RKNNQuery;
import de.lmu.ifi.dbs.elki.database.relation.MaterializedRelation;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResultIter;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DoubleDistanceDBIDResultIter;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DoubleDistanceKNNList;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance;
import de.lmu.ifi.dbs.elki.index.preprocessed.knn.MaterializeKNNPreprocessor;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.logging.progress.FiniteProgress;
import de.lmu.ifi.dbs.elki.logging.progress.StepProgress;
import de.lmu.ifi.dbs.elki.math.DoubleMinMax;
-import de.lmu.ifi.dbs.elki.math.Mean;
import de.lmu.ifi.dbs.elki.result.outlier.OutlierResult;
import de.lmu.ifi.dbs.elki.result.outlier.OutlierScoreMeta;
import de.lmu.ifi.dbs.elki.result.outlier.QuotientOutlierScoreMeta;
@@ -118,19 +120,19 @@ public class LOF<O, D extends NumberDistance<D, ?>> extends AbstractAlgorithm<Ou
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(LOF.class);
+ private static final Logging LOG = Logging.getLogger(LOF.class);
/**
* The distance function to determine the reachability distance between
* database objects.
*/
- public static final OptionID REACHABILITY_DISTANCE_FUNCTION_ID = OptionID.getOrCreateOptionID("lof.reachdistfunction", "Distance function to determine the reachability distance between database objects.");
+ public static final OptionID REACHABILITY_DISTANCE_FUNCTION_ID = new OptionID("lof.reachdistfunction", "Distance function to determine the reachability distance between database objects.");
/**
* Parameter to specify the number of nearest neighbors of an object to be
* considered for computing its LOF_SCORE, must be an integer greater than 1.
*/
- public static final OptionID K_ID = OptionID.getOrCreateOptionID("lof.k", "The number of nearest neighbors of an object to be considered for computing its LOF_SCORE.");
+ public static final OptionID K_ID = new OptionID("lof.k", "The number of nearest neighbors of an object to be considered for computing its LOF_SCORE.");
/**
* Holds the value of {@link #K_ID}.
@@ -189,9 +191,10 @@ public class LOF<O, D extends NumberDistance<D, ?>> extends AbstractAlgorithm<Ou
* calling {@link #doRunInTime}.
*
* @param relation Data to process
+ * @return LOF outlier result
*/
public OutlierResult run(Relation<O> relation) {
- StepProgress stepprog = logger.isVerbose() ? new StepProgress("LOF", 3) : null;
+ StepProgress stepprog = LOG.isVerbose() ? new StepProgress("LOF", 3) : null;
Pair<KNNQuery<O, D>, KNNQuery<O, D>> pair = getKNNQueries(relation, stepprog);
KNNQuery<O, D> kNNRefer = pair.getFirst();
KNNQuery<O, D> kNNReach = pair.getSecond();
@@ -209,13 +212,12 @@ public class LOF<O, D extends NumberDistance<D, ?>> extends AbstractAlgorithm<Ou
// "HEAVY" flag for knnReach since it is used more than once
KNNQuery<O, D> knnReach = QueryUtil.getKNNQuery(relation, reachabilityDistanceFunction, k, DatabaseQuery.HINT_HEAVY_USE, DatabaseQuery.HINT_OPTIMIZED_ONLY, DatabaseQuery.HINT_NO_CACHE);
// No optimized kNN query - use a preprocessor!
- if(!(knnReach instanceof PreprocessorKNNQuery)) {
- if(stepprog != null) {
- if(neighborhoodDistanceFunction.equals(reachabilityDistanceFunction)) {
- stepprog.beginStep(1, "Materializing neighborhoods w.r.t. reference neighborhood distance function.", logger);
- }
- else {
- stepprog.beginStep(1, "Not materializing neighborhoods w.r.t. reference neighborhood distance function, but materializing neighborhoods w.r.t. reachability distance function.", logger);
+ if (!(knnReach instanceof PreprocessorKNNQuery)) {
+ if (stepprog != null) {
+ if (neighborhoodDistanceFunction.equals(reachabilityDistanceFunction)) {
+ stepprog.beginStep(1, "Materializing neighborhoods w.r.t. reference neighborhood distance function.", LOG);
+ } else {
+ stepprog.beginStep(1, "Not materializing neighborhoods w.r.t. reference neighborhood distance function, but materializing neighborhoods w.r.t. reachability distance function.", LOG);
}
}
MaterializeKNNPreprocessor<O, D> preproc = new MaterializeKNNPreprocessor<O, D>(relation, reachabilityDistanceFunction, k);
@@ -226,10 +228,9 @@ public class LOF<O, D extends NumberDistance<D, ?>> extends AbstractAlgorithm<Ou
// knnReach is only used once
KNNQuery<O, D> knnRefer;
- if(neighborhoodDistanceFunction == reachabilityDistanceFunction || neighborhoodDistanceFunction.equals(reachabilityDistanceFunction)) {
+ if (neighborhoodDistanceFunction == reachabilityDistanceFunction || neighborhoodDistanceFunction.equals(reachabilityDistanceFunction)) {
knnRefer = knnReach;
- }
- else {
+ } else {
// do not materialize the first neighborhood, since it is used only once
knnRefer = QueryUtil.getKNNQuery(relation, neighborhoodDistanceFunction, k);
}
@@ -251,30 +252,30 @@ public class LOF<O, D extends NumberDistance<D, ?>> extends AbstractAlgorithm<Ou
*/
protected LOFResult<O, D> doRunInTime(DBIDs ids, KNNQuery<O, D> kNNRefer, KNNQuery<O, D> kNNReach, StepProgress stepprog) {
// Assert we got something
- if(kNNRefer == null) {
+ if (kNNRefer == null) {
throw new AbortException("No kNN queries supported by database for reference neighborhood distance function.");
}
- if(kNNReach == null) {
+ if (kNNReach == null) {
throw new AbortException("No kNN queries supported by database for reachability distance function.");
}
// Compute LRDs
- if(stepprog != null) {
- stepprog.beginStep(2, "Computing LRDs.", logger);
+ if (stepprog != null) {
+ stepprog.beginStep(2, "Computing LRDs.", LOG);
}
WritableDoubleDataStore lrds = computeLRDs(ids, kNNReach);
// compute LOF_SCORE of each db object
- if(stepprog != null) {
- stepprog.beginStep(3, "Computing LOFs.", logger);
+ if (stepprog != null) {
+ stepprog.beginStep(3, "Computing LOFs.", LOG);
}
Pair<WritableDoubleDataStore, DoubleMinMax> lofsAndMax = computeLOFs(ids, lrds, kNNRefer);
WritableDoubleDataStore lofs = lofsAndMax.getFirst();
// track the maximum value for normalization.
DoubleMinMax lofminmax = lofsAndMax.getSecond();
- if(stepprog != null) {
- stepprog.setCompleted(logger);
+ if (stepprog != null) {
+ stepprog.setCompleted(LOG);
}
// Build result representation.
@@ -295,26 +296,44 @@ public class LOF<O, D extends NumberDistance<D, ?>> extends AbstractAlgorithm<Ou
*/
protected WritableDoubleDataStore computeLRDs(DBIDs ids, KNNQuery<O, D> knnReach) {
WritableDoubleDataStore lrds = DataStoreUtil.makeDoubleStorage(ids, DataStoreFactory.HINT_HOT | DataStoreFactory.HINT_TEMP);
- FiniteProgress lrdsProgress = logger.isVerbose() ? new FiniteProgress("LRD", ids.size(), logger) : null;
- Mean mean = new Mean();
- for(DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
- mean.reset();
- KNNResult<D> neighbors = knnReach.getKNNForDBID(iter, k);
- for(DistanceResultPair<D> neighbor : neighbors) {
- if(objectIsInKNN || !neighbor.sameDBID(iter)) {
- KNNResult<D> neighborsNeighbors = knnReach.getKNNForDBID(neighbor, k);
- mean.put(Math.max(neighbor.getDistance().doubleValue(), neighborsNeighbors.getKNNDistance().doubleValue()));
+ FiniteProgress lrdsProgress = LOG.isVerbose() ? new FiniteProgress("LRD", ids.size(), LOG) : null;
+ for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
+ final KNNResult<D> neighbors = knnReach.getKNNForDBID(iter, k);
+ double sum = 0.0;
+ int count = 0;
+ if (neighbors instanceof DoubleDistanceKNNList) {
+ // Fast version for double distances
+ for (DoubleDistanceDBIDResultIter neighbor = ((DoubleDistanceKNNList) neighbors).iter(); neighbor.valid(); neighbor.advance()) {
+ if (objectIsInKNN || !DBIDUtil.equal(neighbor, iter)) {
+ KNNResult<D> neighborsNeighbors = knnReach.getKNNForDBID(neighbor, k);
+ final double nkdist;
+ if (neighborsNeighbors instanceof DoubleDistanceKNNList) {
+ nkdist = ((DoubleDistanceKNNList) neighborsNeighbors).doubleKNNDistance();
+ } else {
+ nkdist = neighborsNeighbors.getKNNDistance().doubleValue();
+ }
+ sum += Math.max(neighbor.doubleDistance(), nkdist);
+ count++;
+ }
+ }
+ } else {
+ for (DistanceDBIDResultIter<D> neighbor = neighbors.iter(); neighbor.valid(); neighbor.advance()) {
+ if (objectIsInKNN || !DBIDUtil.equal(neighbor, iter)) {
+ KNNResult<D> neighborsNeighbors = knnReach.getKNNForDBID(neighbor, k);
+ sum += Math.max(neighbor.getDistance().doubleValue(), neighborsNeighbors.getKNNDistance().doubleValue());
+ count++;
+ }
}
}
// Avoid division by 0
- final double lrd = (mean.getCount() > 0) ? 1 / mean.getMean() : 0.0;
+ final double lrd = (sum > 0) ? (count / sum) : 0;
lrds.putDouble(iter, lrd);
- if(lrdsProgress != null) {
- lrdsProgress.incrementProcessed(logger);
+ if (lrdsProgress != null) {
+ lrdsProgress.incrementProcessed(LOG);
}
}
- if(lrdsProgress != null) {
- lrdsProgress.ensureCompleted(logger);
+ if (lrdsProgress != null) {
+ lrdsProgress.ensureCompleted(LOG);
}
return lrds;
}
@@ -328,40 +347,40 @@ public class LOF<O, D extends NumberDistance<D, ?>> extends AbstractAlgorithm<Ou
* reference distance
* @return the LOFs of the objects and the maximum LOF
*/
- protected Pair<WritableDoubleDataStore, DoubleMinMax> computeLOFs(DBIDs ids, DataStore<Double> lrds, KNNQuery<O, D> knnRefer) {
+ protected Pair<WritableDoubleDataStore, DoubleMinMax> computeLOFs(DBIDs ids, DoubleDataStore lrds, KNNQuery<O, D> knnRefer) {
WritableDoubleDataStore lofs = DataStoreUtil.makeDoubleStorage(ids, DataStoreFactory.HINT_STATIC);
// track the maximum value for normalization.
DoubleMinMax lofminmax = new DoubleMinMax();
- FiniteProgress progressLOFs = logger.isVerbose() ? new FiniteProgress("LOF_SCORE for objects", ids.size(), logger) : null;
- Mean mean = new Mean();
- for(DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
- double lrdp = lrds.get(iter);
+ FiniteProgress progressLOFs = LOG.isVerbose() ? new FiniteProgress("LOF_SCORE for objects", ids.size(), LOG) : null;
+ for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
+ final double lrdp = lrds.doubleValue(iter);
final double lof;
- if(lrdp > 0) {
+ if (lrdp > 0) {
final KNNResult<D> neighbors = knnRefer.getKNNForDBID(iter, k);
- mean.reset();
- for(DistanceResultPair<D> neighbor : neighbors) {
+ double sum = 0.0;
+ int count = 0;
+ for (DBIDIter neighbor = neighbors.iter(); neighbor.valid(); neighbor.advance()) {
// skip the point itself
- if(objectIsInKNN || !neighbor.sameDBID(iter)) {
- mean.put(lrds.get(neighbor));
+ if (objectIsInKNN || !DBIDUtil.equal(neighbor, iter)) {
+ sum += lrds.doubleValue(neighbor);
+ count++;
}
}
- lof = mean.getMean() / lrdp;
- }
- else {
+ lof = sum / (count * lrdp);
+ } else {
lof = 1.0;
}
lofs.putDouble(iter, lof);
// update minimum and maximum
lofminmax.put(lof);
- if(progressLOFs != null) {
- progressLOFs.incrementProcessed(logger);
+ if (progressLOFs != null) {
+ progressLOFs.incrementProcessed(LOG);
}
}
- if(progressLOFs != null) {
- progressLOFs.ensureCompleted(logger);
+ if (progressLOFs != null) {
+ progressLOFs.ensureCompleted(LOG);
}
return new Pair<WritableDoubleDataStore, DoubleMinMax>(lofs, lofminmax);
}
@@ -369,10 +388,9 @@ public class LOF<O, D extends NumberDistance<D, ?>> extends AbstractAlgorithm<Ou
@Override
public TypeInformation[] getInputTypeRestriction() {
final TypeInformation type;
- if(reachabilityDistanceFunction.equals(neighborhoodDistanceFunction)) {
+ if (reachabilityDistanceFunction.equals(neighborhoodDistanceFunction)) {
type = reachabilityDistanceFunction.getInputTypeRestriction();
- }
- else {
+ } else {
type = new CombinedTypeInformation(neighborhoodDistanceFunction.getInputTypeRestriction(), reachabilityDistanceFunction.getInputTypeRestriction());
}
return TypeUtil.array(type);
@@ -380,7 +398,7 @@ public class LOF<O, D extends NumberDistance<D, ?>> extends AbstractAlgorithm<Ou
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -442,6 +460,8 @@ public class LOF<O, D extends NumberDistance<D, ?>> extends AbstractAlgorithm<Ou
}
/**
+ * Get the knn query for the reference set.
+ *
* @return the kNN query w.r.t. the reference neighborhood distance
*/
public KNNQuery<O, D> getKNNRefer() {
@@ -449,6 +469,8 @@ public class LOF<O, D extends NumberDistance<D, ?>> extends AbstractAlgorithm<Ou
}
/**
+ * Get the knn query for the reachability set.
+ *
* @return the kNN query w.r.t. the reachability distance
*/
public KNNQuery<O, D> getKNNReach() {
@@ -456,6 +478,8 @@ public class LOF<O, D extends NumberDistance<D, ?>> extends AbstractAlgorithm<Ou
}
/**
+ * Get the LRD data store.
+ *
* @return the LRD values of the objects
*/
public WritableDoubleDataStore getLrds() {
@@ -463,6 +487,8 @@ public class LOF<O, D extends NumberDistance<D, ?>> extends AbstractAlgorithm<Ou
}
/**
+ * Get the LOF data store.
+ *
* @return the LOF values of the objects
*/
public WritableDoubleDataStore getLofs() {
@@ -470,6 +496,8 @@ public class LOF<O, D extends NumberDistance<D, ?>> extends AbstractAlgorithm<Ou
}
/**
+ * Get the outlier result.
+ *
* @return the result of the run of the {@link LOF} algorithm
*/
public OutlierResult getResult() {
@@ -486,6 +514,8 @@ public class LOF<O, D extends NumberDistance<D, ?>> extends AbstractAlgorithm<Ou
}
/**
+ * Get the RkNN query for the reference set.
+ *
* @return the RkNN query w.r.t. the reference neighborhood distance
*/
public RKNNQuery<O, D> getRkNNRefer() {
@@ -493,6 +523,8 @@ public class LOF<O, D extends NumberDistance<D, ?>> extends AbstractAlgorithm<Ou
}
/**
+ * Get the RkNN query for the reachability set.
+ *
* @return the RkNN query w.r.t. the reachability distance
*/
public RKNNQuery<O, D> getRkNNReach() {
@@ -518,7 +550,7 @@ public class LOF<O, D extends NumberDistance<D, ?>> extends AbstractAlgorithm<Ou
*/
public static class Parameterizer<O, D extends NumberDistance<D, ?>> extends AbstractDistanceBasedAlgorithm.Parameterizer<O, D> {
/**
- * The neighborhood size to use
+ * The neighborhood size to use.
*/
protected int k = 2;
@@ -536,13 +568,14 @@ public class LOF<O, D extends NumberDistance<D, ?>> extends AbstractAlgorithm<Ou
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- final IntParameter pK = new IntParameter(K_ID, new GreaterConstraint(1));
- if(config.grab(pK)) {
+ final IntParameter pK = new IntParameter(K_ID);
+ pK.addConstraint(new GreaterConstraint(1));
+ if (config.grab(pK)) {
k = pK.getValue();
}
final ObjectParameter<DistanceFunction<O, D>> reachDistP = new ObjectParameter<DistanceFunction<O, D>>(REACHABILITY_DISTANCE_FUNCTION_ID, DistanceFunction.class, true);
- if(config.grab(reachDistP)) {
+ if (config.grab(reachDistP)) {
reachabilityDistanceFunction = reachDistP.instantiateClass(config);
}
}
@@ -554,4 +587,4 @@ public class LOF<O, D extends NumberDistance<D, ?>> extends AbstractAlgorithm<Ou
return new LOF<O, D>(k, distanceFunction, rdist);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/LoOP.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/LoOP.java
index dc0d26a4..5da06983 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/LoOP.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/LoOP.java
@@ -33,15 +33,18 @@ import de.lmu.ifi.dbs.elki.database.datastore.DataStoreFactory;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreUtil;
import de.lmu.ifi.dbs.elki.database.datastore.WritableDoubleDataStore;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.query.DatabaseQuery;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.database.query.knn.KNNQuery;
-import de.lmu.ifi.dbs.elki.database.query.knn.KNNResult;
import de.lmu.ifi.dbs.elki.database.relation.MaterializedRelation;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
import de.lmu.ifi.dbs.elki.distance.distancefunction.EuclideanDistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResultIter;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DoubleDistanceDBIDResultIter;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DoubleDistanceKNNList;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance;
import de.lmu.ifi.dbs.elki.index.preprocessed.knn.MaterializeKNNPreprocessor;
import de.lmu.ifi.dbs.elki.logging.Logging;
@@ -76,7 +79,8 @@ import de.lmu.ifi.dbs.elki.utilities.pairs.Pair;
*
* @apiviz.has KNNQuery
*
- * @param <O> the type of DatabaseObjects handled by this Algorithm
+ * @param <O> type of objects handled by this algorithm
+ * @param <D> type of distances used
*/
@Title("LoOP: Local Outlier Probabilities")
@Description("Variant of the LOF algorithm normalized using statistical values.")
@@ -85,37 +89,37 @@ public class LoOP<O, D extends NumberDistance<D, ?>> extends AbstractAlgorithm<O
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(LoOP.class);
+ private static final Logging LOG = Logging.getLogger(LoOP.class);
/**
* The distance function to determine the reachability distance between
* database objects.
*/
- public static final OptionID REACHABILITY_DISTANCE_FUNCTION_ID = OptionID.getOrCreateOptionID("loop.referencedistfunction", "Distance function to determine the density of an object.");
+ public static final OptionID REACHABILITY_DISTANCE_FUNCTION_ID = new OptionID("loop.referencedistfunction", "Distance function to determine the density of an object.");
/**
* The distance function to determine the reachability distance between
* database objects.
*/
- public static final OptionID COMPARISON_DISTANCE_FUNCTION_ID = OptionID.getOrCreateOptionID("loop.comparedistfunction", "Distance function to determine the reference set of an object.");
+ public static final OptionID COMPARISON_DISTANCE_FUNCTION_ID = new OptionID("loop.comparedistfunction", "Distance function to determine the reference set of an object.");
/**
* Parameter to specify the number of nearest neighbors of an object to be
* considered for computing its LOOP_SCORE, must be an integer greater than 1.
*/
- public static final OptionID KREACH_ID = OptionID.getOrCreateOptionID("loop.kref", "The number of nearest neighbors of an object to be used for the PRD value.");
+ public static final OptionID KREACH_ID = new OptionID("loop.kref", "The number of nearest neighbors of an object to be used for the PRD value.");
/**
* Parameter to specify the number of nearest neighbors of an object to be
* considered for computing its LOOP_SCORE, must be an integer greater than 1.
*/
- public static final OptionID KCOMP_ID = OptionID.getOrCreateOptionID("loop.kcomp", "The number of nearest neighbors of an object to be considered for computing its LOOP_SCORE.");
+ public static final OptionID KCOMP_ID = new OptionID("loop.kcomp", "The number of nearest neighbors of an object to be considered for computing its LOOP_SCORE.");
/**
* Parameter to specify the number of nearest neighbors of an object to be
* considered for computing its LOOP_SCORE, must be an integer greater than 1.
*/
- public static final OptionID LAMBDA_ID = OptionID.getOrCreateOptionID("loop.lambda", "The number of standard deviations to consider for density computation.");
+ public static final OptionID LAMBDA_ID = new OptionID("loop.lambda", "The number of standard deviations to consider for density computation.");
/**
* Holds the value of {@link #KREACH_ID}.
@@ -133,12 +137,12 @@ public class LoOP<O, D extends NumberDistance<D, ?>> extends AbstractAlgorithm<O
double lambda;
/**
- * Preprocessor Step 1
+ * Preprocessor Step 1.
*/
protected DistanceFunction<? super O, D> reachabilityDistanceFunction;
/**
- * Preprocessor Step 2
+ * Preprocessor Step 2.
*/
protected DistanceFunction<? super O, D> comparisonDistanceFunction;
@@ -150,11 +154,11 @@ public class LoOP<O, D extends NumberDistance<D, ?>> extends AbstractAlgorithm<O
/**
* Constructor with parameters.
*
- * @param kreach
- * @param kcomp
- * @param reachabilityDistanceFunction
- * @param comparisonDistanceFunction
- * @param lambda
+ * @param kreach k for reachability
+ * @param kcomp k for comparison
+ * @param reachabilityDistanceFunction distance function for reachability
+ * @param comparisonDistanceFunction distance function for comparison
+ * @param lambda Lambda parameter
*/
public LoOP(int kreach, int kcomp, DistanceFunction<? super O, D> reachabilityDistanceFunction, DistanceFunction<? super O, D> comparisonDistanceFunction, double lambda) {
super();
@@ -168,36 +172,35 @@ public class LoOP<O, D extends NumberDistance<D, ?>> extends AbstractAlgorithm<O
/**
* Get the kNN queries for the algorithm.
*
- * @param database Database
- * @param stepprog Progress logger
+ * @param database Database to analyze
+ * @param relation Relation to analyze
+ * @param stepprog Progress logger, may be {@code null}
* @return result
*/
protected Pair<KNNQuery<O, D>, KNNQuery<O, D>> getKNNQueries(Database database, Relation<O> relation, StepProgress stepprog) {
KNNQuery<O, D> knnComp;
KNNQuery<O, D> knnReach;
- if(comparisonDistanceFunction == reachabilityDistanceFunction || comparisonDistanceFunction.equals(reachabilityDistanceFunction)) {
+ if (comparisonDistanceFunction == reachabilityDistanceFunction || comparisonDistanceFunction.equals(reachabilityDistanceFunction)) {
// We need each neighborhood twice - use "HEAVY" flag.
knnComp = QueryUtil.getKNNQuery(relation, comparisonDistanceFunction, Math.max(kreach, kcomp), DatabaseQuery.HINT_HEAVY_USE, DatabaseQuery.HINT_OPTIMIZED_ONLY, DatabaseQuery.HINT_NO_CACHE);
// No optimized kNN query - use a preprocessor!
- if(knnComp == null) {
- if(stepprog != null) {
- stepprog.beginStep(1, "Materializing neighborhoods with respect to reference neighborhood distance function.", logger);
+ if (knnComp == null) {
+ if (stepprog != null) {
+ stepprog.beginStep(1, "Materializing neighborhoods with respect to reference neighborhood distance function.", LOG);
}
MaterializeKNNPreprocessor<O, D> preproc = new MaterializeKNNPreprocessor<O, D>(relation, comparisonDistanceFunction, kcomp);
database.addIndex(preproc);
DistanceQuery<O, D> cdq = database.getDistanceQuery(relation, comparisonDistanceFunction);
knnComp = preproc.getKNNQuery(cdq, kreach, DatabaseQuery.HINT_HEAVY_USE);
- }
- else {
- if(stepprog != null) {
- stepprog.beginStep(1, "Optimized neighborhoods provided by database.", logger);
+ } else {
+ if (stepprog != null) {
+ stepprog.beginStep(1, "Optimized neighborhoods provided by database.", LOG);
}
}
knnReach = knnComp;
- }
- else {
- if(stepprog != null) {
- stepprog.beginStep(1, "Not materializing distance functions, since we request each DBID once only.", logger);
+ } else {
+ if (stepprog != null) {
+ stepprog.beginStep(1, "Not materializing distance functions, since we request each DBID once only.", LOG);
}
knnComp = QueryUtil.getKNNQuery(relation, comparisonDistanceFunction, kreach);
knnReach = QueryUtil.getKNNQuery(relation, reachabilityDistanceFunction, kcomp);
@@ -215,17 +218,17 @@ public class LoOP<O, D extends NumberDistance<D, ?>> extends AbstractAlgorithm<O
public OutlierResult run(Database database, Relation<O> relation) {
final double sqrt2 = Math.sqrt(2.0);
- StepProgress stepprog = logger.isVerbose() ? new StepProgress(5) : null;
+ StepProgress stepprog = LOG.isVerbose() ? new StepProgress(5) : null;
Pair<KNNQuery<O, D>, KNNQuery<O, D>> pair = getKNNQueries(database, relation, stepprog);
KNNQuery<O, D> knnComp = pair.getFirst();
KNNQuery<O, D> knnReach = pair.getSecond();
// Assert we got something
- if(knnComp == null) {
+ if (knnComp == null) {
throw new AbortException("No kNN queries supported by database for comparison distance function.");
}
- if(knnReach == null) {
+ if (knnReach == null) {
throw new AbortException("No kNN queries supported by database for density estimation distance function.");
}
@@ -233,29 +236,43 @@ public class LoOP<O, D extends NumberDistance<D, ?>> extends AbstractAlgorithm<O
WritableDoubleDataStore pdists = DataStoreUtil.makeDoubleStorage(relation.getDBIDs(), DataStoreFactory.HINT_HOT | DataStoreFactory.HINT_TEMP);
Mean mean = new Mean();
{// computing PRDs
- if(stepprog != null) {
- stepprog.beginStep(3, "Computing pdists", logger);
+ if (stepprog != null) {
+ stepprog.beginStep(3, "Computing pdists", LOG);
}
- FiniteProgress prdsProgress = logger.isVerbose() ? new FiniteProgress("pdists", relation.size(), logger) : null;
- for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
+ FiniteProgress prdsProgress = LOG.isVerbose() ? new FiniteProgress("pdists", relation.size(), LOG) : null;
+ for (DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
final KNNResult<D> neighbors = knnReach.getKNNForDBID(iditer, kreach);
mean.reset();
// use first kref neighbors as reference set
int ks = 0;
- for(DistanceResultPair<D> neighbor : neighbors) {
- if(objectIsInKNN || !neighbor.sameDBID(iditer)) {
- double d = neighbor.getDistance().doubleValue();
- mean.put(d * d);
- ks++;
- if(ks >= kreach) {
- break;
+ // TODO: optimize for double distances
+ if (neighbors instanceof DoubleDistanceKNNList) {
+ for (DoubleDistanceDBIDResultIter neighbor = ((DoubleDistanceKNNList) neighbors).iter(); neighbor.valid(); neighbor.advance()) {
+ if (objectIsInKNN || !DBIDUtil.equal(neighbor, iditer)) {
+ final double d = neighbor.doubleDistance();
+ mean.put(d * d);
+ ks++;
+ if (ks >= kreach) {
+ break;
+ }
+ }
+ }
+ } else {
+ for (DistanceDBIDResultIter<D> neighbor = neighbors.iter(); neighbor.valid(); neighbor.advance()) {
+ if (objectIsInKNN || !DBIDUtil.equal(neighbor, iditer)) {
+ double d = neighbor.getDistance().doubleValue();
+ mean.put(d * d);
+ ks++;
+ if (ks >= kreach) {
+ break;
+ }
}
}
}
double pdist = lambda * Math.sqrt(mean.getMean());
pdists.putDouble(iditer, pdist);
- if(prdsProgress != null) {
- prdsProgress.incrementProcessed(logger);
+ if (prdsProgress != null) {
+ prdsProgress.incrementProcessed(LOG);
}
}
}
@@ -263,63 +280,63 @@ public class LoOP<O, D extends NumberDistance<D, ?>> extends AbstractAlgorithm<O
WritableDoubleDataStore plofs = DataStoreUtil.makeDoubleStorage(relation.getDBIDs(), DataStoreFactory.HINT_HOT | DataStoreFactory.HINT_TEMP);
MeanVariance mvplof = new MeanVariance();
{// compute LOOP_SCORE of each db object
- if(stepprog != null) {
- stepprog.beginStep(4, "Computing PLOF", logger);
+ if (stepprog != null) {
+ stepprog.beginStep(4, "Computing PLOF", LOG);
}
- FiniteProgress progressPLOFs = logger.isVerbose() ? new FiniteProgress("PLOFs for objects", relation.size(), logger) : null;
+ FiniteProgress progressPLOFs = LOG.isVerbose() ? new FiniteProgress("PLOFs for objects", relation.size(), LOG) : null;
MeanVariance mv = new MeanVariance();
- for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
+ for (DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
final KNNResult<D> neighbors = knnComp.getKNNForDBID(iditer, kcomp);
mv.reset();
// use first kref neighbors as comparison set.
int ks = 0;
- for(DistanceResultPair<D> neighbor1 : neighbors) {
- if(objectIsInKNN || !neighbor1.sameDBID(iditer)) {
- mv.put(pdists.doubleValue(neighbor1));
+ for (DBIDIter neighbor = neighbors.iter(); neighbor.valid(); neighbor.advance()) {
+ if (objectIsInKNN || !DBIDUtil.equal(neighbor, iditer)) {
+ mv.put(pdists.doubleValue(neighbor));
ks++;
- if(ks >= kcomp) {
+ if (ks >= kcomp) {
break;
}
}
}
double plof = Math.max(pdists.doubleValue(iditer) / mv.getMean(), 1.0);
- if(Double.isNaN(plof) || Double.isInfinite(plof)) {
+ if (Double.isNaN(plof) || Double.isInfinite(plof)) {
plof = 1.0;
}
plofs.putDouble(iditer, plof);
mvplof.put((plof - 1.0) * (plof - 1.0));
- if(progressPLOFs != null) {
- progressPLOFs.incrementProcessed(logger);
+ if (progressPLOFs != null) {
+ progressPLOFs.incrementProcessed(LOG);
}
}
}
double nplof = lambda * Math.sqrt(mvplof.getMean());
- if(logger.isDebugging()) {
- logger.verbose("nplof normalization factor is " + nplof + " " + mvplof.getMean() + " " + mvplof.getSampleStddev());
+ if (LOG.isDebugging()) {
+ LOG.verbose("nplof normalization factor is " + nplof + " " + mvplof.getMean() + " " + mvplof.getSampleStddev());
}
// Compute final LoOP values.
WritableDoubleDataStore loops = DataStoreUtil.makeDoubleStorage(relation.getDBIDs(), DataStoreFactory.HINT_STATIC);
{// compute LOOP_SCORE of each db object
- if(stepprog != null) {
- stepprog.beginStep(5, "Computing LoOP scores", logger);
+ if (stepprog != null) {
+ stepprog.beginStep(5, "Computing LoOP scores", LOG);
}
- FiniteProgress progressLOOPs = logger.isVerbose() ? new FiniteProgress("LoOP for objects", relation.size(), logger) : null;
- for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
+ FiniteProgress progressLOOPs = LOG.isVerbose() ? new FiniteProgress("LoOP for objects", relation.size(), LOG) : null;
+ for (DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
loops.putDouble(iditer, NormalDistribution.erf((plofs.doubleValue(iditer) - 1) / (nplof * sqrt2)));
- if(progressLOOPs != null) {
- progressLOOPs.incrementProcessed(logger);
+ if (progressLOOPs != null) {
+ progressLOOPs.incrementProcessed(LOG);
}
}
}
- if(stepprog != null) {
- stepprog.setCompleted(logger);
+ if (stepprog != null) {
+ stepprog.setCompleted(LOG);
}
// Build result representation.
@@ -331,10 +348,9 @@ public class LoOP<O, D extends NumberDistance<D, ?>> extends AbstractAlgorithm<O
@Override
public TypeInformation[] getInputTypeRestriction() {
final TypeInformation type;
- if(reachabilityDistanceFunction.equals(comparisonDistanceFunction)) {
+ if (reachabilityDistanceFunction.equals(comparisonDistanceFunction)) {
type = reachabilityDistanceFunction.getInputTypeRestriction();
- }
- else {
+ } else {
type = new CombinedTypeInformation(reachabilityDistanceFunction.getInputTypeRestriction(), comparisonDistanceFunction.getInputTypeRestriction());
}
return TypeUtil.array(type);
@@ -342,7 +358,7 @@ public class LoOP<O, D extends NumberDistance<D, ?>> extends AbstractAlgorithm<O
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -369,45 +385,48 @@ public class LoOP<O, D extends NumberDistance<D, ?>> extends AbstractAlgorithm<O
double lambda = 2.0;
/**
- * Preprocessor Step 1
+ * Preprocessor Step 1.
*/
protected DistanceFunction<O, D> reachabilityDistanceFunction = null;
/**
- * Preprocessor Step 2
+ * Preprocessor Step 2.
*/
protected DistanceFunction<O, D> comparisonDistanceFunction = null;
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- final IntParameter kcompP = new IntParameter(KCOMP_ID, new GreaterConstraint(1));
- if(config.grab(kcompP)) {
- kcomp = kcompP.getValue();
+ final IntParameter kcompP = new IntParameter(KCOMP_ID);
+ kcompP.addConstraint(new GreaterConstraint(1));
+ if (config.grab(kcompP)) {
+ kcomp = kcompP.intValue();
}
final ObjectParameter<DistanceFunction<O, D>> compDistP = new ObjectParameter<DistanceFunction<O, D>>(COMPARISON_DISTANCE_FUNCTION_ID, DistanceFunction.class, EuclideanDistanceFunction.class);
- if(config.grab(compDistP)) {
+ if (config.grab(compDistP)) {
comparisonDistanceFunction = compDistP.instantiateClass(config);
}
- final IntParameter kreachP = new IntParameter(KREACH_ID, new GreaterConstraint(1), true);
- if(config.grab(kreachP)) {
- kreach = kreachP.getValue();
- }
- else {
+ final IntParameter kreachP = new IntParameter(KREACH_ID);
+ kreachP.addConstraint(new GreaterConstraint(1));
+ kreachP.setOptional(true);
+ if (config.grab(kreachP)) {
+ kreach = kreachP.intValue();
+ } else {
kreach = kcomp;
}
final ObjectParameter<DistanceFunction<O, D>> reachDistP = new ObjectParameter<DistanceFunction<O, D>>(REACHABILITY_DISTANCE_FUNCTION_ID, DistanceFunction.class, true);
- if(config.grab(reachDistP)) {
+ if (config.grab(reachDistP)) {
reachabilityDistanceFunction = reachDistP.instantiateClass(config);
}
// TODO: make default 1.0?
- final DoubleParameter lambdaP = new DoubleParameter(LAMBDA_ID, new GreaterConstraint(0.0), 2.0);
- if(config.grab(lambdaP)) {
- lambda = lambdaP.getValue();
+ final DoubleParameter lambdaP = new DoubleParameter(LAMBDA_ID, 2.0);
+ lambdaP.addConstraint(new GreaterConstraint(0.0));
+ if (config.grab(lambdaP)) {
+ lambda = lambdaP.doubleValue();
}
}
@@ -417,4 +436,4 @@ public class LoOP<O, D extends NumberDistance<D, ?>> extends AbstractAlgorithm<O
return new LoOP<O, D>(kreach, kcomp, realreach, comparisonDistanceFunction, lambda);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/OPTICSOF.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/OPTICSOF.java
index b3d24463..bed27a33 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/OPTICSOF.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/OPTICSOF.java
@@ -37,14 +37,14 @@ import de.lmu.ifi.dbs.elki.database.datastore.WritableDoubleDataStore;
import de.lmu.ifi.dbs.elki.database.datastore.WritableIntegerDataStore;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.database.query.knn.KNNQuery;
-import de.lmu.ifi.dbs.elki.database.query.knn.KNNResult;
import de.lmu.ifi.dbs.elki.database.query.range.RangeQuery;
import de.lmu.ifi.dbs.elki.database.relation.MaterializedRelation;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResultIter;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.math.DoubleMinMax;
@@ -83,7 +83,7 @@ public class OPTICSOF<O, D extends NumberDistance<D, ?>> extends AbstractDistanc
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(OPTICSOF.class);
+ private static final Logging LOG = Logging.getLogger(OPTICSOF.class);
/**
* Parameter to specify the threshold MinPts.
@@ -136,9 +136,10 @@ public class OPTICSOF<O, D extends NumberDistance<D, ?>> extends AbstractDistanc
for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
List<Double> core = new ArrayList<Double>();
double lrd = 0;
- for(DistanceResultPair<D> neighPair : nMinPts.get(iditer)) {
- double coreDist = coreDistance.doubleValue(neighPair);
- double dist = distQuery.distance(iditer, neighPair).doubleValue();
+ // TODO: optimize for double distances
+ for (DistanceDBIDResultIter<D> neighbor = nMinPts.get(iditer).iter(); neighbor.valid(); neighbor.advance()) {
+ double coreDist = coreDistance.doubleValue(neighbor);
+ double dist = distQuery.distance(iditer, neighbor).doubleValue();
double rd = Math.max(coreDist, dist);
lrd = rd + lrd;
core.add(rd);
@@ -153,9 +154,9 @@ public class OPTICSOF<O, D extends NumberDistance<D, ?>> extends AbstractDistanc
WritableDoubleDataStore ofs = DataStoreUtil.makeDoubleStorage(ids, DataStoreFactory.HINT_STATIC);
for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
double of = 0;
- for(DistanceResultPair<D> pair : nMinPts.get(iditer)) {
+ for (DBIDIter neighbor = nMinPts.get(iditer).iter(); neighbor.valid(); neighbor.advance()) {
double lrd = lrds.doubleValue(iditer);
- double lrdN = lrds.doubleValue(pair);
+ double lrdN = lrds.doubleValue(neighbor);
of = of + lrdN / lrd;
}
of = of / minPtsNeighborhoodSize.intValue(iditer);
@@ -176,7 +177,7 @@ public class OPTICSOF<O, D extends NumberDistance<D, ?>> extends AbstractDistanc
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -192,7 +193,8 @@ public class OPTICSOF<O, D extends NumberDistance<D, ?>> extends AbstractDistanc
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- final IntParameter param = new IntParameter(OPTICS.MINPTS_ID, new GreaterConstraint(1));
+ final IntParameter param = new IntParameter(OPTICS.MINPTS_ID);
+ param.addConstraint(new GreaterConstraint(1));
if(config.grab(param)) {
minpts = param.getValue();
}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/OnlineLOF.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/OnlineLOF.java
index 9b974ad9..bac5db36 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/OnlineLOF.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/OnlineLOF.java
@@ -34,14 +34,14 @@ import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.ids.ModifiableDBIDs;
import de.lmu.ifi.dbs.elki.database.query.DatabaseQuery;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.database.query.knn.KNNQuery;
-import de.lmu.ifi.dbs.elki.database.query.knn.KNNResult;
import de.lmu.ifi.dbs.elki.database.query.knn.PreprocessorKNNQuery;
import de.lmu.ifi.dbs.elki.database.query.rknn.RKNNQuery;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance;
import de.lmu.ifi.dbs.elki.index.preprocessed.knn.AbstractMaterializeKNNPreprocessor;
import de.lmu.ifi.dbs.elki.index.preprocessed.knn.KNNChangeEvent;
@@ -73,7 +73,7 @@ public class OnlineLOF<O, D extends NumberDistance<D, ?>> extends LOF<O, D> {
/**
* The logger for this class.
*/
- static final Logging logger = Logging.getLogger(OnlineLOF.class);
+ private static final Logging LOG = Logging.getLogger(OnlineLOF.class);
/**
* Constructor.
@@ -93,7 +93,7 @@ public class OnlineLOF<O, D extends NumberDistance<D, ?>> extends LOF<O, D> {
*/
@Override
public OutlierResult run(Relation<O> relation) {
- StepProgress stepprog = logger.isVerbose() ? new StepProgress("OnlineLOF", 3) : null;
+ StepProgress stepprog = LOG.isVerbose() ? new StepProgress("OnlineLOF", 3) : null;
Pair<Pair<KNNQuery<O, D>, KNNQuery<O, D>>, Pair<RKNNQuery<O, D>, RKNNQuery<O, D>>> queries = getKNNAndRkNNQueries(relation, stepprog);
KNNQuery<O, D> kNNRefer = queries.getFirst().getFirst();
@@ -128,7 +128,7 @@ public class OnlineLOF<O, D extends NumberDistance<D, ?>> extends LOF<O, D> {
// No optimized kNN query or RkNN query - use a preprocessor!
if(kNNRefer == null || rkNNRefer == null) {
if(stepprog != null) {
- stepprog.beginStep(1, "Materializing neighborhood w.r.t. reference neighborhood distance function.", logger);
+ stepprog.beginStep(1, "Materializing neighborhood w.r.t. reference neighborhood distance function.", LOG);
}
MaterializeKNNAndRKNNPreprocessor<O, D> preproc = new MaterializeKNNAndRKNNPreprocessor<O, D>(relation, neighborhoodDistanceFunction, k);
DistanceQuery<O, D> ndq = relation.getDatabase().getDistanceQuery(relation, neighborhoodDistanceFunction);
@@ -139,7 +139,7 @@ public class OnlineLOF<O, D extends NumberDistance<D, ?>> extends LOF<O, D> {
}
else {
if(stepprog != null) {
- stepprog.beginStep(1, "Optimized neighborhood w.r.t. reference neighborhood distance function provided by database.", logger);
+ stepprog.beginStep(1, "Optimized neighborhood w.r.t. reference neighborhood distance function provided by database.", LOG);
}
}
@@ -147,7 +147,7 @@ public class OnlineLOF<O, D extends NumberDistance<D, ?>> extends LOF<O, D> {
RKNNQuery<O, D> rkNNReach = QueryUtil.getRKNNQuery(relation, reachabilityDistanceFunction, DatabaseQuery.HINT_HEAVY_USE, DatabaseQuery.HINT_OPTIMIZED_ONLY, DatabaseQuery.HINT_NO_CACHE);
if(kNNReach == null || rkNNReach == null) {
if(stepprog != null) {
- stepprog.beginStep(2, "Materializing neighborhood w.r.t. reachability distance function.", logger);
+ stepprog.beginStep(2, "Materializing neighborhood w.r.t. reachability distance function.", LOG);
}
ListParameterization config = new ListParameterization();
config.addParameter(AbstractMaterializeKNNPreprocessor.Factory.DISTANCE_FUNCTION_ID, reachabilityDistanceFunction);
@@ -261,14 +261,14 @@ public class OnlineLOF<O, D extends NumberDistance<D, ?>> extends LOF<O, D> {
* @param lofResult the result of the former LOF run
*/
private void kNNsInserted(DBIDs insertions, DBIDs updates1, DBIDs updates2, LOFResult<O, D> lofResult) {
- StepProgress stepprog = logger.isVerbose() ? new StepProgress(3) : null;
+ StepProgress stepprog = LOG.isVerbose() ? new StepProgress(3) : null;
// recompute lrds
if(stepprog != null) {
- stepprog.beginStep(1, "Recompute LRDs.", logger);
+ stepprog.beginStep(1, "Recompute LRDs.", LOG);
}
ArrayDBIDs lrd_ids = DBIDUtil.ensureArray(DBIDUtil.union(insertions, updates2));
- List<List<DistanceResultPair<D>>> reachDistRKNNs = lofResult.getRkNNReach().getRKNNForBulkDBIDs(lrd_ids, k);
+ List<? extends DistanceDBIDResult<D>> reachDistRKNNs = lofResult.getRkNNReach().getRKNNForBulkDBIDs(lrd_ids, k);
ArrayDBIDs affected_lrd_id_candidates = mergeIDs(reachDistRKNNs, lrd_ids);
ArrayModifiableDBIDs affected_lrd_ids = DBIDUtil.newArray(affected_lrd_id_candidates.size());
WritableDoubleDataStore new_lrds = computeLRDs(affected_lrd_id_candidates, lofResult.getKNNReach());
@@ -283,20 +283,20 @@ public class OnlineLOF<O, D extends NumberDistance<D, ?>> extends LOF<O, D> {
// recompute lofs
if(stepprog != null) {
- stepprog.beginStep(2, "Recompute LOFS.", logger);
+ stepprog.beginStep(2, "Recompute LOFS.", LOG);
}
- List<List<DistanceResultPair<D>>> primDistRKNNs = lofResult.getRkNNRefer().getRKNNForBulkDBIDs(affected_lrd_ids, k);
+ List<? extends DistanceDBIDResult<D>> primDistRKNNs = lofResult.getRkNNRefer().getRKNNForBulkDBIDs(affected_lrd_ids, k);
ArrayDBIDs affected_lof_ids = mergeIDs(primDistRKNNs, affected_lrd_ids, insertions, updates1);
recomputeLOFs(affected_lof_ids, lofResult);
// fire result changed
if(stepprog != null) {
- stepprog.beginStep(3, "Inform listeners.", logger);
+ stepprog.beginStep(3, "Inform listeners.", LOG);
}
lofResult.getResult().getHierarchy().resultChanged(lofResult.getResult());
if(stepprog != null) {
- stepprog.setCompleted(logger);
+ stepprog.setCompleted(LOG);
}
}
@@ -311,11 +311,11 @@ public class OnlineLOF<O, D extends NumberDistance<D, ?>> extends LOF<O, D> {
* @param lofResult the result of the former LOF run
*/
private void kNNsRemoved(DBIDs deletions, DBIDs updates1, DBIDs updates2, LOFResult<O, D> lofResult) {
- StepProgress stepprog = logger.isVerbose() ? new StepProgress(4) : null;
+ StepProgress stepprog = LOG.isVerbose() ? new StepProgress(4) : null;
// delete lrds and lofs
if(stepprog != null) {
- stepprog.beginStep(1, "Delete old LRDs and LOFs.", logger);
+ stepprog.beginStep(1, "Delete old LRDs and LOFs.", LOG);
}
for (DBIDIter iter = deletions.iter(); iter.valid(); iter.advance()) {
lofResult.getLrds().delete(iter);
@@ -324,10 +324,10 @@ public class OnlineLOF<O, D extends NumberDistance<D, ?>> extends LOF<O, D> {
// recompute lrds
if(stepprog != null) {
- stepprog.beginStep(2, "Recompute LRDs.", logger);
+ stepprog.beginStep(2, "Recompute LRDs.", LOG);
}
ArrayDBIDs lrd_ids = DBIDUtil.ensureArray(updates2);
- List<List<DistanceResultPair<D>>> reachDistRKNNs = lofResult.getRkNNReach().getRKNNForBulkDBIDs(lrd_ids, k);
+ List<? extends DistanceDBIDResult<D>> reachDistRKNNs = lofResult.getRkNNReach().getRKNNForBulkDBIDs(lrd_ids, k);
ArrayDBIDs affected_lrd_id_candidates = mergeIDs(reachDistRKNNs, lrd_ids);
ArrayModifiableDBIDs affected_lrd_ids = DBIDUtil.newArray(affected_lrd_id_candidates.size());
WritableDoubleDataStore new_lrds = computeLRDs(affected_lrd_id_candidates, lofResult.getKNNReach());
@@ -342,20 +342,20 @@ public class OnlineLOF<O, D extends NumberDistance<D, ?>> extends LOF<O, D> {
// recompute lofs
if(stepprog != null) {
- stepprog.beginStep(3, "Recompute LOFS.", logger);
+ stepprog.beginStep(3, "Recompute LOFS.", LOG);
}
- List<List<DistanceResultPair<D>>> primDistRKNNs = lofResult.getRkNNRefer().getRKNNForBulkDBIDs(affected_lrd_ids, k);
+ List<? extends DistanceDBIDResult<D>> primDistRKNNs = lofResult.getRkNNRefer().getRKNNForBulkDBIDs(affected_lrd_ids, k);
ArrayDBIDs affected_lof_ids = mergeIDs(primDistRKNNs, affected_lrd_ids, updates1);
recomputeLOFs(affected_lof_ids, lofResult);
// fire result changed
if(stepprog != null) {
- stepprog.beginStep(4, "Inform listeners.", logger);
+ stepprog.beginStep(4, "Inform listeners.", LOG);
}
lofResult.getResult().getHierarchy().resultChanged(lofResult.getResult());
if(stepprog != null) {
- stepprog.setCompleted(logger);
+ stepprog.setCompleted(LOG);
}
}
@@ -367,15 +367,13 @@ public class OnlineLOF<O, D extends NumberDistance<D, ?>> extends LOF<O, D> {
* @return a set containing the ids of the query result and the specified
* ids
*/
- private ArrayModifiableDBIDs mergeIDs(List<List<DistanceResultPair<D>>> queryResults, DBIDs... ids) {
+ private ArrayModifiableDBIDs mergeIDs(List<? extends DistanceDBIDResult<D>> queryResults, DBIDs... ids) {
ModifiableDBIDs result = DBIDUtil.newHashSet();
for(DBIDs dbids : ids) {
result.addDBIDs(dbids);
}
- for(List<DistanceResultPair<D>> queryResult : queryResults) {
- for(DistanceResultPair<D> qr : queryResult) {
- result.add(qr);
- }
+ for(DistanceDBIDResult<D> queryResult : queryResults) {
+ result.addDBIDs(queryResult);
}
return DBIDUtil.newArray(result);
}
@@ -410,7 +408,7 @@ public class OnlineLOF<O, D extends NumberDistance<D, ?>> extends LOF<O, D> {
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -440,7 +438,8 @@ public class OnlineLOF<O, D extends NumberDistance<D, ?>> extends LOF<O, D> {
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- final IntParameter pK = new IntParameter(K_ID, new GreaterConstraint(1));
+ final IntParameter pK = new IntParameter(K_ID);
+ pK.addConstraint(new GreaterConstraint(1));
if(config.grab(pK)) {
k = pK.getValue();
}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/OutlierAlgorithm.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/OutlierAlgorithm.java
index d8322d8b..00c4a8ec 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/OutlierAlgorithm.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/OutlierAlgorithm.java
@@ -31,6 +31,8 @@ import de.lmu.ifi.dbs.elki.result.outlier.OutlierResult;
* Generic super interface for outlier detection algorithms.
*
* @author Erich Schubert
+ *
+ * @apiviz.landmark
*
* @apiviz.has OutlierResult
*/
@@ -39,4 +41,4 @@ public interface OutlierAlgorithm extends Algorithm {
// Use the magic in AbstractAlgorithm and just implement a run method for your input data
@Override
OutlierResult run(Database database);
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/ReferenceBasedOutlierDetection.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/ReferenceBasedOutlierDetection.java
index dd1d37a3..93eca7db 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/ReferenceBasedOutlierDetection.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/ReferenceBasedOutlierDetection.java
@@ -23,11 +23,8 @@ package de.lmu.ifi.dbs.elki.algorithm.outlier;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.Iterator;
-import java.util.List;
import de.lmu.ifi.dbs.elki.algorithm.AbstractAlgorithm;
import de.lmu.ifi.dbs.elki.algorithm.AbstractDistanceBasedAlgorithm;
@@ -39,12 +36,13 @@ import de.lmu.ifi.dbs.elki.database.datastore.DataStoreUtil;
import de.lmu.ifi.dbs.elki.database.datastore.WritableDoubleDataStore;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
-import de.lmu.ifi.dbs.elki.database.query.GenericDistanceResultPair;
+import de.lmu.ifi.dbs.elki.database.ids.DistanceDBIDPair;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.database.relation.MaterializedRelation;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.GenericDistanceDBIDList;
import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.math.Mean;
@@ -88,23 +86,23 @@ import de.lmu.ifi.dbs.elki.utilities.referencepoints.ReferencePointsHeuristic;
@Title("An Efficient Reference-based Approach to Outlier Detection in Large Datasets")
@Description("Computes kNN distances approximately, using reference points with various reference point strategies.")
@Reference(authors = "Y. Pei, O.R. Zaiane, Y. Gao", title = "An Efficient Reference-based Approach to Outlier Detection in Large Datasets", booktitle = "Proc. 6th IEEE Int. Conf. on Data Mining (ICDM '06), Hong Kong, China, 2006", url = "http://dx.doi.org/10.1109/ICDM.2006.17")
-public class ReferenceBasedOutlierDetection<V extends NumberVector<?, ?>, D extends NumberDistance<D, ?>> extends AbstractAlgorithm<OutlierResult> implements OutlierAlgorithm {
+public class ReferenceBasedOutlierDetection<V extends NumberVector<?>, D extends NumberDistance<D, ?>> extends AbstractAlgorithm<OutlierResult> implements OutlierAlgorithm {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(ReferenceBasedOutlierDetection.class);
+ private static final Logging LOG = Logging.getLogger(ReferenceBasedOutlierDetection.class);
/**
* Parameter for the reference points heuristic.
*/
- public static final OptionID REFP_ID = OptionID.getOrCreateOptionID("refod.refp", "The heuristic for finding reference points.");
+ public static final OptionID REFP_ID = new OptionID("refod.refp", "The heuristic for finding reference points.");
/**
* Parameter to specify the number of nearest neighbors of an object, to be
* considered for computing its REFOD_SCORE, must be an integer greater than
* 1.
*/
- public static final OptionID K_ID = OptionID.getOrCreateOptionID("refod.k", "The number of nearest neighbors");
+ public static final OptionID K_ID = new OptionID("refod.k", "The number of nearest neighbors");
/**
* Holds the value of {@link #K_ID}.
@@ -160,7 +158,7 @@ public class ReferenceBasedOutlierDetection<V extends NumberVector<?, ?>, D exte
}
V firstRef = iter.next();
// compute distance vector for the first reference point
- List<DistanceResultPair<D>> firstReferenceDists = computeDistanceVector(firstRef, relation, distFunc);
+ DistanceDBIDResult<D> firstReferenceDists = computeDistanceVector(firstRef, relation, distFunc);
for(int l = 0; l < firstReferenceDists.size(); l++) {
double density = computeDensity(firstReferenceDists, l);
// Initial value
@@ -169,7 +167,7 @@ public class ReferenceBasedOutlierDetection<V extends NumberVector<?, ?>, D exte
// compute density values for all remaining reference points
while(iter.hasNext()) {
V refPoint = iter.next();
- List<DistanceResultPair<D>> referenceDists = computeDistanceVector(refPoint, relation, distFunc);
+ DistanceDBIDResult<D> referenceDists = computeDistanceVector(refPoint, relation, distFunc);
// compute density value for each object
for(int l = 0; l < referenceDists.size(); l++) {
double density = computeDensity(referenceDists, l);
@@ -215,14 +213,13 @@ public class ReferenceBasedOutlierDetection<V extends NumberVector<?, ?>, D exte
* @return array containing the distance to one reference point for each
* database object and the object id
*/
- protected List<DistanceResultPair<D>> computeDistanceVector(V refPoint, Relation<V> database, DistanceQuery<V, D> distFunc) {
+ protected DistanceDBIDResult<D> computeDistanceVector(V refPoint, Relation<V> database, DistanceQuery<V, D> distFunc) {
// TODO: optimize for double distances?
- List<DistanceResultPair<D>> referenceDists = new ArrayList<DistanceResultPair<D>>(database.size());
+ GenericDistanceDBIDList<D> referenceDists = new GenericDistanceDBIDList<D>(database.size());
for(DBIDIter iditer = database.iterDBIDs(); iditer.valid(); iditer.advance()) {
- final D distance = distFunc.distance(iditer, refPoint);
- referenceDists.add(new GenericDistanceResultPair<D>(distance, iditer.getDBID()));
+ referenceDists.add(distFunc.distance(iditer, refPoint), iditer);
}
- Collections.sort(referenceDists);
+ referenceDists.sort();
return referenceDists;
}
@@ -238,8 +235,8 @@ public class ReferenceBasedOutlierDetection<V extends NumberVector<?, ?>, D exte
* @param index index of the current object
* @return density for one object and reference point
*/
- protected double computeDensity(List<DistanceResultPair<D>> referenceDists, int index) {
- final DistanceResultPair<D> x = referenceDists.get(index);
+ protected double computeDensity(DistanceDBIDResult<D> referenceDists, int index) {
+ final DistanceDBIDPair<D> x = referenceDists.get(index);
final double xDist = x.getDistance().doubleValue();
int lef = index - 1;
@@ -295,7 +292,7 @@ public class ReferenceBasedOutlierDetection<V extends NumberVector<?, ?>, D exte
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -305,7 +302,7 @@ public class ReferenceBasedOutlierDetection<V extends NumberVector<?, ?>, D exte
*
* @apiviz.exclude
*/
- public static class Parameterizer<V extends NumberVector<?, ?>, D extends NumberDistance<D, ?>> extends AbstractDistanceBasedAlgorithm.Parameterizer<V, D> {
+ public static class Parameterizer<V extends NumberVector<?>, D extends NumberDistance<D, ?>> extends AbstractDistanceBasedAlgorithm.Parameterizer<V, D> {
/**
* Holds the value of {@link #K_ID}.
*/
@@ -319,7 +316,8 @@ public class ReferenceBasedOutlierDetection<V extends NumberVector<?, ?>, D exte
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- final IntParameter pK = new IntParameter(K_ID, new GreaterConstraint(1));
+ final IntParameter pK = new IntParameter(K_ID);
+ pK.addConstraint(new GreaterConstraint(1));
if(config.grab(pK)) {
k = pK.getValue();
}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/SimpleCOP.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/SimpleCOP.java
new file mode 100644
index 00000000..e8077819
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/SimpleCOP.java
@@ -0,0 +1,236 @@
+package de.lmu.ifi.dbs.elki.algorithm.outlier;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import de.lmu.ifi.dbs.elki.algorithm.AbstractDistanceBasedAlgorithm;
+import de.lmu.ifi.dbs.elki.algorithm.DependencyDerivator;
+import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.data.model.CorrelationAnalysisSolution;
+import de.lmu.ifi.dbs.elki.data.type.SimpleTypeInformation;
+import de.lmu.ifi.dbs.elki.data.type.TypeInformation;
+import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
+import de.lmu.ifi.dbs.elki.database.Database;
+import de.lmu.ifi.dbs.elki.database.QueryUtil;
+import de.lmu.ifi.dbs.elki.database.datastore.DataStoreFactory;
+import de.lmu.ifi.dbs.elki.database.datastore.DataStoreUtil;
+import de.lmu.ifi.dbs.elki.database.datastore.WritableDataStore;
+import de.lmu.ifi.dbs.elki.database.datastore.WritableDoubleDataStore;
+import de.lmu.ifi.dbs.elki.database.datastore.WritableIntegerDataStore;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
+import de.lmu.ifi.dbs.elki.database.ids.ModifiableDBIDs;
+import de.lmu.ifi.dbs.elki.database.query.knn.KNNQuery;
+import de.lmu.ifi.dbs.elki.database.relation.MaterializedRelation;
+import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
+import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance;
+import de.lmu.ifi.dbs.elki.logging.Logging;
+import de.lmu.ifi.dbs.elki.logging.progress.FiniteProgress;
+import de.lmu.ifi.dbs.elki.math.linearalgebra.Matrix;
+import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector;
+import de.lmu.ifi.dbs.elki.math.linearalgebra.pca.PCAFilteredRunner;
+import de.lmu.ifi.dbs.elki.math.statistics.distribution.NormalDistribution;
+import de.lmu.ifi.dbs.elki.result.outlier.OutlierResult;
+import de.lmu.ifi.dbs.elki.result.outlier.OutlierScoreMeta;
+import de.lmu.ifi.dbs.elki.result.outlier.ProbabilisticOutlierScore;
+import de.lmu.ifi.dbs.elki.utilities.FormatUtil;
+import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
+import de.lmu.ifi.dbs.elki.utilities.documentation.Title;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ObjectParameter;
+
+/**
+ * Algorithm to compute local correlation outlier probability.
+ *
+ * This is the simpler, original version of COP, as published in
+ * <p>
+ * Arthur Zimek<br />
+ * Correlation Clustering.<br />
+ * PhD thesis, Chapter 18
+ * </p>
+ * which has then been refined to the method published as {@link COP}
+ *
+ * @author Erich Schubert
+ * @param <V> the type of NumberVector handled by this Algorithm
+ */
+@Title("Simple COP: Correlation Outlier Probability")
+@Reference(authors = "Arthur Zimek", title = "Correlation Clustering. PhD thesis, Chapter 18", booktitle = "")
+public class SimpleCOP<V extends NumberVector<?>, D extends NumberDistance<D, ?>> extends AbstractDistanceBasedAlgorithm<V, D, OutlierResult> implements OutlierAlgorithm {
+ /**
+ * The logger for this class.
+ */
+ private static final Logging LOG = Logging.getLogger(SimpleCOP.class);
+
+ /**
+ * Number of neighbors to be considered.
+ */
+ int k;
+
+ /**
+ * Holds the object performing the dependency derivation
+ */
+ private DependencyDerivator<V, D> dependencyDerivator;
+
+ /**
+ * Constructor.
+ *
+ * @param distanceFunction Distance function
+ * @param k k Parameter
+ * @param pca PCA runner-
+ */
+ public SimpleCOP(DistanceFunction<? super V, D> distanceFunction, int k, PCAFilteredRunner<V> pca) {
+ super(distanceFunction);
+ this.k = k;
+ this.dependencyDerivator = new DependencyDerivator<V, D>(null, FormatUtil.NF8, pca, 0, false);
+ }
+
+ public OutlierResult run(Database database, Relation<V> data) throws IllegalStateException {
+ KNNQuery<V, D> knnQuery = QueryUtil.getKNNQuery(data, getDistanceFunction(), k + 1);
+
+ DBIDs ids = data.getDBIDs();
+
+ WritableDoubleDataStore cop_score = DataStoreUtil.makeDoubleStorage(ids, DataStoreFactory.HINT_HOT | DataStoreFactory.HINT_STATIC);
+ WritableDataStore<Vector> cop_err_v = DataStoreUtil.makeStorage(ids, DataStoreFactory.HINT_HOT | DataStoreFactory.HINT_STATIC, Vector.class);
+ WritableDataStore<Matrix> cop_datav = DataStoreUtil.makeStorage(ids, DataStoreFactory.HINT_HOT | DataStoreFactory.HINT_STATIC, Matrix.class);
+ WritableIntegerDataStore cop_dim = DataStoreUtil.makeIntegerStorage(ids, DataStoreFactory.HINT_HOT | DataStoreFactory.HINT_STATIC, -1);
+ WritableDataStore<CorrelationAnalysisSolution<?>> cop_sol = DataStoreUtil.makeStorage(ids, DataStoreFactory.HINT_HOT | DataStoreFactory.HINT_STATIC, CorrelationAnalysisSolution.class);
+ {// compute neighbors of each db object
+ FiniteProgress progressLocalPCA = LOG.isVerbose() ? new FiniteProgress("Correlation Outlier Probabilities", data.size(), LOG) : null;
+ double sqrt2 = Math.sqrt(2.0);
+ for (DBIDIter id = data.iterDBIDs(); id.valid(); id.advance()) {
+ KNNResult<D> neighbors = knnQuery.getKNNForDBID(id, k + 1);
+ ModifiableDBIDs nids = DBIDUtil.newArray(neighbors);
+ nids.remove(id);
+
+ // TODO: do we want to use the query point as centroid?
+ CorrelationAnalysisSolution<V> depsol = dependencyDerivator.generateModel(data, nids);
+
+ double stddev = depsol.getStandardDeviation();
+ double distance = depsol.distance(data.get(id));
+ double prob = NormalDistribution.erf(distance / (stddev * sqrt2));
+
+ cop_score.putDouble(id, prob);
+
+ Vector errv = depsol.errorVector(data.get(id)).timesEquals(-1);
+ cop_err_v.put(id, errv);
+
+ Matrix datav = depsol.dataProjections(data.get(id));
+ cop_datav.put(id, datav);
+
+ cop_dim.putInt(id, depsol.getCorrelationDimensionality());
+
+ cop_sol.put(id, depsol);
+
+ if (progressLocalPCA != null) {
+ progressLocalPCA.incrementProcessed(LOG);
+ }
+ }
+ if (progressLocalPCA != null) {
+ progressLocalPCA.ensureCompleted(LOG);
+ }
+ }
+ // combine results.
+ Relation<Double> scoreResult = new MaterializedRelation<Double>("Original Correlation Outlier Probabilities", "origcop-outlier", TypeUtil.DOUBLE, cop_score, ids);
+ OutlierScoreMeta scoreMeta = new ProbabilisticOutlierScore();
+ OutlierResult result = new OutlierResult(scoreMeta, scoreResult);
+ // extra results
+ result.addChildResult(new MaterializedRelation<Integer>("Local Dimensionality", COP.COP_DIM, TypeUtil.INTEGER, cop_dim, ids));
+ result.addChildResult(new MaterializedRelation<Vector>("Error vectors", COP.COP_ERRORVEC, TypeUtil.VECTOR, cop_err_v, ids));
+ result.addChildResult(new MaterializedRelation<Matrix>("Data vectors", "cop-datavec", TypeUtil.MATRIX, cop_datav, ids));
+ result.addChildResult(new MaterializedRelation<CorrelationAnalysisSolution<?>>("Correlation analysis", "cop-sol", new SimpleTypeInformation<CorrelationAnalysisSolution<?>>(CorrelationAnalysisSolution.class), cop_sol, ids));
+ return result;
+ }
+
+ @Override
+ public TypeInformation[] getInputTypeRestriction() {
+ return TypeUtil.array(TypeUtil.NUMBER_VECTOR_FIELD);
+ }
+
+ @Override
+ protected Logging getLogger() {
+ return LOG;
+ }
+
+ /**
+ * Parameterization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public static class Parameterizer<V extends NumberVector<?>, D extends NumberDistance<D, ?>> extends AbstractDistanceBasedAlgorithm.Parameterizer<V, D> {
+ /**
+ * Parameter to specify the number of nearest neighbors of an object to be
+ * considered for computing its COP_SCORE, must be an integer greater than
+ * 0.
+ * <p/>
+ * Key: {@code -cop.k}
+ * </p>
+ */
+ public static final OptionID K_ID = new OptionID("cop.k", "The number of nearest neighbors of an object to be considered for computing its COP_SCORE.");
+
+ /**
+ * Parameter for the PCA runner class.
+ *
+ * <p>
+ * Key: {@code -cop.pcarunner}
+ * </p>
+ */
+ public static final OptionID PCARUNNER_ID = new OptionID("cop.pcarunner", "The class to compute (filtered) PCA.");
+
+ /**
+ * Number of neighbors to be considered.
+ */
+ int k;
+
+ /**
+ * Holds the object performing the dependency derivation
+ */
+ protected PCAFilteredRunner<V> pca;
+
+ @Override
+ protected void makeOptions(Parameterization config) {
+ super.makeOptions(config);
+ IntParameter kP = new IntParameter(K_ID);
+ kP.addConstraint(new GreaterConstraint(0));
+ if (config.grab(kP)) {
+ k = kP.intValue();
+ }
+ ObjectParameter<PCAFilteredRunner<V>> pcaP = new ObjectParameter<PCAFilteredRunner<V>>(PCARUNNER_ID, PCAFilteredRunner.class, PCAFilteredRunner.class);
+ if (config.grab(pcaP)) {
+ pca = pcaP.instantiateClass(config);
+ }
+ }
+
+ @Override
+ protected SimpleCOP<V, D> makeInstance() {
+ return new SimpleCOP<V, D>(distanceFunction, k, pca);
+ }
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/SimpleKernelDensityLOF.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/SimpleKernelDensityLOF.java
new file mode 100644
index 00000000..1c104c08
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/SimpleKernelDensityLOF.java
@@ -0,0 +1,284 @@
+package de.lmu.ifi.dbs.elki.algorithm.outlier;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import de.lmu.ifi.dbs.elki.algorithm.AbstractDistanceBasedAlgorithm;
+import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.data.type.CombinedTypeInformation;
+import de.lmu.ifi.dbs.elki.data.type.TypeInformation;
+import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
+import de.lmu.ifi.dbs.elki.database.QueryUtil;
+import de.lmu.ifi.dbs.elki.database.datastore.DataStoreFactory;
+import de.lmu.ifi.dbs.elki.database.datastore.DataStoreUtil;
+import de.lmu.ifi.dbs.elki.database.datastore.WritableDoubleDataStore;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
+import de.lmu.ifi.dbs.elki.database.query.DatabaseQuery;
+import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
+import de.lmu.ifi.dbs.elki.database.query.knn.KNNQuery;
+import de.lmu.ifi.dbs.elki.database.query.knn.PreprocessorKNNQuery;
+import de.lmu.ifi.dbs.elki.database.relation.MaterializedRelation;
+import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
+import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResultIter;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DoubleDistanceDBIDResultIter;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DoubleDistanceKNNList;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
+import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance;
+import de.lmu.ifi.dbs.elki.index.preprocessed.knn.MaterializeKNNPreprocessor;
+import de.lmu.ifi.dbs.elki.logging.Logging;
+import de.lmu.ifi.dbs.elki.logging.progress.FiniteProgress;
+import de.lmu.ifi.dbs.elki.logging.progress.StepProgress;
+import de.lmu.ifi.dbs.elki.math.DoubleMinMax;
+import de.lmu.ifi.dbs.elki.math.statistics.EpanechnikovKernelDensityFunction;
+import de.lmu.ifi.dbs.elki.math.statistics.KernelDensityFunction;
+import de.lmu.ifi.dbs.elki.result.outlier.OutlierResult;
+import de.lmu.ifi.dbs.elki.result.outlier.OutlierScoreMeta;
+import de.lmu.ifi.dbs.elki.result.outlier.QuotientOutlierScoreMeta;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ObjectParameter;
+
+/**
+ * A simple variant of the LOF algorithm, which uses a simple kernel density
+ * estimation instead of the local reachability density.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.has KNNQuery
+ * @apiviz.has KernelDensityFunction
+ *
+ * @param <O> the type of objects handled by this Algorithm
+ * @param <D> Distance type
+ */
+public class SimpleKernelDensityLOF<O extends NumberVector<?>, D extends NumberDistance<D, ?>> extends AbstractDistanceBasedAlgorithm<O, D, OutlierResult> implements OutlierAlgorithm {
+ /**
+ * The logger for this class.
+ */
+ private static final Logging LOG = Logging.getLogger(SimpleKernelDensityLOF.class);
+
+ /**
+ * Parameter k.
+ */
+ protected int k;
+
+ /**
+ * Kernel density function
+ */
+ private KernelDensityFunction kernel;
+
+ /**
+ * Constructor.
+ *
+ * @param k the value of k
+ * @param kernel Kernel function
+ */
+ public SimpleKernelDensityLOF(int k, DistanceFunction<? super O, D> distance, KernelDensityFunction kernel) {
+ super(distance);
+ this.k = k + 1;
+ this.kernel = kernel;
+ }
+
+ /**
+ * Run the naive kernel density LOF algorithm.
+ *
+ * @param relation Data to process
+ * @return LOF outlier result
+ */
+ public OutlierResult run(Relation<O> relation) {
+ StepProgress stepprog = LOG.isVerbose() ? new StepProgress("KernelDensityLOF", 3) : null;
+
+ final int dim = RelationUtil.dimensionality(relation);
+
+ DBIDs ids = relation.getDBIDs();
+
+ // "HEAVY" flag for KNN Query since it is used more than once
+ KNNQuery<O, D> knnq = QueryUtil.getKNNQuery(relation, getDistanceFunction(), k, DatabaseQuery.HINT_HEAVY_USE, DatabaseQuery.HINT_OPTIMIZED_ONLY, DatabaseQuery.HINT_NO_CACHE);
+ // No optimized kNN query - use a preprocessor!
+ if (!(knnq instanceof PreprocessorKNNQuery)) {
+ if (stepprog != null) {
+ stepprog.beginStep(1, "Materializing neighborhoods w.r.t. distance function.", LOG);
+ }
+ MaterializeKNNPreprocessor<O, D> preproc = new MaterializeKNNPreprocessor<O, D>(relation, getDistanceFunction(), k);
+ relation.getDatabase().addIndex(preproc);
+ DistanceQuery<O, D> rdq = relation.getDatabase().getDistanceQuery(relation, getDistanceFunction());
+ knnq = preproc.getKNNQuery(rdq, k);
+ }
+
+ // Compute LRDs
+ if (stepprog != null) {
+ stepprog.beginStep(2, "Computing densities.", LOG);
+ }
+ WritableDoubleDataStore dens = DataStoreUtil.makeDoubleStorage(ids, DataStoreFactory.HINT_HOT | DataStoreFactory.HINT_TEMP);
+ FiniteProgress densProgress = LOG.isVerbose() ? new FiniteProgress("Densities", ids.size(), LOG) : null;
+ for (DBIDIter it = ids.iter(); it.valid(); it.advance()) {
+ final KNNResult<D> neighbors = knnq.getKNNForDBID(it, k);
+ int count = 0;
+ double sum = 0.0;
+ if (neighbors instanceof DoubleDistanceKNNList) {
+ // Fast version for double distances
+ for (DoubleDistanceDBIDResultIter neighbor = ((DoubleDistanceKNNList) neighbors).iter(); neighbor.valid(); neighbor.advance()) {
+ if (DBIDUtil.equal(neighbor, it)) {
+ continue;
+ }
+ double max = ((DoubleDistanceKNNList)knnq.getKNNForDBID(neighbor, k)).doubleKNNDistance();
+ final double v = neighbor.doubleDistance() / max;
+ sum += kernel.density(v) / Math.pow(max, dim);
+ count++;
+ }
+ } else {
+ for (DistanceDBIDResultIter<D> neighbor = neighbors.iter(); neighbor.valid(); neighbor.advance()) {
+ if (DBIDUtil.equal(neighbor, it)) {
+ continue;
+ }
+ double max = knnq.getKNNForDBID(neighbor, k).getKNNDistance().doubleValue();
+ final double v = neighbor.getDistance().doubleValue() / max;
+ sum += kernel.density(v) / Math.pow(max, dim);
+ count++;
+ }
+ }
+ final double density = sum / count;
+ dens.putDouble(it, density);
+ if (densProgress != null) {
+ densProgress.incrementProcessed(LOG);
+ }
+ }
+ if (densProgress != null) {
+ densProgress.ensureCompleted(LOG);
+ }
+
+ // compute LOF_SCORE of each db object
+ if (stepprog != null) {
+ stepprog.beginStep(3, "Computing KLOFs.", LOG);
+ }
+ WritableDoubleDataStore lofs = DataStoreUtil.makeDoubleStorage(ids, DataStoreFactory.HINT_STATIC);
+ // track the maximum value for normalization.
+ DoubleMinMax lofminmax = new DoubleMinMax();
+
+ FiniteProgress progressLOFs = LOG.isVerbose() ? new FiniteProgress("KLOF_SCORE for objects", ids.size(), LOG) : null;
+ for (DBIDIter it = ids.iter(); it.valid(); it.advance()) {
+ final double lrdp = dens.doubleValue(it);
+ final double lof;
+ if (lrdp > 0) {
+ final KNNResult<D> neighbors = knnq.getKNNForDBID(it, k);
+ double sum = 0.0;
+ int count = 0;
+ for (DBIDIter neighbor = neighbors.iter(); neighbor.valid(); neighbor.advance()) {
+ // skip the point itself
+ if (DBIDUtil.equal(neighbor, it)) {
+ continue;
+ }
+ sum += dens.doubleValue(neighbor);
+ count++;
+ }
+ lof = sum / (count * lrdp);
+ } else {
+ lof = 1.0;
+ }
+ lofs.putDouble(it, lof);
+ // update minimum and maximum
+ lofminmax.put(lof);
+
+ if (progressLOFs != null) {
+ progressLOFs.incrementProcessed(LOG);
+ }
+ }
+ if (progressLOFs != null) {
+ progressLOFs.ensureCompleted(LOG);
+ }
+
+ if (stepprog != null) {
+ stepprog.setCompleted(LOG);
+ }
+
+ // Build result representation.
+ Relation<Double> scoreResult = new MaterializedRelation<Double>("Kernel Density Local Outlier Factor", "kernel-density-slof-outlier", TypeUtil.DOUBLE, lofs, ids);
+ OutlierScoreMeta scoreMeta = new QuotientOutlierScoreMeta(lofminmax.getMin(), lofminmax.getMax(), 0.0, Double.POSITIVE_INFINITY, 1.0);
+ OutlierResult result = new OutlierResult(scoreMeta, scoreResult);
+
+ return result;
+ }
+
+ @Override
+ public TypeInformation[] getInputTypeRestriction() {
+ return TypeUtil.array(new CombinedTypeInformation(getDistanceFunction().getInputTypeRestriction(), TypeUtil.NUMBER_VECTOR_FIELD));
+ }
+
+ @Override
+ protected Logging getLogger() {
+ return LOG;
+ }
+
+ /**
+ * Parameterization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ *
+ * @param <O> vector type
+ * @param <D> distance type
+ */
+ public static class Parameterizer<O extends NumberVector<?>, D extends NumberDistance<D, ?>> extends AbstractDistanceBasedAlgorithm.Parameterizer<O, D> {
+ /**
+ * Option ID for kernel density LOF kernel.
+ */
+ public static final OptionID KERNEL_ID = new OptionID("kernellof.kernel", "Kernel to use for kernel density LOF.");
+
+ /**
+ * The neighborhood size to use.
+ */
+ protected int k = 2;
+
+ /**
+ * Kernel density function parameter
+ */
+ KernelDensityFunction kernel;
+
+ @Override
+ protected void makeOptions(Parameterization config) {
+ super.makeOptions(config);
+
+ final IntParameter pK = new IntParameter(LOF.K_ID);
+ pK.addConstraint(new GreaterConstraint(1));
+ if (config.grab(pK)) {
+ k = pK.getValue();
+ }
+
+ ObjectParameter<KernelDensityFunction> kernelP = new ObjectParameter<KernelDensityFunction>(KERNEL_ID, KernelDensityFunction.class, EpanechnikovKernelDensityFunction.class);
+ if (config.grab(kernelP)) {
+ kernel = kernelP.instantiateClass(config);
+ }
+ }
+
+ @Override
+ protected SimpleKernelDensityLOF<O, D> makeInstance() {
+ return new SimpleKernelDensityLOF<O, D>(k, distanceFunction, kernel);
+ }
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/SimpleLOF.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/SimpleLOF.java
new file mode 100644
index 00000000..48505ed5
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/SimpleLOF.java
@@ -0,0 +1,249 @@
+package de.lmu.ifi.dbs.elki.algorithm.outlier;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import de.lmu.ifi.dbs.elki.algorithm.AbstractDistanceBasedAlgorithm;
+import de.lmu.ifi.dbs.elki.data.type.TypeInformation;
+import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
+import de.lmu.ifi.dbs.elki.database.QueryUtil;
+import de.lmu.ifi.dbs.elki.database.datastore.DataStoreFactory;
+import de.lmu.ifi.dbs.elki.database.datastore.DataStoreUtil;
+import de.lmu.ifi.dbs.elki.database.datastore.WritableDoubleDataStore;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
+import de.lmu.ifi.dbs.elki.database.query.DatabaseQuery;
+import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
+import de.lmu.ifi.dbs.elki.database.query.knn.KNNQuery;
+import de.lmu.ifi.dbs.elki.database.query.knn.PreprocessorKNNQuery;
+import de.lmu.ifi.dbs.elki.database.relation.MaterializedRelation;
+import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResultIter;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DoubleDistanceDBIDResultIter;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DoubleDistanceKNNList;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
+import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance;
+import de.lmu.ifi.dbs.elki.index.preprocessed.knn.MaterializeKNNPreprocessor;
+import de.lmu.ifi.dbs.elki.logging.Logging;
+import de.lmu.ifi.dbs.elki.logging.progress.FiniteProgress;
+import de.lmu.ifi.dbs.elki.logging.progress.StepProgress;
+import de.lmu.ifi.dbs.elki.math.DoubleMinMax;
+import de.lmu.ifi.dbs.elki.result.outlier.OutlierResult;
+import de.lmu.ifi.dbs.elki.result.outlier.OutlierScoreMeta;
+import de.lmu.ifi.dbs.elki.result.outlier.QuotientOutlierScoreMeta;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
+
+/**
+ * A simplified version of the original LOF algorithm, which does not use the
+ * reachability distance, yielding less stable results on inliers.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.has KNNQuery
+ *
+ * @param <O> the type of DatabaseObjects handled by this Algorithm
+ * @param <D> Distance type
+ */
+public class SimpleLOF<O, D extends NumberDistance<D, ?>> extends AbstractDistanceBasedAlgorithm<O, D, OutlierResult> implements OutlierAlgorithm {
+ /**
+ * The logger for this class.
+ */
+ private static final Logging LOG = Logging.getLogger(SimpleLOF.class);
+
+ /**
+ * Parameter k.
+ */
+ protected int k;
+
+ /**
+ * Constructor.
+ *
+ * @param k the value of k
+ */
+ public SimpleLOF(int k, DistanceFunction<? super O, D> distance) {
+ super(distance);
+ this.k = k + 1;
+ }
+
+ /**
+ * Run the Simple LOF algorithm.
+ *
+ * @param relation Data to process
+ * @return LOF outlier result
+ */
+ public OutlierResult run(Relation<O> relation) {
+ StepProgress stepprog = LOG.isVerbose() ? new StepProgress("SimpleLOF", 3) : null;
+
+ DBIDs ids = relation.getDBIDs();
+
+ // "HEAVY" flag for KNN Query since it is used more than once
+ KNNQuery<O, D> knnq = QueryUtil.getKNNQuery(relation, getDistanceFunction(), k, DatabaseQuery.HINT_HEAVY_USE, DatabaseQuery.HINT_OPTIMIZED_ONLY, DatabaseQuery.HINT_NO_CACHE);
+ // No optimized kNN query - use a preprocessor!
+ if (!(knnq instanceof PreprocessorKNNQuery)) {
+ if (stepprog != null) {
+ stepprog.beginStep(1, "Materializing neighborhoods w.r.t. distance function.", LOG);
+ }
+ MaterializeKNNPreprocessor<O, D> preproc = new MaterializeKNNPreprocessor<O, D>(relation, getDistanceFunction(), k);
+ relation.getDatabase().addIndex(preproc);
+ DistanceQuery<O, D> rdq = relation.getDatabase().getDistanceQuery(relation, getDistanceFunction());
+ knnq = preproc.getKNNQuery(rdq, k);
+ }
+
+ // Compute LRDs
+ if (stepprog != null) {
+ stepprog.beginStep(2, "Computing densities.", LOG);
+ }
+ WritableDoubleDataStore dens = DataStoreUtil.makeDoubleStorage(ids, DataStoreFactory.HINT_HOT | DataStoreFactory.HINT_TEMP);
+ FiniteProgress densProgress = LOG.isVerbose() ? new FiniteProgress("Densities", ids.size(), LOG) : null;
+ for (DBIDIter it = ids.iter(); it.valid(); it.advance()) {
+ final KNNResult<D> neighbors = knnq.getKNNForDBID(it, k);
+ double sum = 0.0;
+ int count = 0;
+ if (neighbors instanceof DoubleDistanceKNNList) {
+ // Fast version for double distances
+ for (DoubleDistanceDBIDResultIter neighbor = ((DoubleDistanceKNNList) neighbors).iter(); neighbor.valid(); neighbor.advance()) {
+ if (DBIDUtil.equal(neighbor, it)) {
+ continue;
+ }
+ sum += neighbor.doubleDistance();
+ count++;
+ }
+ } else {
+ for (DistanceDBIDResultIter<D> neighbor = neighbors.iter(); neighbor.valid(); neighbor.advance()) {
+ if (DBIDUtil.equal(neighbor, it)) {
+ continue;
+ }
+ sum += neighbor.getDistance().doubleValue();
+ count++;
+ }
+ }
+ // Avoid division by 0
+ final double lrd = (sum > 0) ? (count / sum) : 0;
+ dens.putDouble(it, lrd);
+ if (densProgress != null) {
+ densProgress.incrementProcessed(LOG);
+ }
+ }
+ if (densProgress != null) {
+ densProgress.ensureCompleted(LOG);
+ }
+
+ // compute LOF_SCORE of each db object
+ if (stepprog != null) {
+ stepprog.beginStep(3, "Computing SLOFs.", LOG);
+ }
+ WritableDoubleDataStore lofs = DataStoreUtil.makeDoubleStorage(ids, DataStoreFactory.HINT_STATIC);
+ // track the maximum value for normalization.
+ DoubleMinMax lofminmax = new DoubleMinMax();
+
+ FiniteProgress progressLOFs = LOG.isVerbose() ? new FiniteProgress("Simple LOF scores.", ids.size(), LOG) : null;
+ for (DBIDIter it = ids.iter(); it.valid(); it.advance()) {
+ final double lrdp = dens.doubleValue(it);
+ final double lof;
+ if (lrdp > 0) {
+ final KNNResult<D> neighbors = knnq.getKNNForDBID(it, k);
+ double sum = 0.0;
+ int count = 0;
+ for (DBIDIter neighbor = neighbors.iter(); neighbor.valid(); neighbor.advance()) {
+ // skip the point itself
+ if (DBIDUtil.equal(neighbor, it)) {
+ continue;
+ }
+ sum += dens.doubleValue(neighbor);
+ count++;
+ }
+ lof = sum / (count * lrdp);
+ } else {
+ lof = 1.0;
+ }
+ lofs.putDouble(it, lof);
+ // update minimum and maximum
+ lofminmax.put(lof);
+
+ if (progressLOFs != null) {
+ progressLOFs.incrementProcessed(LOG);
+ }
+ }
+ if (progressLOFs != null) {
+ progressLOFs.ensureCompleted(LOG);
+ }
+
+ if (stepprog != null) {
+ stepprog.setCompleted(LOG);
+ }
+
+ // Build result representation.
+ Relation<Double> scoreResult = new MaterializedRelation<Double>("Simple Local Outlier Factor", "simple-lof-outlier", TypeUtil.DOUBLE, lofs, ids);
+ OutlierScoreMeta scoreMeta = new QuotientOutlierScoreMeta(lofminmax.getMin(), lofminmax.getMax(), 0.0, Double.POSITIVE_INFINITY, 1.0);
+ OutlierResult result = new OutlierResult(scoreMeta, scoreResult);
+
+ return result;
+ }
+
+ @Override
+ public TypeInformation[] getInputTypeRestriction() {
+ return TypeUtil.array(getDistanceFunction().getInputTypeRestriction());
+ }
+
+ @Override
+ protected Logging getLogger() {
+ return LOG;
+ }
+
+ /**
+ * Parameterization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ *
+ * @param <O> vector type
+ * @param <D> distance type
+ */
+ public static class Parameterizer<O, D extends NumberDistance<D, ?>> extends AbstractDistanceBasedAlgorithm.Parameterizer<O, D> {
+ /**
+ * The neighborhood size to use.
+ */
+ protected int k = 2;
+
+ @Override
+ protected void makeOptions(Parameterization config) {
+ super.makeOptions(config);
+
+ final IntParameter pK = new IntParameter(LOF.K_ID);
+ pK.addConstraint(new GreaterConstraint(1));
+ if (config.grab(pK)) {
+ k = pK.getValue();
+ }
+ }
+
+ @Override
+ protected SimpleLOF<O, D> makeInstance() {
+ return new SimpleLOF<O, D>(k, distanceFunction);
+ }
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/meta/ExternalDoubleOutlierScore.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/meta/ExternalDoubleOutlierScore.java
index 1542b8e3..f230fd3b 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/meta/ExternalDoubleOutlierScore.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/meta/ExternalDoubleOutlierScore.java
@@ -77,7 +77,7 @@ public class ExternalDoubleOutlierScore extends AbstractAlgorithm<OutlierResult>
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(ExternalDoubleOutlierScore.class);
+ private static final Logging LOG = Logging.getLogger(ExternalDoubleOutlierScore.class);
/**
* The comment character.
@@ -183,7 +183,7 @@ public class ExternalDoubleOutlierScore extends AbstractAlgorithm<OutlierResult>
minmax.put(score);
}
else if(id == null && Double.isNaN(score)) {
- logger.warning("Line did not match either ID nor score nor comment: " + line);
+ LOG.warning("Line did not match either ID nor score nor comment: " + line);
}
else {
throw new AbortException("Line matched only ID or only SCORE patterns: " + line);
@@ -224,7 +224,7 @@ public class ExternalDoubleOutlierScore extends AbstractAlgorithm<OutlierResult>
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
@Override
@@ -246,7 +246,7 @@ public class ExternalDoubleOutlierScore extends AbstractAlgorithm<OutlierResult>
* Key: {@code -externaloutlier.file}
* </p>
*/
- public static final OptionID FILE_ID = OptionID.getOrCreateOptionID("externaloutlier.file", "The file name containing the (external) outlier scores.");
+ public static final OptionID FILE_ID = new OptionID("externaloutlier.file", "The file name containing the (external) outlier scores.");
/**
* Parameter that specifies the object ID pattern
@@ -255,7 +255,7 @@ public class ExternalDoubleOutlierScore extends AbstractAlgorithm<OutlierResult>
* Default: ^ID=
* </p>
*/
- public static final OptionID ID_ID = OptionID.getOrCreateOptionID("externaloutlier.idpattern", "The pattern to match object ID prefix");
+ public static final OptionID ID_ID = new OptionID("externaloutlier.idpattern", "The pattern to match object ID prefix");
/**
* Parameter that specifies the object score pattern
@@ -263,7 +263,7 @@ public class ExternalDoubleOutlierScore extends AbstractAlgorithm<OutlierResult>
* Key: {@code -externaloutlier.scorepattern}<br />
* </p>
*/
- public static final OptionID SCORE_ID = OptionID.getOrCreateOptionID("externaloutlier.scorepattern", "The pattern to match object score prefix");
+ public static final OptionID SCORE_ID = new OptionID("externaloutlier.scorepattern", "The pattern to match object score prefix");
/**
* Parameter to specify a scaling function to use.
@@ -271,12 +271,12 @@ public class ExternalDoubleOutlierScore extends AbstractAlgorithm<OutlierResult>
* Key: {@code -externaloutlier.scaling}
* </p>
*/
- public static final OptionID SCALING_ID = OptionID.getOrCreateOptionID("externaloutlier.scaling", "Class to use as scaling function.");
+ public static final OptionID SCALING_ID = new OptionID("externaloutlier.scaling", "Class to use as scaling function.");
/**
* Flag parameter for inverted scores.
*/
- public static final OptionID INVERTED_ID = OptionID.getOrCreateOptionID("externaloutlier.inverted", "Flag to signal an inverted outlier score.");
+ public static final OptionID INVERTED_ID = new OptionID("externaloutlier.inverted", "Flag to signal an inverted outlier score.");
/**
* The file to be reparsed
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/meta/FeatureBagging.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/meta/FeatureBagging.java
index 407b7400..b53a0942 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/meta/FeatureBagging.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/meta/FeatureBagging.java
@@ -39,6 +39,7 @@ import de.lmu.ifi.dbs.elki.database.datastore.WritableDoubleDataStore;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.relation.MaterializedRelation;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.distance.distancefunction.subspace.SubspaceEuclideanDistanceFunction;
import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
import de.lmu.ifi.dbs.elki.logging.Logging;
@@ -47,7 +48,7 @@ import de.lmu.ifi.dbs.elki.math.DoubleMinMax;
import de.lmu.ifi.dbs.elki.result.outlier.BasicOutlierScoreMeta;
import de.lmu.ifi.dbs.elki.result.outlier.OutlierResult;
import de.lmu.ifi.dbs.elki.result.outlier.OutlierScoreMeta;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
+import de.lmu.ifi.dbs.elki.utilities.RandomFactory;
import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
import de.lmu.ifi.dbs.elki.utilities.documentation.Title;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
@@ -57,7 +58,7 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterEqualCons
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.Flag;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.LongParameter;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.RandomParameter;
import de.lmu.ifi.dbs.elki.utilities.pairs.Pair;
/**
@@ -85,22 +86,22 @@ public class FeatureBagging extends AbstractAlgorithm<OutlierResult> implements
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(FeatureBagging.class);
+ private static final Logging LOG = Logging.getLogger(FeatureBagging.class);
/**
- * Number of instances to use
+ * Number of instances to use.
*/
protected int num = 1;
/**
- * Cumulative sum or breadth first combinations
+ * Cumulative sum or breadth first combinations.
*/
protected boolean breadth = false;
/**
- * Random number generator for subspace choice
+ * Random number generator for subspace choice.
*/
- private Random RANDOM;
+ private RandomFactory rnd;
/**
* The parameters k for LOF.
@@ -113,18 +114,14 @@ public class FeatureBagging extends AbstractAlgorithm<OutlierResult> implements
* @param k k Parameter for LOF
* @param num Number of subspaces to use
* @param breadth Flag for breadth-first merging
+ * @param rnd Random generator
*/
- public FeatureBagging(int k, int num, boolean breadth, Long seed) {
+ public FeatureBagging(int k, int num, boolean breadth, RandomFactory rnd) {
super();
this.k = k;
this.num = num;
this.breadth = breadth;
- if(seed != null) {
- this.RANDOM = new Random(seed);
- }
- else {
- this.RANDOM = new Random();
- }
+ this.rnd = rnd;
}
/**
@@ -133,80 +130,79 @@ public class FeatureBagging extends AbstractAlgorithm<OutlierResult> implements
* @param relation Relation to use
* @return Outlier detection result
*/
- public OutlierResult run(Relation<NumberVector<?, ?>> relation) {
- final int dbdim = DatabaseUtil.dimensionality(relation);
- final int mindim = dbdim / 2;
+ public OutlierResult run(Relation<NumberVector<?>> relation) {
+ final int dbdim = RelationUtil.dimensionality(relation);
+ final int mindim = dbdim >> 1;
final int maxdim = dbdim - 1;
+ final Random rand = rnd.getRandom();
ArrayList<OutlierResult> results = new ArrayList<OutlierResult>(num);
{
- FiniteProgress prog = logger.isVerbose() ? new FiniteProgress("LOF iterations", num, logger) : null;
- for(int i = 0; i < num; i++) {
- BitSet dimset = randomSubspace(dbdim, mindim, maxdim);
+ FiniteProgress prog = LOG.isVerbose() ? new FiniteProgress("LOF iterations", num, LOG) : null;
+ for (int i = 0; i < num; i++) {
+ BitSet dimset = randomSubspace(dbdim, mindim, maxdim, rand);
SubspaceEuclideanDistanceFunction df = new SubspaceEuclideanDistanceFunction(dimset);
- LOF<NumberVector<?, ?>, DoubleDistance> lof = new LOF<NumberVector<?, ?>, DoubleDistance>(k, df);
+ LOF<NumberVector<?>, DoubleDistance> lof = new LOF<NumberVector<?>, DoubleDistance>(k, df);
// run LOF and collect the result
OutlierResult result = lof.run(relation);
results.add(result);
- if(prog != null) {
- prog.incrementProcessed(logger);
+ if (prog != null) {
+ prog.incrementProcessed(LOG);
}
}
- if(prog != null) {
- prog.ensureCompleted(logger);
+ if (prog != null) {
+ prog.ensureCompleted(LOG);
}
}
WritableDoubleDataStore scores = DataStoreUtil.makeDoubleStorage(relation.getDBIDs(), DataStoreFactory.HINT_STATIC);
DoubleMinMax minmax = new DoubleMinMax();
- if(breadth) {
- FiniteProgress cprog = logger.isVerbose() ? new FiniteProgress("Combining results", relation.size(), logger) : null;
+ if (breadth) {
+ FiniteProgress cprog = LOG.isVerbose() ? new FiniteProgress("Combining results", relation.size(), LOG) : null;
Pair<DBIDIter, Relation<Double>>[] IDVectorOntoScoreVector = Pair.newPairArray(results.size());
// Mapping score-sorted DBID-Iterators onto their corresponding scores.
// We need to initialize them now be able to iterate them "in parallel".
{
int i = 0;
- for(OutlierResult r : results) {
+ for (OutlierResult r : results) {
IDVectorOntoScoreVector[i] = new Pair<DBIDIter, Relation<Double>>(r.getOrdering().iter(relation.getDBIDs()).iter(), r.getScores());
i++;
}
}
// Iterating over the *lines* of the AS_t(i)-matrix.
- for(int i = 0; i < relation.size(); i++) {
+ for (int i = 0; i < relation.size(); i++) {
// Iterating over the elements of a line (breadth-first).
- for(Pair<DBIDIter, Relation<Double>> pair : IDVectorOntoScoreVector) {
+ for (Pair<DBIDIter, Relation<Double>> pair : IDVectorOntoScoreVector) {
DBIDIter iter = pair.first;
// Always true if every algorithm returns a complete result (one score
// for every DBID).
- if(iter.valid()) {
+ if (iter.valid()) {
double score = pair.second.get(iter);
- if(Double.isNaN(scores.doubleValue(iter))) {
+ if (Double.isNaN(scores.doubleValue(iter))) {
scores.putDouble(iter, score);
minmax.put(score);
}
iter.advance();
- }
- else {
- logger.warning("Incomplete result: Iterator does not contain |DB| DBIDs");
+ } else {
+ LOG.warning("Incomplete result: Iterator does not contain |DB| DBIDs");
}
}
// Progress does not take the initial mapping into account.
- if(cprog != null) {
- cprog.incrementProcessed(logger);
+ if (cprog != null) {
+ cprog.incrementProcessed(LOG);
}
}
- if(cprog != null) {
- cprog.ensureCompleted(logger);
+ if (cprog != null) {
+ cprog.ensureCompleted(LOG);
}
- }
- else {
- FiniteProgress cprog = logger.isVerbose() ? new FiniteProgress("Combining results", relation.size(), logger) : null;
- for(DBIDIter iter = relation.iterDBIDs(); iter.valid(); iter.advance()) {
+ } else {
+ FiniteProgress cprog = LOG.isVerbose() ? new FiniteProgress("Combining results", relation.size(), LOG) : null;
+ for (DBIDIter iter = relation.iterDBIDs(); iter.valid(); iter.advance()) {
double sum = 0.0;
- for(OutlierResult r : results) {
+ for (OutlierResult r : results) {
final Double s = r.getScores().get(iter);
if (s != null && !Double.isNaN(s)) {
sum += s;
@@ -214,12 +210,12 @@ public class FeatureBagging extends AbstractAlgorithm<OutlierResult> implements
}
scores.putDouble(iter, sum);
minmax.put(sum);
- if(cprog != null) {
- cprog.incrementProcessed(logger);
+ if (cprog != null) {
+ cprog.incrementProcessed(LOG);
}
}
- if(cprog != null) {
- cprog.ensureCompleted(logger);
+ if (cprog != null) {
+ cprog.ensureCompleted(LOG);
}
}
OutlierScoreMeta meta = new BasicOutlierScoreMeta(minmax.getMin(), minmax.getMax());
@@ -228,36 +224,34 @@ public class FeatureBagging extends AbstractAlgorithm<OutlierResult> implements
}
/**
- * Choose a random subspace
+ * Choose a random subspace.
*
* @param alldim Number of total dimensions
* @param mindim Minimum number to choose
* @param maxdim Maximum number to choose
* @return Subspace as bits.
*/
- private BitSet randomSubspace(final int alldim, final int mindim, final int maxdim) {
+ private BitSet randomSubspace(final int alldim, final int mindim, final int maxdim, final Random rand) {
BitSet dimset = new BitSet();
- {
- // Fill with all dimensions
- int[] dims = new int[alldim];
- for(int d = 0; d < alldim; d++) {
- dims[d] = d;
- }
- // Target dimensionality:
- int subdim = mindim + RANDOM.nextInt(maxdim - mindim);
- // Shrink the subspace to the destination size
- for(int d = 0; d < alldim - subdim; d++) {
- int s = RANDOM.nextInt(alldim - d);
- dimset.set(dims[s]);
- dims[s] = dims[alldim - d - 1];
- }
+ // Fill with all dimensions
+ int[] dims = new int[alldim];
+ for (int d = 0; d < alldim; d++) {
+ dims[d] = d;
+ }
+ // Target dimensionality:
+ int subdim = mindim + rand.nextInt(maxdim - mindim);
+ // Shrink the subspace to the destination size
+ for (int d = 0; d < alldim - subdim; d++) {
+ int s = rand.nextInt(alldim - d);
+ dimset.set(dims[s]);
+ dims[s] = dims[alldim - d - 1];
}
return dimset;
}
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
@Override
@@ -279,69 +273,71 @@ public class FeatureBagging extends AbstractAlgorithm<OutlierResult> implements
* Key: {@code -fbagging.num}
* </p>
*/
- public static final OptionID NUM_ID = OptionID.getOrCreateOptionID("fbagging.num", "The number of instances to use in the ensemble.");
+ public static final OptionID NUM_ID = new OptionID("fbagging.num", "The number of instances to use in the ensemble.");
/**
- * The flag for using the breadth first approach
+ * The flag for using the breadth first approach.
* <p>
* Key: {@code -fbagging.breadth}
* </p>
*/
- public static final OptionID BREADTH_ID = OptionID.getOrCreateOptionID("fbagging.breadth", "Use the breadth first combinations instead of the cumulative sum approach");
+ public static final OptionID BREADTH_ID = new OptionID("fbagging.breadth", "Use the breadth first combinations instead of the cumulative sum approach");
/**
- * The parameter to specify the random seed
+ * The parameter to specify the random seed.
* <p>
* Key: {@code -fbagging.seed}
* </p>
*/
- public static final OptionID SEED_ID = OptionID.getOrCreateOptionID("fbagging.seed", "Specify a particular random seed.");
+ public static final OptionID SEED_ID = new OptionID("fbagging.seed", "Specify a particular random seed.");
/**
- * The neighborhood size to use
+ * The neighborhood size to use.
*/
protected int k = 2;
/**
- * Number of instances to use
+ * Number of instances to use.
*/
protected int num = 1;
/**
- * Cumulative sum or breadth first combinations
+ * Cumulative sum or breadth first combinations.
*/
protected boolean breadth = false;
/**
- * Random generator seed
+ * Random generator.
*/
- protected Long seed = null;
+ protected RandomFactory rnd;
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- final IntParameter pK = new IntParameter(LOF.K_ID, new GreaterConstraint(1));
- if(config.grab(pK)) {
+ final IntParameter pK = new IntParameter(LOF.K_ID);
+ pK.addConstraint(new GreaterConstraint(1));
+ if (config.grab(pK)) {
k = pK.getValue();
}
- IntParameter NUM_PARAM = new IntParameter(NUM_ID, new GreaterEqualConstraint(1));
- if(config.grab(NUM_PARAM)) {
- num = NUM_PARAM.getValue();
+ IntParameter numP = new IntParameter(NUM_ID);
+ numP.addConstraint(new GreaterEqualConstraint(1));
+ if (config.grab(numP)) {
+ num = numP.getValue();
}
- Flag BREADTH_FLAG = new Flag(BREADTH_ID);
- if(config.grab(BREADTH_FLAG)) {
- breadth = BREADTH_FLAG.getValue();
+ Flag breadthF = new Flag(BREADTH_ID);
+ if (config.grab(breadthF)) {
+ breadth = breadthF.getValue();
}
- LongParameter seedP = new LongParameter(SEED_ID, true);
- if(config.grab(seedP)) {
- seed = seedP.getValue();
+ RandomParameter rndP = new RandomParameter(SEED_ID);
+ if (config.grab(rndP)) {
+ rnd = rndP.getValue();
}
}
@Override
protected FeatureBagging makeInstance() {
// Default is to re-use the same distance
- return new FeatureBagging(k, num, breadth, seed);
+ return new FeatureBagging(k, num, breadth, rnd);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/meta/HiCS.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/meta/HiCS.java
index 73d4156a..15b94322 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/meta/HiCS.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/meta/HiCS.java
@@ -48,12 +48,14 @@ import de.lmu.ifi.dbs.elki.database.datastore.DataStoreUtil;
import de.lmu.ifi.dbs.elki.database.datastore.WritableDoubleDataStore;
import de.lmu.ifi.dbs.elki.database.ids.ArrayDBIDs;
import de.lmu.ifi.dbs.elki.database.ids.ArrayModifiableDBIDs;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDArrayIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.relation.MaterializedRelation;
import de.lmu.ifi.dbs.elki.database.relation.ProjectedView;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.logging.progress.FiniteProgress;
import de.lmu.ifi.dbs.elki.logging.progress.IndefiniteProgress;
@@ -63,7 +65,7 @@ import de.lmu.ifi.dbs.elki.math.statistics.tests.KolmogorovSmirnovTest;
import de.lmu.ifi.dbs.elki.result.outlier.BasicOutlierScoreMeta;
import de.lmu.ifi.dbs.elki.result.outlier.OutlierResult;
import de.lmu.ifi.dbs.elki.result.outlier.OutlierScoreMeta;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
+import de.lmu.ifi.dbs.elki.utilities.RandomFactory;
import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.TopBoundedHeap;
import de.lmu.ifi.dbs.elki.utilities.documentation.Description;
import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
@@ -74,8 +76,8 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterConstrain
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.DoubleParameter;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.LongParameter;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ObjectParameter;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.RandomParameter;
/**
* Algorithm to compute High Contrast Subspaces for Density-Based Outlier
@@ -99,12 +101,12 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ObjectParameter;
*/
@Title("HiCS: High Contrast Subspaces for Density-Based Outlier Ranking")
@Description("Algorithm to compute High Contrast Subspaces in a database as a pre-processing step for for density-based outlier ranking methods.")
-@Reference(authors = "Fabian Keller, Emmanuel Müller, Klemens Böhm", title = "HiCS: High Contrast Subspaces for Density-Based Outlier Ranking", booktitle = "Proc. IEEE 28th International Conference on Data Engineering (ICDE 2012)")
-public class HiCS<V extends NumberVector<V, ?>> extends AbstractAlgorithm<OutlierResult> implements OutlierAlgorithm {
+@Reference(authors = "Fabian Keller, Emmanuel Müller, Klemens Böhm", title = "HiCS: High Contrast Subspaces for Density-Based Outlier Ranking", booktitle = "Proc. IEEE 28th International Conference on Data Engineering (ICDE 2012)", url = "http://dx.doi.org/10.1109/ICDE.2012.88")
+public class HiCS<V extends NumberVector<?>> extends AbstractAlgorithm<OutlierResult> implements OutlierAlgorithm {
/**
- * The Logger for this class
+ * The Logger for this class.
*/
- private static final Logging logger = Logging.getLogger(HiCS.class);
+ private static final Logging LOG = Logging.getLogger(HiCS.class);
/**
* Maximum number of retries.
@@ -112,57 +114,57 @@ public class HiCS<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Outlie
private static final int MAX_RETRIES = 100;
/**
- * Monte-Carlo iterations
+ * Monte-Carlo iterations.
*/
private int m;
/**
- * Alpha threshold
+ * Alpha threshold.
*/
private double alpha;
/**
- * Outlier detection algorithm
+ * Outlier detection algorithm.
*/
private OutlierAlgorithm outlierAlgorithm;
/**
- * Statistical test to use
+ * Statistical test to use.
*/
private GoodnessOfFitTest statTest;
/**
- * Candidates limit
+ * Candidates limit.
*/
private int cutoff;
-
+
/**
- * Random generator
+ * Random generator.
*/
- private Random random;
+ private RandomFactory rnd;
/**
- * Constructor
+ * Constructor.
*
* @param m value of m
* @param alpha value of alpha
* @param outlierAlgorithm Inner outlier detection algorithm
* @param statTest Test to use
* @param cutoff Candidate limit
- * @param seed Random seed
+ * @param rnd Random generator
*/
- public HiCS(int m, double alpha, OutlierAlgorithm outlierAlgorithm, GoodnessOfFitTest statTest, int cutoff, Long seed) {
+ public HiCS(int m, double alpha, OutlierAlgorithm outlierAlgorithm, GoodnessOfFitTest statTest, int cutoff, RandomFactory rnd) {
super();
this.m = m;
this.alpha = alpha;
this.outlierAlgorithm = outlierAlgorithm;
this.statTest = statTest;
this.cutoff = cutoff;
- this.random = (seed != null) ? new Random(seed) : new Random();
+ this.rnd = rnd;
}
/**
- * Perform HiCS on a given database
+ * Perform HiCS on a given database.
*
* @param relation the database
* @return The aggregated resulting scores that were assigned by the given
@@ -170,23 +172,23 @@ public class HiCS<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Outlie
*/
public OutlierResult run(Relation<V> relation) {
final DBIDs ids = relation.getDBIDs();
- final V factory = DatabaseUtil.assumeVectorField(relation).getFactory();
+ final NumberVector.Factory<V, ?> factory = RelationUtil.getNumberVectorFactory(relation);
ArrayList<ArrayDBIDs> subspaceIndex = buildOneDimIndexes(relation);
- Set<HiCSSubspace> subspaces = calculateSubspaces(relation, subspaceIndex);
+ Set<HiCSSubspace> subspaces = calculateSubspaces(relation, subspaceIndex, rnd.getRandom());
- if(logger.isVerbose()) {
- logger.verbose("Number of high-contrast subspaces: " + subspaces.size());
+ if (LOG.isVerbose()) {
+ LOG.verbose("Number of high-contrast subspaces: " + subspaces.size());
}
List<Relation<Double>> results = new ArrayList<Relation<Double>>();
- FiniteProgress prog = logger.isVerbose() ? new FiniteProgress("Calculating Outlier scores for high Contrast subspaces", subspaces.size(), logger) : null;
+ FiniteProgress prog = LOG.isVerbose() ? new FiniteProgress("Calculating Outlier scores for high Contrast subspaces", subspaces.size(), LOG) : null;
// run outlier detection and collect the result
// TODO extend so that any outlierAlgorithm can be used (use materialized
// relation instead of SubspaceEuclideanDistanceFunction?)
- for(HiCSSubspace dimset : subspaces) {
- if(logger.isVerbose()) {
- logger.verbose("Performing outlier detection in subspace " + dimset);
+ for (HiCSSubspace dimset : subspaces) {
+ if (LOG.isVerbose()) {
+ LOG.verbose("Performing outlier detection in subspace " + dimset);
}
ProxyDatabase pdb = new ProxyDatabase(ids);
@@ -196,22 +198,22 @@ public class HiCS<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Outlie
// run LOF and collect the result
OutlierResult result = outlierAlgorithm.run(pdb);
results.add(result.getScores());
- if(prog != null) {
- prog.incrementProcessed(logger);
+ if (prog != null) {
+ prog.incrementProcessed(LOG);
}
}
- if(prog != null) {
- prog.ensureCompleted(logger);
+ if (prog != null) {
+ prog.ensureCompleted(LOG);
}
WritableDoubleDataStore scores = DataStoreUtil.makeDoubleStorage(relation.getDBIDs(), DataStoreFactory.HINT_STATIC);
DoubleMinMax minmax = new DoubleMinMax();
- for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
+ for (DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
double sum = 0.0;
- for(Relation<Double> r : results) {
+ for (Relation<Double> r : results) {
final Double s = r.get(iditer);
- if(s != null && !Double.isNaN(s)) {
+ if (s != null && !Double.isNaN(s)) {
sum += s;
}
}
@@ -232,12 +234,12 @@ public class HiCS<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Outlie
* @param relation Relation to index
* @return List of sorted objects
*/
- private ArrayList<ArrayDBIDs> buildOneDimIndexes(Relation<? extends NumberVector<?, ?>> relation) {
- final int dim = DatabaseUtil.dimensionality(relation);
+ private ArrayList<ArrayDBIDs> buildOneDimIndexes(Relation<? extends NumberVector<?>> relation) {
+ final int dim = RelationUtil.dimensionality(relation);
ArrayList<ArrayDBIDs> subspaceIndex = new ArrayList<ArrayDBIDs>(dim + 1);
SortDBIDsBySingleDimension comp = new VectorUtil.SortDBIDsBySingleDimension(relation);
- for(int i = 1; i <= dim; i++) {
+ for (int i = 0; i < dim; i++) {
ArrayModifiableDBIDs amDBIDs = DBIDUtil.newArray(relation.getDBIDs());
comp.setDimension(i);
amDBIDs.sort(comp);
@@ -248,140 +250,143 @@ public class HiCS<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Outlie
}
/**
- * Identifies high contrast subspaces in a given full-dimensional database
+ * Identifies high contrast subspaces in a given full-dimensional database.
*
* @param relation the relation the HiCS should be evaluated for
* @param subspaceIndex Subspace indexes
* @return a set of high contrast subspaces
*/
- private Set<HiCSSubspace> calculateSubspaces(Relation<? extends NumberVector<?, ?>> relation, ArrayList<ArrayDBIDs> subspaceIndex) {
- final int dbdim = DatabaseUtil.dimensionality(relation);
+ private Set<HiCSSubspace> calculateSubspaces(Relation<? extends NumberVector<?>> relation, ArrayList<ArrayDBIDs> subspaceIndex, Random random) {
+ final int dbdim = RelationUtil.dimensionality(relation);
- FiniteProgress dprog = logger.isVerbose() ? new FiniteProgress("Subspace dimensionality", dbdim, logger) : null;
- if(dprog != null) {
- dprog.setProcessed(2, logger);
+ FiniteProgress dprog = LOG.isVerbose() ? new FiniteProgress("Subspace dimensionality", dbdim, LOG) : null;
+ if (dprog != null) {
+ dprog.setProcessed(2, LOG);
}
TreeSet<HiCSSubspace> subspaceList = new TreeSet<HiCSSubspace>(HiCSSubspace.SORT_BY_SUBSPACE);
TopBoundedHeap<HiCSSubspace> dDimensionalList = new TopBoundedHeap<HiCSSubspace>(cutoff, HiCSSubspace.SORT_BY_CONTRAST_ASC);
- FiniteProgress prog = logger.isVerbose() ? new FiniteProgress("Generating two-element subsets", dbdim * (dbdim - 1) / 2, logger) : null;
+ FiniteProgress prog = LOG.isVerbose() ? new FiniteProgress("Generating two-element subsets", (dbdim * (dbdim - 1)) >> 1, LOG) : null;
// compute two-element sets of subspaces
- for(int i = 0; i < dbdim; i++) {
- for(int j = i + 1; j < dbdim; j++) {
+ for (int i = 0; i < dbdim; i++) {
+ for (int j = i + 1; j < dbdim; j++) {
HiCSSubspace ts = new HiCSSubspace();
ts.set(i);
ts.set(j);
- calculateContrast(relation, ts, subspaceIndex);
+ calculateContrast(relation, ts, subspaceIndex, random);
dDimensionalList.add(ts);
- if(prog != null) {
- prog.incrementProcessed(logger);
+ if (prog != null) {
+ prog.incrementProcessed(LOG);
}
}
}
- if(prog != null) {
- prog.ensureCompleted(logger);
+ if (prog != null) {
+ prog.ensureCompleted(LOG);
}
- IndefiniteProgress qprog = logger.isVerbose() ? new IndefiniteProgress("Testing subspace candidates", logger) : null;
- for(int d = 3; !dDimensionalList.isEmpty(); d++) {
- if(dprog != null) {
- dprog.setProcessed(d, logger);
+ IndefiniteProgress qprog = LOG.isVerbose() ? new IndefiniteProgress("Testing subspace candidates", LOG) : null;
+ for (int d = 3; !dDimensionalList.isEmpty(); d++) {
+ if (dprog != null) {
+ dprog.setProcessed(d, LOG);
}
- subspaceList.addAll(dDimensionalList);
// result now contains all d-dimensional sets of subspaces
- ArrayList<HiCSSubspace> candidateList = new ArrayList<HiCSSubspace>(dDimensionalList);
+ ArrayList<HiCSSubspace> candidateList = new ArrayList<HiCSSubspace>(dDimensionalList.size());
+ for (HiCSSubspace sub : dDimensionalList) {
+ subspaceList.add(sub);
+ candidateList.add(sub);
+ }
dDimensionalList.clear();
// candidateList now contains the *m* best d-dimensional sets
Collections.sort(candidateList, HiCSSubspace.SORT_BY_SUBSPACE);
// TODO: optimize APRIORI style, by not even computing the bit set or?
- for(int i = 0; i < candidateList.size() - 1; i++) {
- for(int j = i + 1; j < candidateList.size(); j++) {
+ for (int i = 0; i < candidateList.size() - 1; i++) {
+ for (int j = i + 1; j < candidateList.size(); j++) {
HiCSSubspace set1 = candidateList.get(i);
HiCSSubspace set2 = candidateList.get(j);
HiCSSubspace joinedSet = new HiCSSubspace();
joinedSet.or(set1);
joinedSet.or(set2);
- if(joinedSet.cardinality() != d) {
+ if (joinedSet.cardinality() != d) {
continue;
}
- calculateContrast(relation, joinedSet, subspaceIndex);
+ calculateContrast(relation, joinedSet, subspaceIndex, random);
dDimensionalList.add(joinedSet);
- if(qprog != null) {
- qprog.incrementProcessed(logger);
+ if (qprog != null) {
+ qprog.incrementProcessed(LOG);
}
}
}
// Prune
- for(HiCSSubspace cand : candidateList) {
- for(HiCSSubspace nextSet : dDimensionalList) {
- if(nextSet.contrast > cand.contrast) {
+ for (HiCSSubspace cand : candidateList) {
+ for (HiCSSubspace nextSet : dDimensionalList) {
+ if (nextSet.contrast > cand.contrast) {
subspaceList.remove(cand);
break;
}
}
}
}
- if(qprog != null) {
- qprog.setCompleted(logger);
+ if (qprog != null) {
+ qprog.setCompleted(LOG);
}
- if(dprog != null) {
- dprog.setProcessed(dbdim, logger);
- dprog.ensureCompleted(logger);
+ if (dprog != null) {
+ dprog.setProcessed(dbdim, LOG);
+ dprog.ensureCompleted(LOG);
}
return subspaceList;
}
/**
- * Calculates the actual contrast of a given subspace
+ * Calculates the actual contrast of a given subspace.
*
- * @param relation
- * @param subspace
+ * @param relation Relation to process
+ * @param subspace Subspace
* @param subspaceIndex Subspace indexes
*/
- private void calculateContrast(Relation<? extends NumberVector<?, ?>> relation, HiCSSubspace subspace, ArrayList<ArrayDBIDs> subspaceIndex) {
+ private void calculateContrast(Relation<? extends NumberVector<?>> relation, HiCSSubspace subspace, ArrayList<ArrayDBIDs> subspaceIndex, Random random) {
final int card = subspace.cardinality();
final double alpha1 = Math.pow(alpha, (1.0 / card));
final int windowsize = (int) (relation.size() * alpha1);
- final FiniteProgress prog = logger.isDebugging() ? new FiniteProgress("Monte-Carlo iterations", m, logger) : null;
+ final FiniteProgress prog = LOG.isDebugging() ? new FiniteProgress("Monte-Carlo iterations", m, LOG) : null;
int retries = 0;
double deviationSum = 0.0;
- for(int i = 0; i < m; i++) {
+ for (int i = 0; i < m; i++) {
// Choose a random set bit.
int chosen = -1;
- for(int tmp = random.nextInt(card); tmp >= 0; tmp--) {
+ for (int tmp = random.nextInt(card); tmp >= 0; tmp--) {
chosen = subspace.nextSetBit(chosen + 1);
}
// initialize sample
DBIDs conditionalSample = relation.getDBIDs();
- for(int j = subspace.nextSetBit(0); j >= 0; j = subspace.nextSetBit(j + 1)) {
- if(j == chosen) {
+ for (int j = subspace.nextSetBit(0); j >= 0; j = subspace.nextSetBit(j + 1)) {
+ if (j == chosen) {
continue;
}
ArrayDBIDs sortedIndices = subspaceIndex.get(j);
- ArrayModifiableDBIDs indexBlock = DBIDUtil.newArray();
+ ArrayModifiableDBIDs indexBlock = DBIDUtil.newArray(windowsize);
// initialize index block
- int start = random.nextInt(relation.size() - windowsize);
- for(int k = start; k < start + windowsize; k++) {
- indexBlock.add(sortedIndices.get(k)); // select index block
+ DBIDArrayIter iter = sortedIndices.iter();
+ iter.seek(random.nextInt(relation.size() - windowsize));
+ for (int k = 0; k < windowsize; k++, iter.advance()) {
+ indexBlock.add(iter); // select index block
}
conditionalSample = DBIDUtil.intersection(conditionalSample, indexBlock);
}
- if(conditionalSample.size() < 10) {
+ if (conditionalSample.size() < 10) {
retries++;
- if(logger.isDebugging()) {
- logger.debug("Sample size very small. Retry no. " + retries);
+ if (LOG.isDebugging()) {
+ LOG.debug("Sample size very small. Retry no. " + retries);
}
- if(retries >= MAX_RETRIES) {
- logger.warning("Too many retries, for small samples: " + retries);
- }
- else {
+ if (retries >= MAX_RETRIES) {
+ LOG.warning("Too many retries, for small samples: " + retries);
+ } else {
i--;
continue;
}
@@ -391,7 +396,7 @@ public class HiCS<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Outlie
{
int l = 0;
for (DBIDIter iter = conditionalSample.iter(); iter.valid(); iter.advance()) {
- sampleValues[l] = relation.get(iter).doubleValue(chosen + 1);
+ sampleValues[l] = relation.get(iter).doubleValue(chosen);
l++;
}
}
@@ -400,23 +405,23 @@ public class HiCS<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Outlie
{
int l = 0;
for (DBIDIter iter = subspaceIndex.get(chosen).iter(); iter.valid(); iter.advance()) {
- fullValues[l] = relation.get(iter).doubleValue(chosen + 1);
+ fullValues[l] = relation.get(iter).doubleValue(chosen);
l++;
}
}
double contrast = statTest.deviation(fullValues, sampleValues);
- if(Double.isNaN(contrast)) {
+ if (Double.isNaN(contrast)) {
i--;
- logger.warning("Contrast was NaN");
+ LOG.warning("Contrast was NaN");
continue;
}
deviationSum += contrast;
- if(prog != null) {
- prog.incrementProcessed(logger);
+ if (prog != null) {
+ prog.incrementProcessed(LOG);
}
}
- if(prog != null) {
- prog.ensureCompleted(logger);
+ if (prog != null) {
+ prog.ensureCompleted(LOG);
}
subspace.contrast = deviationSum / m;
}
@@ -428,7 +433,7 @@ public class HiCS<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Outlie
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -441,12 +446,12 @@ public class HiCS<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Outlie
*/
public static class HiCSSubspace extends BitSet {
/**
- * Serial version
+ * Serial version.
*/
private static final long serialVersionUID = 1L;
/**
- * The HiCS contrast value
+ * The HiCS contrast value.
*/
protected double contrast;
@@ -459,22 +464,22 @@ public class HiCS<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Outlie
@Override
public String toString() {
- StringBuffer buf = new StringBuffer();
+ StringBuilder buf = new StringBuilder();
buf.append("[contrast=").append(contrast);
- for(int i = nextSetBit(0); i >= 0; i = nextSetBit(i + 1)) {
- buf.append(" ").append(i + 1);
+ for (int i = nextSetBit(0); i >= 0; i = nextSetBit(i + 1)) {
+ buf.append(' ').append(i + 1);
}
- buf.append("]");
+ buf.append(']');
return buf.toString();
}
/**
* Sort subspaces by their actual subspace.
*/
- public static Comparator<HiCSSubspace> SORT_BY_CONTRAST_ASC = new Comparator<HiCSSubspace>() {
+ public static final Comparator<HiCSSubspace> SORT_BY_CONTRAST_ASC = new Comparator<HiCSSubspace>() {
@Override
public int compare(HiCSSubspace o1, HiCSSubspace o2) {
- if(o1.contrast == o2.contrast) {
+ if (o1.contrast == o2.contrast) {
return 0;
}
return o1.contrast > o2.contrast ? 1 : -1;
@@ -484,10 +489,10 @@ public class HiCS<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Outlie
/**
* Sort subspaces by their actual subspace.
*/
- public static Comparator<HiCSSubspace> SORT_BY_CONTRAST_DESC = new Comparator<HiCSSubspace>() {
+ public static final Comparator<HiCSSubspace> SORT_BY_CONTRAST_DESC = new Comparator<HiCSSubspace>() {
@Override
public int compare(HiCSSubspace o1, HiCSSubspace o2) {
- if(o1.contrast == o2.contrast) {
+ if (o1.contrast == o2.contrast) {
return 0;
}
return o1.contrast < o2.contrast ? 1 : -1;
@@ -497,16 +502,15 @@ public class HiCS<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Outlie
/**
* Sort subspaces by their actual subspace.
*/
- public static Comparator<HiCSSubspace> SORT_BY_SUBSPACE = new Comparator<HiCSSubspace>() {
+ public static final Comparator<HiCSSubspace> SORT_BY_SUBSPACE = new Comparator<HiCSSubspace>() {
@Override
public int compare(HiCSSubspace o1, HiCSSubspace o2) {
int dim1 = o1.nextSetBit(0);
int dim2 = o2.nextSetBit(0);
- while(dim1 >= 0 && dim2 >= 0) {
- if(dim1 < dim2) {
+ while (dim1 >= 0 && dim2 >= 0) {
+ if (dim1 < dim2) {
return -1;
- }
- else if(dim1 > dim2) {
+ } else if (dim1 > dim2) {
return 1;
}
dim1 = o1.nextSetBit(dim1 + 1);
@@ -518,7 +522,7 @@ public class HiCS<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Outlie
}
/**
- * Parameterization class
+ * Parameterization class.
*
* @author Jan Brusis
*
@@ -526,40 +530,40 @@ public class HiCS<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Outlie
*
* @param <V> vector type
*/
- public static class Parameterizer<V extends NumberVector<V, ?>> extends AbstractParameterizer {
+ public static class Parameterizer<V extends NumberVector<?>> extends AbstractParameterizer {
/**
* Parameter that specifies the number of iterations in the Monte-Carlo
- * process of identifying high contrast subspaces
+ * process of identifying high contrast subspaces.
*/
- public static final OptionID M_ID = OptionID.getOrCreateOptionID("hics.m", "The number of iterations in the Monte-Carlo processing.");
+ public static final OptionID M_ID = new OptionID("hics.m", "The number of iterations in the Monte-Carlo processing.");
/**
* Parameter that determines the size of the test statistic during the
- * Monte-Carlo iteration
+ * Monte-Carlo iteration.
*/
- public static final OptionID ALPHA_ID = OptionID.getOrCreateOptionID("hics.alpha", "The discriminance value that determines the size of the test statistic .");
+ public static final OptionID ALPHA_ID = new OptionID("hics.alpha", "The discriminance value that determines the size of the test statistic .");
/**
* Parameter that specifies which outlier detection algorithm to use on the
- * resulting set of high contrast subspaces
+ * resulting set of high contrast subspaces.
*/
- public static final OptionID ALGO_ID = OptionID.getOrCreateOptionID("hics.algo", "The Algorithm that performs the actual outlier detection on the resulting set of subspace");
+ public static final OptionID ALGO_ID = new OptionID("hics.algo", "The Algorithm that performs the actual outlier detection on the resulting set of subspace");
/**
* Parameter that specifies which statistical test to use in order to
- * calculate the deviation of two given data samples
+ * calculate the deviation of two given data samples.
*/
- public static final OptionID TEST_ID = OptionID.getOrCreateOptionID("hics.test", "The statistical test that is used to calculate the deviation of two data samples");
+ public static final OptionID TEST_ID = new OptionID("hics.test", "The statistical test that is used to calculate the deviation of two data samples");
/**
- * Parameter that specifies the candidate_cutoff
+ * Parameter that specifies the candidate_cutoff.
*/
- public static final OptionID LIMIT_ID = OptionID.getOrCreateOptionID("hics.limit", "The threshold that determines how many d-dimensional subspace candidates to retain in each step of the generation");
+ public static final OptionID LIMIT_ID = new OptionID("hics.limit", "The threshold that determines how many d-dimensional subspace candidates to retain in each step of the generation");
/**
- * Parameter that specifies the random seed
+ * Parameter that specifies the random seed.
*/
- public static final OptionID SEED_ID = OptionID.getOrCreateOptionID("hics.seed", "The random seed.");
+ public static final OptionID SEED_ID = new OptionID("hics.seed", "The random seed.");
/**
* Holds the value of {@link #M_ID}.
@@ -582,52 +586,55 @@ public class HiCS<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Outlie
private GoodnessOfFitTest statTest;
/**
- * Holds the value of {@link #LIMIT_ID}
+ * Holds the value of {@link #LIMIT_ID}.
*/
private int cutoff = 400;
-
+
/**
- * Random seed (optional)
+ * Random generator.
*/
- private Long seed = null;
+ private RandomFactory rnd;
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- final IntParameter mP = new IntParameter(M_ID, new GreaterConstraint(1), 50);
- if(config.grab(mP)) {
- m = mP.getValue();
+ final IntParameter mP = new IntParameter(M_ID, 50);
+ mP.addConstraint(new GreaterConstraint(1));
+ if (config.grab(mP)) {
+ m = mP.intValue();
}
- final DoubleParameter alphaP = new DoubleParameter(ALPHA_ID, new GreaterConstraint(0), 0.1);
- if(config.grab(alphaP)) {
- alpha = alphaP.getValue();
+ final DoubleParameter alphaP = new DoubleParameter(ALPHA_ID, 0.1);
+ alphaP.addConstraint(new GreaterConstraint(0));
+ if (config.grab(alphaP)) {
+ alpha = alphaP.doubleValue();
}
final ObjectParameter<OutlierAlgorithm> algoP = new ObjectParameter<OutlierAlgorithm>(ALGO_ID, OutlierAlgorithm.class, LOF.class);
- if(config.grab(algoP)) {
+ if (config.grab(algoP)) {
outlierAlgorithm = algoP.instantiateClass(config);
}
final ObjectParameter<GoodnessOfFitTest> testP = new ObjectParameter<GoodnessOfFitTest>(TEST_ID, GoodnessOfFitTest.class, KolmogorovSmirnovTest.class);
- if(config.grab(testP)) {
+ if (config.grab(testP)) {
statTest = testP.instantiateClass(config);
}
- final IntParameter cutoffP = new IntParameter(LIMIT_ID, new GreaterConstraint(1), 100);
- if(config.grab(cutoffP)) {
- cutoff = cutoffP.getValue();
+ final IntParameter cutoffP = new IntParameter(LIMIT_ID, 100);
+ cutoffP.addConstraint(new GreaterConstraint(1));
+ if (config.grab(cutoffP)) {
+ cutoff = cutoffP.intValue();
}
- final LongParameter seedP = new LongParameter(SEED_ID, true);
- if(config.grab(seedP)) {
- seed = seedP.getValue();
+ final RandomParameter rndP = new RandomParameter(SEED_ID);
+ if (config.grab(rndP)) {
+ rnd = rndP.getValue();
}
-}
+ }
@Override
protected HiCS<V> makeInstance() {
- return new HiCS<V>(m, alpha, outlierAlgorithm, statTest, cutoff, seed);
+ return new HiCS<V>(m, alpha, outlierAlgorithm, statTest, cutoff, rnd);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/meta/RescaleMetaOutlierAlgorithm.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/meta/RescaleMetaOutlierAlgorithm.java
index a4db7e3d..387041da 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/meta/RescaleMetaOutlierAlgorithm.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/meta/RescaleMetaOutlierAlgorithm.java
@@ -62,7 +62,7 @@ public class RescaleMetaOutlierAlgorithm extends AbstractAlgorithm<OutlierResult
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(RescaleMetaOutlierAlgorithm.class);
+ private static final Logging LOG = Logging.getLogger(RescaleMetaOutlierAlgorithm.class);
/**
* Parameter to specify a scaling function to use.
@@ -70,7 +70,7 @@ public class RescaleMetaOutlierAlgorithm extends AbstractAlgorithm<OutlierResult
* Key: {@code -comphist.scaling}
* </p>
*/
- public static final OptionID SCALING_ID = OptionID.getOrCreateOptionID("metaoutlier.scaling", "Class to use as scaling function.");
+ public static final OptionID SCALING_ID = new OptionID("metaoutlier.scaling", "Class to use as scaling function.");
/**
* Holds the algorithm to run.
@@ -137,7 +137,7 @@ public class RescaleMetaOutlierAlgorithm extends AbstractAlgorithm<OutlierResult
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
@Override
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/meta/SimpleOutlierEnsemble.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/meta/SimpleOutlierEnsemble.java
new file mode 100644
index 00000000..b7791fc4
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/meta/SimpleOutlierEnsemble.java
@@ -0,0 +1,222 @@
+package de.lmu.ifi.dbs.elki.algorithm.outlier.meta;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import de.lmu.ifi.dbs.elki.algorithm.AbstractAlgorithm;
+import de.lmu.ifi.dbs.elki.algorithm.Algorithm;
+import de.lmu.ifi.dbs.elki.algorithm.outlier.OutlierAlgorithm;
+import de.lmu.ifi.dbs.elki.data.type.CombinedTypeInformation;
+import de.lmu.ifi.dbs.elki.data.type.TypeInformation;
+import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
+import de.lmu.ifi.dbs.elki.database.Database;
+import de.lmu.ifi.dbs.elki.database.datastore.DataStoreFactory;
+import de.lmu.ifi.dbs.elki.database.datastore.DataStoreUtil;
+import de.lmu.ifi.dbs.elki.database.datastore.WritableDoubleDataStore;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
+import de.lmu.ifi.dbs.elki.database.ids.ModifiableDBIDs;
+import de.lmu.ifi.dbs.elki.database.relation.MaterializedRelation;
+import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.logging.Logging;
+import de.lmu.ifi.dbs.elki.logging.progress.FiniteProgress;
+import de.lmu.ifi.dbs.elki.math.DoubleMinMax;
+import de.lmu.ifi.dbs.elki.result.Result;
+import de.lmu.ifi.dbs.elki.result.ResultUtil;
+import de.lmu.ifi.dbs.elki.result.outlier.BasicOutlierScoreMeta;
+import de.lmu.ifi.dbs.elki.result.outlier.OutlierResult;
+import de.lmu.ifi.dbs.elki.result.outlier.OutlierScoreMeta;
+import de.lmu.ifi.dbs.elki.utilities.ensemble.EnsembleVoting;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.ChainedParameterization;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.ListParameterization;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ObjectListParameter;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ObjectParameter;
+
+/**
+ * Simple outlier ensemble method.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.composedOf EnsembleVoting
+ * @apiviz.uses OutlierResult oneway - - reads
+ * @apiviz.uses OutlierResult oneway - - «create»
+ */
+public class SimpleOutlierEnsemble extends AbstractAlgorithm<OutlierResult> implements OutlierAlgorithm {
+ /**
+ * The logger for this class.
+ */
+ private static final Logging LOG = Logging.getLogger(SimpleOutlierEnsemble.class);
+
+ /**
+ * The algorithms to run.
+ */
+ private List<OutlierAlgorithm> algorithms;
+
+ /**
+ * The voting in use.
+ */
+ private EnsembleVoting voting;
+
+ /**
+ * Constructor.
+ *
+ * @param algorithms Algorithms to run
+ * @param voting Voting method
+ */
+ public SimpleOutlierEnsemble(List<OutlierAlgorithm> algorithms, EnsembleVoting voting) {
+ this.algorithms = algorithms;
+ this.voting = voting;
+ }
+
+ @Override
+ public OutlierResult run(Database database) throws IllegalStateException {
+ int num = algorithms.size();
+ // Run inner outlier algorithms
+ ModifiableDBIDs ids = DBIDUtil.newHashSet();
+ ArrayList<OutlierResult> results = new ArrayList<OutlierResult>(num);
+ {
+ FiniteProgress prog = LOG.isVerbose() ? new FiniteProgress("Inner outlier algorithms", num, LOG) : null;
+ for (Algorithm alg : algorithms) {
+ Result res = alg.run(database);
+ List<OutlierResult> ors = ResultUtil.getOutlierResults(res);
+ for (OutlierResult or : ors) {
+ results.add(or);
+ ids.addDBIDs(or.getScores().getDBIDs());
+ }
+ if (prog != null) {
+ prog.incrementProcessed(LOG);
+ }
+ }
+ if (prog != null) {
+ prog.ensureCompleted(LOG);
+ }
+ }
+ // Combine
+ WritableDoubleDataStore sumscore = DataStoreUtil.makeDoubleStorage(ids, DataStoreFactory.HINT_STATIC);
+ DoubleMinMax minmax = new DoubleMinMax();
+ {
+ FiniteProgress cprog = LOG.isVerbose() ? new FiniteProgress("Combining results", ids.size(), LOG) : null;
+ for (DBIDIter id = ids.iter(); id.valid(); id.advance()) {
+ double[] scores = new double[num];
+ int i = 0;
+ for (OutlierResult r : results) {
+ Double score = r.getScores().get(id);
+ if (score != null) {
+ scores[i] = score;
+ i++;
+ } else {
+ LOG.warning("DBID " + id + " was not given a score by result " + r);
+ }
+ }
+ if (i > 0) {
+ // Shrink array if necessary.
+ if (i < scores.length) {
+ scores = Arrays.copyOf(scores, i);
+ }
+ double combined = voting.combine(scores);
+ sumscore.putDouble(id, combined);
+ minmax.put(combined);
+ } else {
+ LOG.warning("DBID " + id + " was not given any score at all.");
+ }
+ if (cprog != null) {
+ cprog.incrementProcessed(LOG);
+ }
+ }
+ if (cprog != null) {
+ cprog.ensureCompleted(LOG);
+ }
+ }
+ OutlierScoreMeta meta = new BasicOutlierScoreMeta(minmax.getMin(), minmax.getMax());
+ Relation<Double> scores = new MaterializedRelation<Double>("Simple Outlier Ensemble", "ensemble-outlier", TypeUtil.DOUBLE, sumscore, ids);
+ return new OutlierResult(meta, scores);
+ }
+
+ @Override
+ protected Logging getLogger() {
+ return LOG;
+ }
+
+ @Override
+ public TypeInformation[] getInputTypeRestriction() {
+ TypeInformation[] trs = new TypeInformation[algorithms.size()];
+ for (int i = 0; i < trs.length; i++) {
+ // FIXME: what if an algorithm needs more than one input data source?
+ trs[i] = algorithms.get(i).getInputTypeRestriction()[0];
+ }
+ return TypeUtil.array(new CombinedTypeInformation(trs));
+ }
+
+ /**
+ * Parameterization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public static class Parameterizer extends AbstractParameterizer {
+ /**
+ * Voting strategy to use in the ensemble.
+ */
+ public static final OptionID VOTING_ID = new OptionID("ensemble.voting", "Voting strategy to use in the ensemble.");
+
+ /**
+ * The algorithms to run.
+ */
+ private List<OutlierAlgorithm> algorithms;
+
+ /**
+ * The voting in use.
+ */
+ private EnsembleVoting voting;
+
+ @Override
+ protected void makeOptions(Parameterization config) {
+ super.makeOptions(config);
+ ObjectListParameter<OutlierAlgorithm> algP = new ObjectListParameter<OutlierAlgorithm>(OptionID.ALGORITHM, OutlierAlgorithm.class);
+ if (config.grab(algP)) {
+ ListParameterization subconfig = new ListParameterization();
+ ChainedParameterization chain = new ChainedParameterization(subconfig, config);
+ chain.errorsTo(config);
+ algorithms = algP.instantiateClasses(chain);
+ subconfig.logAndClearReportedErrors();
+ }
+ ObjectParameter<EnsembleVoting> votingP = new ObjectParameter<EnsembleVoting>(VOTING_ID, EnsembleVoting.class);
+ if (config.grab(votingP)) {
+ voting = votingP.instantiateClass(config);
+ }
+ }
+
+ @Override
+ protected SimpleOutlierEnsemble makeInstance() {
+ return new SimpleOutlierEnsemble(algorithms, voting);
+ }
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/meta/package-info.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/meta/package-info.java
index d7e78281..7c5dd8b0 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/meta/package-info.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/meta/package-info.java
@@ -1,5 +1,8 @@
/**
* <p>Meta outlier detection algorithms: external scores, score rescaling.</p>
+ *
+ * @apiviz.exclude java.io.File
+ * @apiviz.exclude algorithm.AbstractAlgorithm
*/
/*
This file is part of ELKI:
@@ -23,4 +26,4 @@ GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package de.lmu.ifi.dbs.elki.algorithm.outlier.meta; \ No newline at end of file
+package de.lmu.ifi.dbs.elki.algorithm.outlier.meta;
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/package-info.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/package-info.java
index ea5d3ec4..eca0d876 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/package-info.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/package-info.java
@@ -4,6 +4,11 @@
* @see de.lmu.ifi.dbs.elki.algorithm
*
* @apiviz.exclude database.query
+ * @apiviz.exclude java.lang.Comparable
+ * @apiviz.exclude de.lmu.ifi.dbs.elki.utilities
+ * @apiviz.exclude de.lmu.ifi.dbs.elki.algorithm.Algorithm
+ * @apiviz.exclude de.lmu.ifi.dbs.elki.algorithm.AbstractAlgorithm
+ * @apiviz.exclude AggarwalYuEvoluationary.Individuum
*/
/*
This file is part of ELKI:
@@ -27,4 +32,4 @@ GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package de.lmu.ifi.dbs.elki.algorithm.outlier; \ No newline at end of file
+package de.lmu.ifi.dbs.elki.algorithm.outlier;
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/AbstractDistanceBasedSpatialOutlier.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/AbstractDistanceBasedSpatialOutlier.java
index 1caf7582..f37ee182 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/AbstractDistanceBasedSpatialOutlier.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/AbstractDistanceBasedSpatialOutlier.java
@@ -45,7 +45,7 @@ public abstract class AbstractDistanceBasedSpatialOutlier<N, O, D extends Number
/**
* Parameter to specify the non spatial distance function to use
*/
- public static final OptionID NON_SPATIAL_DISTANCE_FUNCTION_ID = OptionID.getOrCreateOptionID("spatialoutlier.nonspatialdistance", "The distance function to use for non spatial attributes");
+ public static final OptionID NON_SPATIAL_DISTANCE_FUNCTION_ID = new OptionID("spatialoutlier.nonspatialdistance", "The distance function to use for non spatial attributes");
/**
* The distance function to use
@@ -84,7 +84,7 @@ public abstract class AbstractDistanceBasedSpatialOutlier<N, O, D extends Number
* @param <O> Non-spatial object type
* @param <D> Distance value type
*/
- public static abstract class Parameterizer<N, O, D extends NumberDistance<D, ?>> extends AbstractNeighborhoodOutlier.Parameterizer<N> {
+ public abstract static class Parameterizer<N, O, D extends NumberDistance<D, ?>> extends AbstractNeighborhoodOutlier.Parameterizer<N> {
/**
* The distance function to use on the non-spatial attributes.
*/
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/AbstractNeighborhoodOutlier.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/AbstractNeighborhoodOutlier.java
index f0c05e1e..d3770504 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/AbstractNeighborhoodOutlier.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/AbstractNeighborhoodOutlier.java
@@ -44,7 +44,7 @@ public abstract class AbstractNeighborhoodOutlier<O> extends AbstractAlgorithm<O
/**
* Parameter to specify the neighborhood predicate to use.
*/
- public static final OptionID NEIGHBORHOOD_ID = OptionID.getOrCreateOptionID("neighborhood", "The neighborhood predicate to use in comparison step.");
+ public static final OptionID NEIGHBORHOOD_ID = new OptionID("neighborhood", "The neighborhood predicate to use in comparison step.");
/**
* Our predicate to obtain the neighbors
@@ -79,7 +79,7 @@ public abstract class AbstractNeighborhoodOutlier<O> extends AbstractAlgorithm<O
*
* @param <O> Object type
*/
- public static abstract class Parameterizer<O> extends AbstractParameterizer {
+ public abstract static class Parameterizer<O> extends AbstractParameterizer {
/**
* The predicate to obtain the neighbors.
*/
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/CTLuGLSBackwardSearchAlgorithm.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/CTLuGLSBackwardSearchAlgorithm.java
index 7f3bac29..cd5670f7 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/CTLuGLSBackwardSearchAlgorithm.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/CTLuGLSBackwardSearchAlgorithm.java
@@ -37,13 +37,13 @@ import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.ModifiableDBIDs;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
import de.lmu.ifi.dbs.elki.database.query.knn.KNNQuery;
-import de.lmu.ifi.dbs.elki.database.query.knn.KNNResult;
import de.lmu.ifi.dbs.elki.database.relation.MaterializedRelation;
import de.lmu.ifi.dbs.elki.database.relation.ProxyView;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.math.DoubleMinMax;
@@ -52,7 +52,6 @@ import de.lmu.ifi.dbs.elki.math.statistics.distribution.NormalDistribution;
import de.lmu.ifi.dbs.elki.result.outlier.BasicOutlierScoreMeta;
import de.lmu.ifi.dbs.elki.result.outlier.OutlierResult;
import de.lmu.ifi.dbs.elki.result.outlier.OutlierScoreMeta;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
import de.lmu.ifi.dbs.elki.utilities.documentation.Title;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
@@ -85,11 +84,11 @@ import de.lmu.ifi.dbs.elki.utilities.pairs.Pair;
*/
@Title("GLS-Backward Search")
@Reference(authors = "F. Chen and C.-T. Lu and A. P. Boedihardjo", title = "GLS-SOD: A Generalized Local Statistical Approach for Spatial Outlier Detection", booktitle = "Proc. 16th ACM SIGKDD international conference on Knowledge discovery and data mining", url = "http://dx.doi.org/10.1145/1835804.1835939")
-public class CTLuGLSBackwardSearchAlgorithm<V extends NumberVector<?, ?>, D extends NumberDistance<D, ?>> extends AbstractDistanceBasedAlgorithm<V, D, OutlierResult> implements OutlierAlgorithm {
+public class CTLuGLSBackwardSearchAlgorithm<V extends NumberVector<?>, D extends NumberDistance<D, ?>> extends AbstractDistanceBasedAlgorithm<V, D, OutlierResult> implements OutlierAlgorithm {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(CTLuGLSBackwardSearchAlgorithm.class);
+ private static final Logging LOG = Logging.getLogger(CTLuGLSBackwardSearchAlgorithm.class);
/**
* Parameter Alpha - significance niveau
@@ -121,7 +120,7 @@ public class CTLuGLSBackwardSearchAlgorithm<V extends NumberVector<?, ?>, D exte
* @param relationy Attribute relation
* @return Algorithm result
*/
- public OutlierResult run(Relation<V> relationx, Relation<? extends NumberVector<?, ?>> relationy) {
+ public OutlierResult run(Relation<V> relationx, Relation<? extends NumberVector<?>> relationy) {
WritableDoubleDataStore scores = DataStoreUtil.makeDoubleStorage(relationx.getDBIDs(), DataStoreFactory.HINT_STATIC);
DoubleMinMax mm = new DoubleMinMax(0.0, 0.0);
@@ -130,7 +129,7 @@ public class CTLuGLSBackwardSearchAlgorithm<V extends NumberVector<?, ?>, D exte
ModifiableDBIDs idview = DBIDUtil.newHashSet(relationx.getDBIDs());
ProxyView<V> proxy = new ProxyView<V>(relationx.getDatabase(), idview, relationx);
- double phialpha = NormalDistribution.standardNormalQuantile(1.0 - alpha / 2);
+ double phialpha = NormalDistribution.standardNormalQuantile(1.0 - alpha *.5);
// Detect outliers while significant.
while(true) {
Pair<DBID, Double> candidate = singleIteration(proxy, relationy);
@@ -138,15 +137,15 @@ public class CTLuGLSBackwardSearchAlgorithm<V extends NumberVector<?, ?>, D exte
break;
}
scores.putDouble(candidate.first, candidate.second);
- if (!Double.isNaN(candidate.second)) {
+ if(!Double.isNaN(candidate.second)) {
mm.put(candidate.second);
}
idview.remove(candidate.first);
}
// Remaining objects are inliers
- for (DBIDIter iter = idview.iter(); iter.valid(); iter.advance()) {
- scores.putDouble(iter.getDBID(), 0.0);
+ for(DBIDIter iter = idview.iter(); iter.valid(); iter.advance()) {
+ scores.putDouble(iter, 0.0);
}
}
@@ -162,9 +161,9 @@ public class CTLuGLSBackwardSearchAlgorithm<V extends NumberVector<?, ?>, D exte
* @param relationy Attribute relation
* @return Top outlier and associated score
*/
- private Pair<DBID, Double> singleIteration(Relation<V> relationx, Relation<? extends NumberVector<?, ?>> relationy) {
- final int dim = DatabaseUtil.dimensionality(relationx);
- final int dimy = DatabaseUtil.dimensionality(relationy);
+ private Pair<DBID, Double> singleIteration(Relation<V> relationx, Relation<? extends NumberVector<?>> relationy) {
+ final int dim = RelationUtil.dimensionality(relationx);
+ final int dimy = RelationUtil.dimensionality(relationy);
assert (dim == 2);
KNNQuery<V, D> knnQuery = QueryUtil.getKNNQuery(relationx, getDistanceFunction(), k + 1);
@@ -177,47 +176,51 @@ public class CTLuGLSBackwardSearchAlgorithm<V extends NumberVector<?, ?>, D exte
Matrix X = new Matrix(ids.size(), 6);
Matrix F = new Matrix(ids.size(), ids.size());
Matrix Y = new Matrix(ids.size(), dimy);
- for(int i = 0; i < ids.size(); i++) {
- DBID id = ids.get(i);
-
- // Fill the data matrix
- {
- V vec = relationx.get(id);
- double la = vec.doubleValue(1);
- double lo = vec.doubleValue(2);
- X.set(i, 0, 1.0);
- X.set(i, 1, la);
- X.set(i, 2, lo);
- X.set(i, 3, la * lo);
- X.set(i, 4, la * la);
- X.set(i, 5, lo * lo);
- }
- {
- for(int d = 0; d < dimy; d++) {
- double idy = relationy.get(id).doubleValue(d + 1);
- Y.set(i, d, idy);
+ {
+ int i = 0;
+ for(DBIDIter id = ids.iter(); id.valid(); id.advance(), i++) {
+ // Fill the data matrix
+ {
+ V vec = relationx.get(id);
+ double la = vec.doubleValue(0);
+ double lo = vec.doubleValue(1);
+ X.set(i, 0, 1.0);
+ X.set(i, 1, la);
+ X.set(i, 2, lo);
+ X.set(i, 3, la * lo);
+ X.set(i, 4, la * la);
+ X.set(i, 5, lo * lo);
}
- }
- // Fill the neighborhood matrix F:
- {
- KNNResult<D> neighbors = knnQuery.getKNNForDBID(id, k + 1);
- ModifiableDBIDs neighborhood = DBIDUtil.newArray(neighbors.size());
- for(DistanceResultPair<D> dpair : neighbors) {
- if(id.sameDBID(dpair.getDBID())) {
- continue;
+ {
+ final NumberVector<?> vecy = relationy.get(id);
+ for(int d = 0; d < dimy; d++) {
+ double idy = vecy.doubleValue(d);
+ Y.set(i, d, idy);
}
- neighborhood.add(dpair.getDBID());
}
- // Weight object itself positively.
- F.set(i, i, 1.0);
- final int nweight = -1 / neighborhood.size();
- // We need to find the index positions of the neighbors, unfortunately.
- for (DBIDIter iter = neighborhood.iter(); iter.valid(); iter.advance()) {
- int pos = ids.binarySearch(iter.getDBID());
- assert (pos >= 0);
- F.set(pos, i, nweight);
+
+ // Fill the neighborhood matrix F:
+ {
+ KNNResult<D> neighbors = knnQuery.getKNNForDBID(id, k + 1);
+ ModifiableDBIDs neighborhood = DBIDUtil.newArray(neighbors.size());
+ for(DBIDIter neighbor = neighbors.iter(); neighbor.valid(); neighbor.advance()) {
+ if(DBIDUtil.equal(id, neighbor)) {
+ continue;
+ }
+ neighborhood.add(neighbor);
+ }
+ // Weight object itself positively.
+ F.set(i, i, 1.0);
+ final int nweight = -1 / neighborhood.size();
+ // We need to find the index positions of the neighbors,
+ // unfortunately.
+ for(DBIDIter iter = neighborhood.iter(); iter.valid(); iter.advance()) {
+ int pos = ids.binarySearch(iter);
+ assert (pos >= 0);
+ F.set(pos, i, nweight);
+ }
}
}
}
@@ -236,13 +239,13 @@ public class CTLuGLSBackwardSearchAlgorithm<V extends NumberVector<?, ?>, D exte
DBID worstid = null;
double worstscore = Double.NEGATIVE_INFINITY;
- for(int i = 0; i < ids.size(); i++) {
- DBID id = ids.get(i);
+ int i = 0;
+ for(DBIDIter id = ids.iter(); id.valid(); id.advance(), i++) {
double err = E.getRow(i).euclideanLength();
// double err = Math.abs(E.get(i, 0));
if(err > worstscore) {
worstscore = err;
- worstid = id;
+ worstid = DBIDUtil.deref(id);
}
}
@@ -256,7 +259,7 @@ public class CTLuGLSBackwardSearchAlgorithm<V extends NumberVector<?, ?>, D exte
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -269,16 +272,16 @@ public class CTLuGLSBackwardSearchAlgorithm<V extends NumberVector<?, ?>, D exte
* @param <V> Input vector type
* @param <D> Distance type
*/
- public static class Parameterizer<V extends NumberVector<?, ?>, D extends NumberDistance<D, ?>> extends AbstractDistanceBasedAlgorithm.Parameterizer<V, D> {
+ public static class Parameterizer<V extends NumberVector<?>, D extends NumberDistance<D, ?>> extends AbstractDistanceBasedAlgorithm.Parameterizer<V, D> {
/**
* Holds the alpha value - significance niveau
*/
- public static final OptionID ALPHA_ID = OptionID.getOrCreateOptionID("glsbs.alpha", "Significance niveau");
+ public static final OptionID ALPHA_ID = new OptionID("glsbs.alpha", "Significance niveau");
/**
* Parameter to specify the k nearest neighbors
*/
- public static final OptionID K_ID = OptionID.getOrCreateOptionID("glsbs.k", "k nearest neighbors to use");
+ public static final OptionID K_ID = new OptionID("glsbs.k", "k nearest neighbors to use");
/**
* Parameter Alpha - significance niveau
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/CTLuMeanMultipleAttributes.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/CTLuMeanMultipleAttributes.java
index a0c09057..2caee128 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/CTLuMeanMultipleAttributes.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/CTLuMeanMultipleAttributes.java
@@ -31,11 +31,11 @@ import de.lmu.ifi.dbs.elki.database.datastore.DataStoreFactory;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreUtil;
import de.lmu.ifi.dbs.elki.database.datastore.WritableDataStore;
import de.lmu.ifi.dbs.elki.database.datastore.WritableDoubleDataStore;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.relation.MaterializedRelation;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.math.DoubleMinMax;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Centroid;
@@ -45,7 +45,6 @@ import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector;
import de.lmu.ifi.dbs.elki.result.outlier.BasicOutlierScoreMeta;
import de.lmu.ifi.dbs.elki.result.outlier.OutlierResult;
import de.lmu.ifi.dbs.elki.result.outlier.OutlierScoreMeta;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
/**
@@ -72,11 +71,11 @@ import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
* @param <O> Attribute Vector
*/
@Reference(authors = "Chang-Tien Lu and Dechang Chen and Yufeng Kou", title = "Detecting Spatial Outliers with Multiple Attributes", booktitle = "Proc. 15th IEEE International Conference on Tools with Artificial Intelligence, 2003", url = "http://dx.doi.org/10.1109/TAI.2003.1250179")
-public class CTLuMeanMultipleAttributes<N, O extends NumberVector<?, ?>> extends AbstractNeighborhoodOutlier<N> {
+public class CTLuMeanMultipleAttributes<N, O extends NumberVector<?>> extends AbstractNeighborhoodOutlier<N> {
/**
* logger
*/
- public static final Logging logger = Logging.getLogger(CTLuMeanMultipleAttributes.class);
+ private static final Logging LOG = Logging.getLogger(CTLuMeanMultipleAttributes.class);
/**
* Constructor
@@ -89,28 +88,27 @@ public class CTLuMeanMultipleAttributes<N, O extends NumberVector<?, ?>> extends
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
public OutlierResult run(Relation<N> spatial, Relation<O> attributes) {
- if(logger.isDebugging()) {
- logger.debug("Dimensionality: " + DatabaseUtil.dimensionality(attributes));
+ if(LOG.isDebugging()) {
+ LOG.debug("Dimensionality: " + RelationUtil.dimensionality(attributes));
}
final NeighborSetPredicate npred = getNeighborSetPredicateFactory().instantiate(spatial);
- CovarianceMatrix covmaker = new CovarianceMatrix(DatabaseUtil.dimensionality(attributes));
+ CovarianceMatrix covmaker = new CovarianceMatrix(RelationUtil.dimensionality(attributes));
WritableDataStore<Vector> deltas = DataStoreUtil.makeStorage(attributes.getDBIDs(), DataStoreFactory.HINT_TEMP, Vector.class);
for(DBIDIter iditer = attributes.iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
- final O obj = attributes.get(id);
- final DBIDs neighbors = npred.getNeighborDBIDs(id);
+ final O obj = attributes.get(iditer);
+ final DBIDs neighbors = npred.getNeighborDBIDs(iditer);
// TODO: remove object itself from neighbors?
// Mean vector "g"
Vector mean = Centroid.make(attributes, neighbors);
// Delta vector "h"
- Vector delta = obj.getColumnVector().minus(mean);
- deltas.put(id, delta);
+ Vector delta = obj.getColumnVector().minusEquals(mean);
+ deltas.put(iditer, delta);
covmaker.put(delta);
}
// Finalize covariance matrix:
@@ -120,11 +118,10 @@ public class CTLuMeanMultipleAttributes<N, O extends NumberVector<?, ?>> extends
DoubleMinMax minmax = new DoubleMinMax();
WritableDoubleDataStore scores = DataStoreUtil.makeDoubleStorage(attributes.getDBIDs(), DataStoreFactory.HINT_STATIC);
for(DBIDIter iditer = attributes.iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
- Vector temp = deltas.get(id).minus(mean);
+ Vector temp = deltas.get(iditer).minus(mean);
final double score = temp.transposeTimesTimes(cmati, temp);
minmax.put(score);
- scores.putDouble(id, score);
+ scores.putDouble(iditer, score);
}
Relation<Double> scoreResult = new MaterializedRelation<Double>("mean multiple attributes spatial outlier", "mean-multipleattributes-outlier", TypeUtil.DOUBLE, scores, attributes.getDBIDs());
@@ -149,7 +146,7 @@ public class CTLuMeanMultipleAttributes<N, O extends NumberVector<?, ?>> extends
* @param <N> Neighborhood type
* @param <O> Attribute object type
*/
- public static class Parameterizer<N, O extends NumberVector<?, ?>> extends AbstractNeighborhoodOutlier.Parameterizer<N> {
+ public static class Parameterizer<N, O extends NumberVector<?>> extends AbstractNeighborhoodOutlier.Parameterizer<N> {
@Override
protected CTLuMeanMultipleAttributes<N, O> makeInstance() {
return new CTLuMeanMultipleAttributes<N, O>(npredf);
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/CTLuMedianAlgorithm.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/CTLuMedianAlgorithm.java
index 20ab9a00..7755a459 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/CTLuMedianAlgorithm.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/CTLuMedianAlgorithm.java
@@ -1,26 +1,27 @@
package de.lmu.ifi.dbs.elki.algorithm.outlier.spatial;
-/*
-This file is part of ELKI:
-Environment for Developing KDD-Applications Supported by Index-Structures
-
-Copyright (C) 2012
-Ludwig-Maximilians-Universität München
-Lehr- und Forschungseinheit für Datenbanksysteme
-ELKI Development Team
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
import de.lmu.ifi.dbs.elki.algorithm.outlier.spatial.neighborhood.NeighborSetPredicate;
import de.lmu.ifi.dbs.elki.data.NumberVector;
@@ -30,8 +31,8 @@ import de.lmu.ifi.dbs.elki.data.type.VectorFieldTypeInformation;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreFactory;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreUtil;
import de.lmu.ifi.dbs.elki.database.datastore.WritableDoubleDataStore;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.relation.MaterializedRelation;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
@@ -60,22 +61,22 @@ import de.lmu.ifi.dbs.elki.utilities.documentation.Title;
* The Difference e = non-spatial-Attribute-Value - Median (Neighborhood) is
* computed.<br>
* The Spatial Objects with the highest standardized e value are Spatial
- * Outliers. </p>
+ * Outliers.
*
* @author Ahmed Hettab
*
* @param <N> Neighborhood type
*/
@Title("Median Algorithm for Spatial Outlier Detection")
-@Reference(authors = "C.-T. Lu and D. Chen and Y. Kou", title = "Algorithms for Spatial Outlier Detection", booktitle = "Proc. 3rd IEEE International Conference on Data Mining", url="http://dx.doi.org/10.1109/ICDM.2003.1250986")
+@Reference(authors = "C.-T. Lu and D. Chen and Y. Kou", title = "Algorithms for Spatial Outlier Detection", booktitle = "Proc. 3rd IEEE International Conference on Data Mining", url = "http://dx.doi.org/10.1109/ICDM.2003.1250986")
public class CTLuMedianAlgorithm<N> extends AbstractNeighborhoodOutlier<N> {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(CTLuMedianAlgorithm.class);
+ private static final Logging LOG = Logging.getLogger(CTLuMedianAlgorithm.class);
/**
- * Constructor
+ * Constructor.
*
* @param npredf Neighborhood predicate
*/
@@ -84,42 +85,40 @@ public class CTLuMedianAlgorithm<N> extends AbstractNeighborhoodOutlier<N> {
}
/**
- * Main method
+ * Main method.
*
* @param nrel Neighborhood relation
* @param relation Data relation (1d!)
* @return Outlier detection result
*/
- public OutlierResult run(Relation<N> nrel, Relation<? extends NumberVector<?, ?>> relation) {
+ public OutlierResult run(Relation<N> nrel, Relation<? extends NumberVector<?>> relation) {
final NeighborSetPredicate npred = getNeighborSetPredicateFactory().instantiate(nrel);
WritableDoubleDataStore scores = DataStoreUtil.makeDoubleStorage(relation.getDBIDs(), DataStoreFactory.HINT_STATIC);
MeanVariance mv = new MeanVariance();
- for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
- DBIDs neighbors = npred.getNeighborDBIDs(id);
+ for (DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
+ DBIDs neighbors = npred.getNeighborDBIDs(iditer);
final double median;
{
double[] fi = new double[neighbors.size()];
// calculate and store Median of neighborhood
int c = 0;
- for(DBIDIter iter = neighbors.iter(); iter.valid(); iter.advance()) {
- if(id.sameDBID(iter)) {
+ for (DBIDIter iter = neighbors.iter(); iter.valid(); iter.advance()) {
+ if (DBIDUtil.equal(iditer, iter)) {
continue;
}
- fi[c] = relation.get(iter).doubleValue(1);
+ fi[c] = relation.get(iter).doubleValue(0);
c++;
}
- if(c > 0) {
+ if (c > 0) {
median = QuickSelect.median(fi, 0, c);
- }
- else {
- median = relation.get(id).doubleValue(1);
+ } else {
+ median = relation.get(iditer).doubleValue(0);
}
}
- double h = relation.get(id).doubleValue(1) - median;
- scores.putDouble(id, h);
+ double h = relation.get(iditer).doubleValue(0) - median;
+ scores.putDouble(iditer, h);
mv.put(h);
}
@@ -127,11 +126,10 @@ public class CTLuMedianAlgorithm<N> extends AbstractNeighborhoodOutlier<N> {
final double mean = mv.getMean();
final double stddev = mv.getNaiveStddev();
DoubleMinMax minmax = new DoubleMinMax();
- for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
- double score = Math.abs((scores.doubleValue(id) - mean) / stddev);
+ for (DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
+ double score = Math.abs((scores.doubleValue(iditer) - mean) / stddev);
minmax.put(score);
- scores.putDouble(id, score);
+ scores.putDouble(iditer, score);
}
Relation<Double> scoreResult = new MaterializedRelation<Double>("MO", "Median-outlier", TypeUtil.DOUBLE, scores, relation.getDBIDs());
@@ -143,16 +141,16 @@ public class CTLuMedianAlgorithm<N> extends AbstractNeighborhoodOutlier<N> {
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
@Override
public TypeInformation[] getInputTypeRestriction() {
- return TypeUtil.array(getNeighborSetPredicateFactory().getInputTypeRestriction(), VectorFieldTypeInformation.get(NumberVector.class, 1));
+ return TypeUtil.array(getNeighborSetPredicateFactory().getInputTypeRestriction(), new VectorFieldTypeInformation<NumberVector<?>>(NumberVector.class, 1));
}
/**
- * Parameterization class
+ * Parameterization class.
*
* @author Ahmed Hettab
*
@@ -166,4 +164,4 @@ public class CTLuMedianAlgorithm<N> extends AbstractNeighborhoodOutlier<N> {
return new CTLuMedianAlgorithm<N>(npredf);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/CTLuMedianMultipleAttributes.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/CTLuMedianMultipleAttributes.java
index c8bcba74..0d515ac7 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/CTLuMedianMultipleAttributes.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/CTLuMedianMultipleAttributes.java
@@ -31,11 +31,11 @@ import de.lmu.ifi.dbs.elki.database.datastore.DataStoreFactory;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreUtil;
import de.lmu.ifi.dbs.elki.database.datastore.WritableDataStore;
import de.lmu.ifi.dbs.elki.database.datastore.WritableDoubleDataStore;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.relation.MaterializedRelation;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.math.DoubleMinMax;
import de.lmu.ifi.dbs.elki.math.linearalgebra.CovarianceMatrix;
@@ -44,7 +44,6 @@ import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector;
import de.lmu.ifi.dbs.elki.result.outlier.BasicOutlierScoreMeta;
import de.lmu.ifi.dbs.elki.result.outlier.OutlierResult;
import de.lmu.ifi.dbs.elki.result.outlier.OutlierScoreMeta;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.utilities.datastructures.QuickSelect;
import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
@@ -73,11 +72,11 @@ import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
* @param <O> Non Spatial Vector
*/
@Reference(authors = "Chang-Tien Lu and Dechang Chen and Yufeng Kou", title = "Detecting Spatial Outliers with Multiple Attributes", booktitle = "Proc. 15th IEEE International Conference on Tools with Artificial Intelligence, 2003", url = "http://dx.doi.org/10.1109/TAI.2003.1250179")
-public class CTLuMedianMultipleAttributes<N, O extends NumberVector<?, ?>> extends AbstractNeighborhoodOutlier<N> {
+public class CTLuMedianMultipleAttributes<N, O extends NumberVector<?>> extends AbstractNeighborhoodOutlier<N> {
/**
* logger
*/
- public static final Logging logger = Logging.getLogger(CTLuMedianMultipleAttributes.class);
+ private static final Logging LOG = Logging.getLogger(CTLuMedianMultipleAttributes.class);
/**
* Constructor
@@ -90,7 +89,7 @@ public class CTLuMedianMultipleAttributes<N, O extends NumberVector<?, ?>> exten
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -101,18 +100,17 @@ public class CTLuMedianMultipleAttributes<N, O extends NumberVector<?, ?>> exten
* @return Outlier detection result
*/
public OutlierResult run(Relation<N> spatial, Relation<O> attributes) {
- final int dim = DatabaseUtil.dimensionality(attributes);
- if(logger.isDebugging()) {
- logger.debug("Dimensionality: " + dim);
+ final int dim = RelationUtil.dimensionality(attributes);
+ if(LOG.isDebugging()) {
+ LOG.debug("Dimensionality: " + dim);
}
final NeighborSetPredicate npred = getNeighborSetPredicateFactory().instantiate(spatial);
CovarianceMatrix covmaker = new CovarianceMatrix(dim);
WritableDataStore<Vector> deltas = DataStoreUtil.makeStorage(attributes.getDBIDs(), DataStoreFactory.HINT_TEMP, Vector.class);
for(DBIDIter iditer = attributes.iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
- final O obj = attributes.get(id);
- final DBIDs neighbors = npred.getNeighborDBIDs(id);
+ final O obj = attributes.get(iditer);
+ final DBIDs neighbors = npred.getNeighborDBIDs(iditer);
// Compute the median vector
final Vector median;
{
@@ -123,7 +121,7 @@ public class CTLuMedianMultipleAttributes<N, O extends NumberVector<?, ?>> exten
// TODO: skip object itself within neighbors?
O nobj = attributes.get(iter);
for(int d = 0; d < dim; d++) {
- data[d][i] = nobj.doubleValue(d + 1);
+ data[d][i] = nobj.doubleValue(d);
}
i++;
}
@@ -135,8 +133,8 @@ public class CTLuMedianMultipleAttributes<N, O extends NumberVector<?, ?>> exten
}
// Delta vector "h"
- Vector delta = obj.getColumnVector().minus(median);
- deltas.put(id, delta);
+ Vector delta = obj.getColumnVector().minusEquals(median);
+ deltas.put(iditer, delta);
covmaker.put(delta);
}
// Finalize covariance matrix:
@@ -146,11 +144,10 @@ public class CTLuMedianMultipleAttributes<N, O extends NumberVector<?, ?>> exten
DoubleMinMax minmax = new DoubleMinMax();
WritableDoubleDataStore scores = DataStoreUtil.makeDoubleStorage(attributes.getDBIDs(), DataStoreFactory.HINT_STATIC);
for(DBIDIter iditer = attributes.iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
- Vector temp = deltas.get(id).minus(mean);
+ Vector temp = deltas.get(iditer).minus(mean);
final double score = temp.transposeTimesTimes(cmati, temp);
minmax.put(score);
- scores.putDouble(id, score);
+ scores.putDouble(iditer, score);
}
Relation<Double> scoreResult = new MaterializedRelation<Double>("Median multiple attributes outlier", "median-outlier", TypeUtil.DOUBLE, scores, attributes.getDBIDs());
@@ -175,7 +172,7 @@ public class CTLuMedianMultipleAttributes<N, O extends NumberVector<?, ?>> exten
* @param <N> Neighborhood type
* @param <O> Attributes vector type
*/
- public static class Parameterizer<N, O extends NumberVector<?, ?>> extends AbstractNeighborhoodOutlier.Parameterizer<N> {
+ public static class Parameterizer<N, O extends NumberVector<?>> extends AbstractNeighborhoodOutlier.Parameterizer<N> {
@Override
protected CTLuMedianMultipleAttributes<N, O> makeInstance() {
return new CTLuMedianMultipleAttributes<N, O>(npredf);
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/CTLuMoranScatterplotOutlier.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/CTLuMoranScatterplotOutlier.java
index 7b88ae66..3b876bba 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/CTLuMoranScatterplotOutlier.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/CTLuMoranScatterplotOutlier.java
@@ -32,8 +32,8 @@ import de.lmu.ifi.dbs.elki.data.type.VectorFieldTypeInformation;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreFactory;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreUtil;
import de.lmu.ifi.dbs.elki.database.datastore.WritableDoubleDataStore;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.relation.MaterializedRelation;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.logging.Logging;
@@ -76,10 +76,10 @@ public class CTLuMoranScatterplotOutlier<N> extends AbstractNeighborhoodOutlier<
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(CTLuMoranScatterplotOutlier.class);
+ private static final Logging LOG = Logging.getLogger(CTLuMoranScatterplotOutlier.class);
/**
- * Constructor
+ * Constructor.
*
* @param npredf Neighborhood
*/
@@ -88,20 +88,19 @@ public class CTLuMoranScatterplotOutlier<N> extends AbstractNeighborhoodOutlier<
}
/**
- * Main method
+ * Main method.
*
* @param nrel Neighborhood relation
* @param relation Data relation (1d!)
* @return Outlier detection result
*/
- public OutlierResult run(Relation<N> nrel, Relation<? extends NumberVector<?, ?>> relation) {
+ public OutlierResult run(Relation<N> nrel, Relation<? extends NumberVector<?>> relation) {
final NeighborSetPredicate npred = getNeighborSetPredicateFactory().instantiate(nrel);
// Compute the global mean and variance
MeanVariance globalmv = new MeanVariance();
for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
- globalmv.put(relation.get(id).doubleValue(1));
+ globalmv.put(relation.get(iditer).doubleValue(0));
}
DoubleMinMax minmax = new DoubleMinMax();
@@ -110,17 +109,15 @@ public class CTLuMoranScatterplotOutlier<N> extends AbstractNeighborhoodOutlier<
// calculate normalized attribute values
// calculate neighborhood average of normalized attribute values.
for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
// Compute global z score
- final double globalZ = (relation.get(id).doubleValue(1) - globalmv.getMean()) / globalmv.getNaiveStddev();
+ final double globalZ = (relation.get(iditer).doubleValue(0) - globalmv.getMean()) / globalmv.getNaiveStddev();
// Compute local average z score
Mean localm = new Mean();
- for(DBIDIter iter = npred.getNeighborDBIDs(id).iter(); iter.valid(); iter.advance()) {
- DBID n = iter.getDBID();
- if(id.equals(n)) {
+ for(DBIDIter iter = npred.getNeighborDBIDs(iditer).iter(); iter.valid(); iter.advance()) {
+ if(DBIDUtil.equal(iditer, iter)) {
continue;
}
- localm.put((relation.get(n).doubleValue(1) - globalmv.getMean()) / globalmv.getNaiveStddev());
+ localm.put((relation.get(iter).doubleValue(0) - globalmv.getMean()) / globalmv.getNaiveStddev());
}
// if neighors.size == 0
final double localZ;
@@ -136,7 +133,7 @@ public class CTLuMoranScatterplotOutlier<N> extends AbstractNeighborhoodOutlier<
// Note: in the original moran scatterplot, any object with a score < 0 would be an outlier.
final double score = Math.max(-globalZ * localZ, 0);
minmax.put(score);
- scores.putDouble(id, score);
+ scores.putDouble(iditer, score);
}
Relation<Double> scoreResult = new MaterializedRelation<Double>("MoranOutlier", "Moran Scatterplot Outlier", TypeUtil.DOUBLE, scores, relation.getDBIDs());
@@ -148,16 +145,16 @@ public class CTLuMoranScatterplotOutlier<N> extends AbstractNeighborhoodOutlier<
@Override
public TypeInformation[] getInputTypeRestriction() {
- return TypeUtil.array(getNeighborSetPredicateFactory().getInputTypeRestriction(), VectorFieldTypeInformation.get(NumberVector.class, 1));
+ return TypeUtil.array(getNeighborSetPredicateFactory().getInputTypeRestriction(), new VectorFieldTypeInformation<NumberVector<?>>(NumberVector.class, 1));
}
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
- * Parameterization class
+ * Parameterization class.
*
* @author Ahmed Hettab
*
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/CTLuRandomWalkEC.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/CTLuRandomWalkEC.java
index 852c4be4..ec92afd7 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/CTLuRandomWalkEC.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/CTLuRandomWalkEC.java
@@ -1,26 +1,27 @@
package de.lmu.ifi.dbs.elki.algorithm.outlier.spatial;
-/*
-This file is part of ELKI:
-Environment for Developing KDD-Applications Supported by Index-Structures
-
-Copyright (C) 2012
-Ludwig-Maximilians-Universität München
-Lehr- und Forschungseinheit für Datenbanksysteme
-ELKI Development Team
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
import de.lmu.ifi.dbs.elki.algorithm.AbstractDistanceBasedAlgorithm;
import de.lmu.ifi.dbs.elki.algorithm.outlier.OutlierAlgorithm;
@@ -33,7 +34,6 @@ import de.lmu.ifi.dbs.elki.database.datastore.DataStoreUtil;
import de.lmu.ifi.dbs.elki.database.datastore.WritableDataStore;
import de.lmu.ifi.dbs.elki.database.datastore.WritableDoubleDataStore;
import de.lmu.ifi.dbs.elki.database.ids.ArrayDBIDs;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
@@ -42,6 +42,8 @@ import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.database.relation.MaterializedRelation;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNHeap;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNUtil;
import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.math.DoubleMinMax;
@@ -51,7 +53,6 @@ import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector;
import de.lmu.ifi.dbs.elki.result.outlier.BasicOutlierScoreMeta;
import de.lmu.ifi.dbs.elki.result.outlier.OutlierResult;
import de.lmu.ifi.dbs.elki.result.outlier.OutlierScoreMeta;
-import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.KNNHeap;
import de.lmu.ifi.dbs.elki.utilities.documentation.Description;
import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
import de.lmu.ifi.dbs.elki.utilities.documentation.Title;
@@ -82,30 +83,30 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
*/
@Title("Random Walk on Exhaustive Combination")
@Description("Spatial Outlier Detection using Random Walk on Exhaustive Combination")
-@Reference(authors = "X. Liu and C.-T. Lu and F. Chen", title = "Spatial outlier detection: random walk based approaches", booktitle = "Proc. 18th SIGSPATIAL International Conference on Advances in Geographic Information Systems, 2010", url="http://dx.doi.org/10.1145/1869790.1869841")
+@Reference(authors = "X. Liu and C.-T. Lu and F. Chen", title = "Spatial outlier detection: random walk based approaches", booktitle = "Proc. 18th SIGSPATIAL International Conference on Advances in Geographic Information Systems, 2010", url = "http://dx.doi.org/10.1145/1869790.1869841")
public class CTLuRandomWalkEC<N, D extends NumberDistance<D, ?>> extends AbstractDistanceBasedAlgorithm<N, D, OutlierResult> implements OutlierAlgorithm {
/**
- * Logger
+ * Logger.
*/
- private static final Logging logger = Logging.getLogger(CTLuRandomWalkEC.class);
+ private static final Logging LOG = Logging.getLogger(CTLuRandomWalkEC.class);
/**
- * Parameter alpha: Attribute difference exponent
+ * Parameter alpha: Attribute difference exponent.
*/
private double alpha;
/**
- * Parameter c: damping factor
+ * Parameter c: damping factor.
*/
private double c;
/**
- * Parameter k
+ * Parameter k.
*/
private int k;
/**
- * Constructor
+ * Constructor.
*
* @param distanceFunction Distance function
* @param alpha Alpha parameter
@@ -120,13 +121,13 @@ public class CTLuRandomWalkEC<N, D extends NumberDistance<D, ?>> extends Abstrac
}
/**
- * Run the algorithm
+ * Run the algorithm.
*
* @param spatial Spatial neighborhood relation
* @param relation Attribute value relation
* @return Outlier result
*/
- public OutlierResult run(Relation<N> spatial, Relation<? extends NumberVector<?, ?>> relation) {
+ public OutlierResult run(Relation<N> spatial, Relation<? extends NumberVector<?>> relation) {
DistanceQuery<N, D> distFunc = getDistanceFunction().instantiate(spatial);
WritableDataStore<Vector> similarityVectors = DataStoreUtil.makeStorage(spatial.getDBIDs(), DataStoreFactory.HINT_TEMP, Vector.class);
WritableDataStore<DBIDs> neighbors = DataStoreUtil.makeStorage(spatial.getDBIDs(), DataStoreFactory.HINT_TEMP, DBIDs.class);
@@ -136,39 +137,41 @@ public class CTLuRandomWalkEC<N, D extends NumberDistance<D, ?>> extends Abstrac
// construct the relation Matrix of the ec-graph
Matrix E = new Matrix(ids.size(), ids.size());
- KNNHeap<D> heap = new KNNHeap<D>(k);
- for(int i = 0; i < ids.size(); i++) {
- final DBID id = ids.get(i);
- final double val = relation.get(id).doubleValue(1);
- assert (heap.size() == 0);
- for(int j = 0; j < ids.size(); j++) {
- if(i == j) {
- continue;
- }
- final DBID n = ids.get(j);
- final double e;
- final D distance = distFunc.distance(id, n);
- heap.add(distance, n);
- double dist = distance.doubleValue();
- if(dist == 0) {
- logger.warning("Zero distances are not supported - skipping: " + id + " " + n);
- e = 0;
+ KNNHeap<D> heap = KNNUtil.newHeap(distFunc.getDistanceFactory(), k);
+ {
+ int i = 0;
+ for(DBIDIter id = ids.iter(); id.valid(); id.advance(), i++) {
+ final double val = relation.get(id).doubleValue(0);
+ assert (heap.size() == 0);
+ int j = 0;
+ for(DBIDIter n = ids.iter(); n.valid(); n.advance(), j++) {
+ if(i == j) {
+ continue;
+ }
+ final double e;
+ final D distance = distFunc.distance(id, n);
+ heap.add(distance, n);
+ double dist = distance.doubleValue();
+ if(dist == 0) {
+ LOG.warning("Zero distances are not supported - skipping: " + DBIDUtil.toString(id) + " " + DBIDUtil.toString(n));
+ e = 0;
+ }
+ else {
+ double diff = Math.abs(val - relation.get(n).doubleValue(0));
+ double exp = Math.exp(Math.pow(diff, alpha));
+ // Implementation note: not inverting exp worked a lot better.
+ // Therefore we diverge from the article here.
+ e = exp / dist;
+ }
+ E.set(j, i, e);
}
- else {
- double diff = Math.abs(val - relation.get(n).doubleValue(1));
- double exp = Math.exp(Math.pow(diff, alpha));
- // Implementation note: not inverting exp worked a lot better.
- // Therefore we diverge from the article here.
- e = exp / dist;
+ // Convert kNN Heap into DBID array
+ ModifiableDBIDs nids = DBIDUtil.newArray(heap.size());
+ while(heap.size() > 0) {
+ nids.add(heap.poll());
}
- E.set(j, i, e);
- }
- // Convert kNN Heap into DBID array
- ModifiableDBIDs nids = DBIDUtil.newArray(heap.size());
- while(!heap.isEmpty()) {
- nids.add(heap.poll().getDBID());
+ neighbors.put(id, nids);
}
- neighbors.put(id, nids);
}
// normalize the adjacent Matrix
// Sum based normalization - don't use E.normalizeColumns()
@@ -195,26 +198,26 @@ public class CTLuRandomWalkEC<N, D extends NumberDistance<D, ?>> extends Abstrac
E = E.inverse().timesEquals(1 - c);
// Split the matrix into columns
- for(int i = 0; i < ids.size(); i++) {
- DBID id = ids.get(i);
- // Note: matrix times ith unit vector = ith column
- Vector sim = E.getCol(i);
- similarityVectors.put(id, sim);
+ {
+ int i = 0;
+ for(DBIDIter id = ids.iter(); id.valid(); id.advance(), i++) {
+ // Note: matrix times ith unit vector = ith column
+ Vector sim = E.getCol(i);
+ similarityVectors.put(id, sim);
+ }
}
E = null;
// compute the relevance scores between specified Object and its neighbors
DoubleMinMax minmax = new DoubleMinMax();
WritableDoubleDataStore scores = DataStoreUtil.makeDoubleStorage(spatial.getDBIDs(), DataStoreFactory.HINT_STATIC);
- for(int i = 0; i < ids.size(); i++) {
- DBID id = ids.get(i);
+ for(DBIDIter id = ids.iter(); id.valid(); id.advance()) {
double gmean = 1.0;
int cnt = 0;
for(DBIDIter iter = neighbors.get(id).iter(); iter.valid(); iter.advance()) {
- DBID n = iter.getDBID();
- if(id.equals(n)) {
+ if(DBIDUtil.equal(id, iter)) {
continue;
}
- double sim = MathUtil.angle(similarityVectors.get(id), similarityVectors.get(n));
+ double sim = MathUtil.angle(similarityVectors.get(id), similarityVectors.get(iter));
gmean *= sim;
cnt++;
}
@@ -230,12 +233,12 @@ public class CTLuRandomWalkEC<N, D extends NumberDistance<D, ?>> extends Abstrac
@Override
public TypeInformation[] getInputTypeRestriction() {
- return TypeUtil.array(getDistanceFunction().getInputTypeRestriction(), VectorFieldTypeInformation.get(NumberVector.class, 1));
+ return TypeUtil.array(getDistanceFunction().getInputTypeRestriction(), new VectorFieldTypeInformation<NumberVector<?>>(NumberVector.class, 1));
}
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -250,32 +253,32 @@ public class CTLuRandomWalkEC<N, D extends NumberDistance<D, ?>> extends Abstrac
*/
public static class Parameterizer<N, D extends NumberDistance<D, ?>> extends AbstractDistanceBasedAlgorithm.Parameterizer<N, D> {
/**
- * Parameter to specify the number of neighbors
+ * Parameter to specify the number of neighbors.
*/
- public static final OptionID K_ID = OptionID.getOrCreateOptionID("randomwalkec.k", "Number of nearest neighbors to use.");
+ public static final OptionID K_ID = new OptionID("randomwalkec.k", "Number of nearest neighbors to use.");
/**
- * Parameter to specify alpha
+ * Parameter to specify alpha.
*/
- public static final OptionID ALPHA_ID = OptionID.getOrCreateOptionID("randomwalkec.alpha", "Scaling exponent for value differences.");
+ public static final OptionID ALPHA_ID = new OptionID("randomwalkec.alpha", "Scaling exponent for value differences.");
/**
- * Parameter to specify the c
+ * Parameter to specify the c.
*/
- public static final OptionID C_ID = OptionID.getOrCreateOptionID("randomwalkec.c", "The damping parameter c.");
+ public static final OptionID C_ID = new OptionID("randomwalkec.c", "The damping parameter c.");
/**
- * Parameter alpha: scaling
+ * Parameter alpha: scaling.
*/
double alpha = 0.5;
/**
- * Parameter c: damping coefficient
+ * Parameter c: damping coefficient.
*/
double c = 0.9;
/**
- * Parameter for kNN
+ * Parameter for kNN.
*/
int k;
@@ -288,19 +291,20 @@ public class CTLuRandomWalkEC<N, D extends NumberDistance<D, ?>> extends Abstrac
}
/**
- * Get the kNN parameter
+ * Get the kNN parameter.
*
* @param config Parameterization
*/
protected void configK(Parameterization config) {
- final IntParameter param = new IntParameter(K_ID, new GreaterEqualConstraint(1));
+ final IntParameter param = new IntParameter(K_ID);
+ param.addConstraint(new GreaterEqualConstraint(1));
if(config.grab(param)) {
k = param.getValue();
}
}
/**
- * Get the alpha parameter
+ * Get the alpha parameter.
*
* @param config Parameterization
*/
@@ -312,9 +316,9 @@ public class CTLuRandomWalkEC<N, D extends NumberDistance<D, ?>> extends Abstrac
}
/**
- * get the c parameter
+ * get the c parameter.
*
- * @param config
+ * @param config Parameterization
*/
protected void configC(Parameterization config) {
final DoubleParameter param = new DoubleParameter(C_ID);
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/CTLuScatterplotOutlier.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/CTLuScatterplotOutlier.java
index 4f11cb38..295c7414 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/CTLuScatterplotOutlier.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/CTLuScatterplotOutlier.java
@@ -31,8 +31,8 @@ import de.lmu.ifi.dbs.elki.data.type.VectorFieldTypeInformation;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreFactory;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreUtil;
import de.lmu.ifi.dbs.elki.database.datastore.WritableDoubleDataStore;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.relation.MaterializedRelation;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
@@ -78,10 +78,10 @@ public class CTLuScatterplotOutlier<N> extends AbstractNeighborhoodOutlier<N> {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(CTLuScatterplotOutlier.class);
+ private static final Logging LOG = Logging.getLogger(CTLuScatterplotOutlier.class);
/**
- * Constructor
+ * Constructor.
*
* @param npredf Neighborhood predicate
*/
@@ -90,13 +90,13 @@ public class CTLuScatterplotOutlier<N> extends AbstractNeighborhoodOutlier<N> {
}
/**
- * Main method
+ * Main method.
*
* @param nrel Neighborhood relation
* @param relation Data relation (1d!)
* @return Outlier detection result
*/
- public OutlierResult run(Relation<N> nrel, Relation<? extends NumberVector<?, ?>> relation) {
+ public OutlierResult run(Relation<N> nrel, Relation<? extends NumberVector<?>> relation) {
final NeighborSetPredicate npred = getNeighborSetPredicateFactory().instantiate(nrel);
WritableDoubleDataStore means = DataStoreUtil.makeDoubleStorage(relation.getDBIDs(), DataStoreFactory.HINT_TEMP);
@@ -104,17 +104,15 @@ public class CTLuScatterplotOutlier<N> extends AbstractNeighborhoodOutlier<N> {
// regression using the covariance matrix
CovarianceMatrix covm = new CovarianceMatrix(2);
for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
- final double local = relation.get(id).doubleValue(1);
+ final double local = relation.get(iditer).doubleValue(0);
// Compute mean of neighbors
Mean mean = new Mean();
- DBIDs neighbors = npred.getNeighborDBIDs(id);
+ DBIDs neighbors = npred.getNeighborDBIDs(iditer);
for(DBIDIter iter = neighbors.iter(); iter.valid(); iter.advance()) {
- DBID n = iter.getDBID();
- if(id.equals(n)) {
+ if(DBIDUtil.equal(iditer, iter)) {
continue;
}
- mean.put(relation.get(n).doubleValue(1));
+ mean.put(relation.get(iter).doubleValue(0));
}
final double m;
if(mean.getCount() > 0) {
@@ -125,7 +123,7 @@ public class CTLuScatterplotOutlier<N> extends AbstractNeighborhoodOutlier<N> {
m = local;
}
// Store the mean for the score calculation
- means.putDouble(id, m);
+ means.putDouble(iditer, m);
covm.put(new double[] { local, m });
}
// Finalize covariance matrix, compute linear regression
@@ -143,11 +141,10 @@ public class CTLuScatterplotOutlier<N> extends AbstractNeighborhoodOutlier<N> {
WritableDoubleDataStore scores = DataStoreUtil.makeDoubleStorage(relation.getDBIDs(), DataStoreFactory.HINT_STATIC);
MeanVariance mv = new MeanVariance();
for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
// Compute the error from the linear regression
- double y_i = relation.get(id).doubleValue(1);
- double e = means.doubleValue(id) - (slope * y_i + inter);
- scores.putDouble(id, e);
+ double y_i = relation.get(iditer).doubleValue(0);
+ double e = means.doubleValue(iditer) - (slope * y_i + inter);
+ scores.putDouble(iditer, e);
mv.put(e);
}
@@ -157,10 +154,9 @@ public class CTLuScatterplotOutlier<N> extends AbstractNeighborhoodOutlier<N> {
final double mean = mv.getMean();
final double variance = mv.getNaiveStddev();
for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
- double score = Math.abs((scores.doubleValue(id) - mean) / variance);
+ double score = Math.abs((scores.doubleValue(iditer) - mean) / variance);
minmax.put(score);
- scores.putDouble(id, score);
+ scores.putDouble(iditer, score);
}
}
// build representation
@@ -173,16 +169,16 @@ public class CTLuScatterplotOutlier<N> extends AbstractNeighborhoodOutlier<N> {
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
@Override
public TypeInformation[] getInputTypeRestriction() {
- return TypeUtil.array(getNeighborSetPredicateFactory().getInputTypeRestriction(), VectorFieldTypeInformation.get(NumberVector.class, 1));
+ return TypeUtil.array(getNeighborSetPredicateFactory().getInputTypeRestriction(), new VectorFieldTypeInformation<NumberVector<?>>(NumberVector.class, 1));
}
/**
- * Parameterization class
+ * Parameterization class.
*
* @author Ahmed Hettab
*
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/CTLuZTestOutlier.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/CTLuZTestOutlier.java
index 05729481..02573a06 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/CTLuZTestOutlier.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/CTLuZTestOutlier.java
@@ -32,8 +32,8 @@ import de.lmu.ifi.dbs.elki.database.Database;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreFactory;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreUtil;
import de.lmu.ifi.dbs.elki.database.datastore.WritableDoubleDataStore;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.relation.MaterializedRelation;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
@@ -79,60 +79,57 @@ public class CTLuZTestOutlier<N> extends AbstractNeighborhoodOutlier<N> {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(CTLuZTestOutlier.class);
+ private static final Logging LOG = Logging.getLogger(CTLuZTestOutlier.class);
/**
- * Constructor
+ * Constructor.
*
- * @param npredf
+ * @param npredf Neighbor predicate
*/
public CTLuZTestOutlier(NeighborSetPredicate.Factory<N> npredf) {
super(npredf);
}
/**
- * Main method
+ * Main method.
*
* @param database Database
* @param nrel Neighborhood relation
* @param relation Data relation (1d!)
* @return Outlier detection result
*/
- public OutlierResult run(Database database, Relation<N> nrel, Relation<? extends NumberVector<?, ?>> relation) {
+ public OutlierResult run(Database database, Relation<N> nrel, Relation<? extends NumberVector<?>> relation) {
final NeighborSetPredicate npred = getNeighborSetPredicateFactory().instantiate(nrel);
WritableDoubleDataStore scores = DataStoreUtil.makeDoubleStorage(relation.getDBIDs(), DataStoreFactory.HINT_STATIC);
MeanVariance zmv = new MeanVariance();
for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
- DBIDs neighbors = npred.getNeighborDBIDs(id);
+ DBIDs neighbors = npred.getNeighborDBIDs(iditer);
// Compute Mean of neighborhood
Mean localmean = new Mean();
for(DBIDIter iter = neighbors.iter(); iter.valid(); iter.advance()) {
- DBID n = iter.getDBID();
- if(id.equals(n)) {
+ if(DBIDUtil.equal(iditer, iter)) {
continue;
}
- localmean.put(relation.get(n).doubleValue(1));
+ localmean.put(relation.get(iter).doubleValue(0));
}
final double localdiff;
if(localmean.getCount() > 0) {
- localdiff = relation.get(id).doubleValue(1) - localmean.getMean();
+ localdiff = relation.get(iditer).doubleValue(0) - localmean.getMean();
}
else {
localdiff = 0.0;
}
- scores.putDouble(id, localdiff);
+ scores.putDouble(iditer, localdiff);
zmv.put(localdiff);
}
// Normalize scores using mean and variance
DoubleMinMax minmax = new DoubleMinMax();
for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
- double score = Math.abs(scores.doubleValue(id) - zmv.getMean()) / zmv.getSampleStddev();
+ double score = Math.abs(scores.doubleValue(iditer) - zmv.getMean()) / zmv.getSampleStddev();
minmax.put(score);
- scores.putDouble(id, score);
+ scores.putDouble(iditer, score);
}
// Wrap result
@@ -145,16 +142,16 @@ public class CTLuZTestOutlier<N> extends AbstractNeighborhoodOutlier<N> {
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
@Override
public TypeInformation[] getInputTypeRestriction() {
- return TypeUtil.array(getNeighborSetPredicateFactory().getInputTypeRestriction(), VectorFieldTypeInformation.get(NumberVector.class, 1));
+ return TypeUtil.array(getNeighborSetPredicateFactory().getInputTypeRestriction(), new VectorFieldTypeInformation<NumberVector<?>>(NumberVector.class, 1));
}
/**
- * Parameterization class
+ * Parameterization class.
*
* @author Ahmed Hettab
*
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/SLOM.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/SLOM.java
index 8ae23229..720fa39f 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/SLOM.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/SLOM.java
@@ -30,8 +30,8 @@ import de.lmu.ifi.dbs.elki.database.Database;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreFactory;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreUtil;
import de.lmu.ifi.dbs.elki.database.datastore.WritableDoubleDataStore;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.database.relation.MaterializedRelation;
@@ -74,7 +74,7 @@ public class SLOM<N, O, D extends NumberDistance<D, ?>> extends AbstractDistance
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(SLOM.class);
+ private static final Logging LOG = Logging.getLogger(SLOM.class);
/**
* Constructor.
@@ -100,29 +100,27 @@ public class SLOM<N, O, D extends NumberDistance<D, ?>> extends AbstractDistance
WritableDoubleDataStore modifiedDistance = DataStoreUtil.makeDoubleStorage(relation.getDBIDs(), DataStoreFactory.HINT_HOT | DataStoreFactory.HINT_TEMP);
// calculate D-Tilde
for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
double sum = 0;
double maxDist = 0;
int cnt = 0;
- final DBIDs neighbors = npred.getNeighborDBIDs(id);
+ final DBIDs neighbors = npred.getNeighborDBIDs(iditer);
for(DBIDIter iter = neighbors.iter(); iter.valid(); iter.advance()) {
- DBID neighbor = iter.getDBID();
- if(id.equals(neighbor)) {
+ if(DBIDUtil.equal(iditer, iter)) {
continue;
}
- double dist = distFunc.distance(id, neighbor).doubleValue();
+ double dist = distFunc.distance(iditer, iter).doubleValue();
sum += dist;
cnt++;
maxDist = Math.max(maxDist, dist);
}
if(cnt > 1) {
- modifiedDistance.putDouble(id, ((sum - maxDist) / (cnt - 1)));
+ modifiedDistance.putDouble(iditer, ((sum - maxDist) / (cnt - 1)));
}
else {
// Use regular distance when the d-tilde trick is undefined.
// Note: this can be 0 when there were no neighbors.
- modifiedDistance.putDouble(id, maxDist);
+ modifiedDistance.putDouble(iditer, maxDist);
}
}
@@ -131,29 +129,26 @@ public class SLOM<N, O, D extends NumberDistance<D, ?>> extends AbstractDistance
WritableDoubleDataStore sloms = DataStoreUtil.makeDoubleStorage(relation.getDBIDs(), DataStoreFactory.HINT_STATIC);
for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
double sum = 0;
int cnt = 0;
- final DBIDs neighbors = npred.getNeighborDBIDs(id);
+ final DBIDs neighbors = npred.getNeighborDBIDs(iditer);
for(DBIDIter iter = neighbors.iter(); iter.valid(); iter.advance()) {
- DBID neighbor = iter.getDBID();
- if(neighbor.equals(id)) {
+ if(DBIDUtil.equal(iditer, iter)) {
continue;
}
- sum += modifiedDistance.doubleValue(neighbor);
+ sum += modifiedDistance.doubleValue(iter);
cnt++;
}
double slom;
if(cnt > 0) {
// With and without the object itself:
- double avgPlus = (sum + modifiedDistance.doubleValue(id)) / (cnt + 1);
+ double avgPlus = (sum + modifiedDistance.doubleValue(iditer)) / (cnt + 1);
double avg = sum / cnt;
double beta = 0;
for(DBIDIter iter = neighbors.iter(); iter.valid(); iter.advance()) {
- DBID neighbor = iter.getDBID();
- final double dist = modifiedDistance.doubleValue(neighbor);
+ final double dist = modifiedDistance.doubleValue(iter);
if(dist > avgPlus) {
beta += 1;
}
@@ -162,8 +157,8 @@ public class SLOM<N, O, D extends NumberDistance<D, ?>> extends AbstractDistance
}
}
// Include object itself
- if(!neighbors.contains(id)) {
- final double dist = modifiedDistance.doubleValue(id);
+ if(!neighbors.contains(iditer)) {
+ final double dist = modifiedDistance.doubleValue(iditer);
if(dist > avgPlus) {
beta += 1;
}
@@ -182,13 +177,13 @@ public class SLOM<N, O, D extends NumberDistance<D, ?>> extends AbstractDistance
}
beta = beta / (1 + avg);
- slom = beta * modifiedDistance.doubleValue(id);
+ slom = beta * modifiedDistance.doubleValue(iditer);
}
else {
// No neighbors to compare to - no score.
slom = 0.0;
}
- sloms.putDouble(id, slom);
+ sloms.putDouble(iditer, slom);
slomminmax.put(slom);
}
@@ -201,7 +196,7 @@ public class SLOM<N, O, D extends NumberDistance<D, ?>> extends AbstractDistance
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
@Override
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/SOF.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/SOF.java
index e9987bf0..a6f39a60 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/SOF.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/SOF.java
@@ -29,7 +29,6 @@ import de.lmu.ifi.dbs.elki.database.Database;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreFactory;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreUtil;
import de.lmu.ifi.dbs.elki.database.datastore.WritableDoubleDataStore;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
@@ -74,7 +73,7 @@ public class SOF<N, O, D extends NumberDistance<D, ?>> extends AbstractDistanceB
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(SOF.class);
+ private static final Logging LOG = Logging.getLogger(SOF.class);
/**
* Constructor.
@@ -89,7 +88,7 @@ public class SOF<N, O, D extends NumberDistance<D, ?>> extends AbstractDistanceB
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -110,33 +109,31 @@ public class SOF<N, O, D extends NumberDistance<D, ?>> extends AbstractDistanceB
// Compute densities
for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
- DBIDs neighbors = npred.getNeighborDBIDs(id);
+ DBIDs neighbors = npred.getNeighborDBIDs(iditer);
double avg = 0;
for(DBIDIter iter = neighbors.iter(); iter.valid(); iter.advance()) {
- avg += distFunc.distance(id, iter.getDBID()).doubleValue();
+ avg += distFunc.distance(iditer, iter).doubleValue();
}
double lrd = 1 / (avg / neighbors.size());
if (Double.isNaN(lrd)) {
lrd = 0;
}
- lrds.putDouble(id, lrd);
+ lrds.putDouble(iditer, lrd);
}
// Compute density quotients
for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
- DBIDs neighbors = npred.getNeighborDBIDs(id);
+ DBIDs neighbors = npred.getNeighborDBIDs(iditer);
double avg = 0;
for(DBIDIter iter = neighbors.iter(); iter.valid(); iter.advance()) {
- avg += lrds.doubleValue(iter.getDBID());
+ avg += lrds.doubleValue(iter);
}
- final double lrd = (avg / neighbors.size()) / lrds.doubleValue(id);
+ final double lrd = (avg / neighbors.size()) / lrds.doubleValue(iditer);
if (!Double.isNaN(lrd)) {
- lofs.putDouble(id, lrd);
+ lofs.putDouble(iditer, lrd);
lofminmax.put(lrd);
} else {
- lofs.putDouble(id, 0.0);
+ lofs.putDouble(iditer, 0.0);
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/TrimmedMeanApproach.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/TrimmedMeanApproach.java
index 41022414..9aa21b66 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/TrimmedMeanApproach.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/TrimmedMeanApproach.java
@@ -33,11 +33,11 @@ import de.lmu.ifi.dbs.elki.database.Database;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreFactory;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreUtil;
import de.lmu.ifi.dbs.elki.database.datastore.WritableDoubleDataStore;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.relation.MaterializedRelation;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.logging.progress.FiniteProgress;
import de.lmu.ifi.dbs.elki.math.DoubleMinMax;
@@ -45,14 +45,13 @@ import de.lmu.ifi.dbs.elki.math.Mean;
import de.lmu.ifi.dbs.elki.result.outlier.BasicOutlierScoreMeta;
import de.lmu.ifi.dbs.elki.result.outlier.OutlierResult;
import de.lmu.ifi.dbs.elki.result.outlier.OutlierScoreMeta;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.utilities.datastructures.QuickSelect;
import de.lmu.ifi.dbs.elki.utilities.documentation.Description;
import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
import de.lmu.ifi.dbs.elki.utilities.documentation.Title;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.IntervalConstraint;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.IntervalConstraint.IntervalBoundary;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.LessConstraint;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.DoubleParameter;
@@ -83,15 +82,15 @@ public class TrimmedMeanApproach<N> extends AbstractNeighborhoodOutlier<N> {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(TrimmedMeanApproach.class);
+ private static final Logging LOG = Logging.getLogger(TrimmedMeanApproach.class);
/**
- * the parameter p
+ * the parameter p.
*/
private double p;
/**
- * Constructor
+ * Constructor.
*
* @param p Parameter p
* @param npredf Neighborhood factory.
@@ -102,29 +101,28 @@ public class TrimmedMeanApproach<N> extends AbstractNeighborhoodOutlier<N> {
}
/**
- * Run the algorithm
+ * Run the algorithm.
*
* @param database Database
* @param nrel Neighborhood relation
* @param relation Data Relation (1 dimensional!)
* @return Outlier detection result
*/
- public OutlierResult run(Database database, Relation<N> nrel, Relation<? extends NumberVector<?, ?>> relation) {
- assert (DatabaseUtil.dimensionality(relation) == 1) : "TrimmedMean can only process one-dimensional data sets.";
+ public OutlierResult run(Database database, Relation<N> nrel, Relation<? extends NumberVector<?>> relation) {
+ assert (RelationUtil.dimensionality(relation) == 1) : "TrimmedMean can only process one-dimensional data sets.";
final NeighborSetPredicate npred = getNeighborSetPredicateFactory().instantiate(nrel);
WritableDoubleDataStore errors = DataStoreUtil.makeDoubleStorage(relation.getDBIDs(), DataStoreFactory.HINT_TEMP);
WritableDoubleDataStore scores = DataStoreUtil.makeDoubleStorage(relation.getDBIDs(), DataStoreFactory.HINT_STATIC);
- FiniteProgress progress = logger.isVerbose() ? new FiniteProgress("Computing trimmed means", relation.size(), logger) : null;
+ FiniteProgress progress = LOG.isVerbose() ? new FiniteProgress("Computing trimmed means", relation.size(), LOG) : null;
for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
- DBIDs neighbors = npred.getNeighborDBIDs(id);
+ DBIDs neighbors = npred.getNeighborDBIDs(iditer);
int num = 0;
double[] values = new double[neighbors.size()];
// calculate trimmedMean
for(DBIDIter iter = neighbors.iter(); iter.valid(); iter.advance()) {
- values[num] = relation.get(iter).doubleValue(1);
+ values[num] = relation.get(iter).doubleValue(0);
num++;
}
@@ -141,21 +139,21 @@ public class TrimmedMeanApproach<N> extends AbstractNeighborhoodOutlier<N> {
tm = mean.getMean();
}
else {
- tm = relation.get(id).doubleValue(1);
+ tm = relation.get(iditer).doubleValue(0);
}
// Error: deviation from trimmed mean
- errors.putDouble(id, relation.get(id).doubleValue(1) - tm);
+ errors.putDouble(iditer, relation.get(iditer).doubleValue(0) - tm);
if(progress != null) {
- progress.incrementProcessed(logger);
+ progress.incrementProcessed(LOG);
}
}
if(progress != null) {
- progress.ensureCompleted(logger);
+ progress.ensureCompleted(LOG);
}
- if(logger.isVerbose()) {
- logger.verbose("Computing median error.");
+ if(LOG.isVerbose()) {
+ LOG.verbose("Computing median error.");
}
double median_dev_from_median;
{
@@ -164,8 +162,7 @@ public class TrimmedMeanApproach<N> extends AbstractNeighborhoodOutlier<N> {
{
int i = 0;
for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
- ei[i] = errors.doubleValue(id);
+ ei[i] = errors.doubleValue(iditer);
i++;
}
}
@@ -178,15 +175,14 @@ public class TrimmedMeanApproach<N> extends AbstractNeighborhoodOutlier<N> {
median_dev_from_median = QuickSelect.median(ei);
}
- if(logger.isVerbose()) {
- logger.verbose("Normalizing scores.");
+ if(LOG.isVerbose()) {
+ LOG.verbose("Normalizing scores.");
}
// calculate score
DoubleMinMax minmax = new DoubleMinMax();
for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
- double score = Math.abs(errors.doubleValue(id)) * 0.6745 / median_dev_from_median;
- scores.putDouble(id, score);
+ double score = Math.abs(errors.doubleValue(iditer)) * 0.6745 / median_dev_from_median;
+ scores.putDouble(iditer, score);
minmax.put(score);
}
//
@@ -199,17 +195,17 @@ public class TrimmedMeanApproach<N> extends AbstractNeighborhoodOutlier<N> {
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
@Override
public TypeInformation[] getInputTypeRestriction() {
// Get one dimensional attribute for analysis.
- return TypeUtil.array(getNeighborSetPredicateFactory().getInputTypeRestriction(), VectorFieldTypeInformation.get(NumberVector.class, 1));
+ return TypeUtil.array(getNeighborSetPredicateFactory().getInputTypeRestriction(), new VectorFieldTypeInformation<NumberVector<?>>(NumberVector.class, 1));
}
/**
- * Parameterizer
+ * Parameterizer.
*
* @author Ahmed Hettab
*
@@ -219,19 +215,21 @@ public class TrimmedMeanApproach<N> extends AbstractNeighborhoodOutlier<N> {
*/
public static class Parameterizer<N> extends AbstractNeighborhoodOutlier.Parameterizer<N> {
/**
- * Parameter for the percentile value p
+ * Parameter for the percentile value p.
*/
- public static final OptionID P_ID = OptionID.getOrCreateOptionID("tma.p", "the percentile parameter");
+ public static final OptionID P_ID = new OptionID("tma.p", "the percentile parameter");
/**
- * Percentile parameter p
+ * Percentile parameter p.
*/
protected double p = 0.2;
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- DoubleParameter pP = new DoubleParameter(P_ID, new IntervalConstraint(0.0, IntervalBoundary.OPEN, 0.5, IntervalBoundary.OPEN));
+ DoubleParameter pP = new DoubleParameter(P_ID);
+ pP.addConstraint(new GreaterConstraint(0.0));
+ pP.addConstraint(new LessConstraint(0.5));
if(config.grab(pP)) {
p = pP.getValue();
}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/neighborhood/AbstractPrecomputedNeighborhood.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/neighborhood/AbstractPrecomputedNeighborhood.java
index 5898b053..2c706ce0 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/neighborhood/AbstractPrecomputedNeighborhood.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/neighborhood/AbstractPrecomputedNeighborhood.java
@@ -24,7 +24,8 @@ package de.lmu.ifi.dbs.elki.algorithm.outlier.spatial.neighborhood;
*/
import de.lmu.ifi.dbs.elki.database.datastore.DataStore;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.logging.Logging;
@@ -50,7 +51,7 @@ public abstract class AbstractPrecomputedNeighborhood implements NeighborSetPred
}
@Override
- public DBIDs getNeighborDBIDs(DBID reference) {
+ public DBIDs getNeighborDBIDs(DBIDRef reference) {
DBIDs neighbors = store.get(reference);
if(neighbors != null) {
return neighbors;
@@ -60,7 +61,7 @@ public abstract class AbstractPrecomputedNeighborhood implements NeighborSetPred
if(getLogger().isDebugging()) {
getLogger().warning("No neighbors for object " + reference);
}
- return reference;
+ return DBIDUtil.deref(reference);
}
}
@@ -69,7 +70,7 @@ public abstract class AbstractPrecomputedNeighborhood implements NeighborSetPred
*
* @return Logger
*/
- abstract protected Logging getLogger();
+ protected abstract Logging getLogger();
/**
* Factory class.
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/neighborhood/ExtendedNeighborhood.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/neighborhood/ExtendedNeighborhood.java
index 7a2fda52..4aa96b25 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/neighborhood/ExtendedNeighborhood.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/neighborhood/ExtendedNeighborhood.java
@@ -28,7 +28,6 @@ import de.lmu.ifi.dbs.elki.database.datastore.DataStore;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreFactory;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreUtil;
import de.lmu.ifi.dbs.elki.database.datastore.WritableDataStore;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
@@ -54,7 +53,7 @@ public class ExtendedNeighborhood extends AbstractPrecomputedNeighborhood {
/**
* The logger to use.
*/
- static final Logging logger = Logging.getLogger(ExtendedNeighborhood.class);
+ private static final Logging LOG = Logging.getLogger(ExtendedNeighborhood.class);
/**
* Constructor.
@@ -67,7 +66,7 @@ public class ExtendedNeighborhood extends AbstractPrecomputedNeighborhood {
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
@Override
@@ -132,23 +131,22 @@ public class ExtendedNeighborhood extends AbstractPrecomputedNeighborhood {
final WritableDataStore<DBIDs> store = DataStoreUtil.makeStorage(database.getDBIDs(), DataStoreFactory.HINT_HOT | DataStoreFactory.HINT_STATIC | DataStoreFactory.HINT_TEMP, DBIDs.class);
// Expand multiple steps
- FiniteProgress progress = logger.isVerbose() ? new FiniteProgress("Expanding neighborhoods", database.size(), logger) : null;
+ FiniteProgress progress = LOG.isVerbose() ? new FiniteProgress("Expanding neighborhoods", database.size(), LOG) : null;
for(DBIDIter iter = database.iterDBIDs(); iter.valid(); iter.advance()) {
- DBID id = iter.getDBID();
- HashSetModifiableDBIDs res = DBIDUtil.newHashSet(id);
- DBIDs todo = id;
+ HashSetModifiableDBIDs res = DBIDUtil.newHashSet();
+ res.add(iter);
+ DBIDs todo = DBIDUtil.deref(iter);
for(int i = 0; i < steps; i++) {
ModifiableDBIDs ntodo = DBIDUtil.newHashSet();
for(DBIDIter iter2 = todo.iter(); iter2.valid(); iter2.advance()) {
- DBIDs add = innerinst.getNeighborDBIDs(iter2.getDBID());
+ DBIDs add = innerinst.getNeighborDBIDs(iter2);
if(add != null) {
- for(DBIDIter iter3 = add.iter(); iter.valid(); iter.advance()) {
- DBID nid = iter3.getDBID();
- if(res.contains(nid)) {
+ for(DBIDIter iter3 = add.iter(); iter3.valid(); iter3.advance()) {
+ if(res.contains(iter3)) {
continue;
}
- ntodo.add(nid);
- res.add(nid);
+ ntodo.add(iter3);
+ res.add(iter3);
}
}
}
@@ -157,13 +155,13 @@ public class ExtendedNeighborhood extends AbstractPrecomputedNeighborhood {
}
todo = ntodo;
}
- store.put(id, res);
+ store.put(iter, res);
if(progress != null) {
- progress.incrementProcessed(logger);
+ progress.incrementProcessed(LOG);
}
}
if(progress != null) {
- progress.ensureCompleted(logger);
+ progress.ensureCompleted(LOG);
}
return store;
@@ -180,12 +178,12 @@ public class ExtendedNeighborhood extends AbstractPrecomputedNeighborhood {
/**
* Parameter to specify the neighborhood predicate to use.
*/
- public static final OptionID NEIGHBORHOOD_ID = OptionID.getOrCreateOptionID("extendedneighbors.neighborhood", "The inner neighborhood predicate to use.");
+ public static final OptionID NEIGHBORHOOD_ID = new OptionID("extendedneighbors.neighborhood", "The inner neighborhood predicate to use.");
/**
* Parameter to specify the number of steps allowed
*/
- public static final OptionID STEPS_ID = OptionID.getOrCreateOptionID("extendedneighbors.steps", "The number of steps allowed in the neighborhood graph.");
+ public static final OptionID STEPS_ID = new OptionID("extendedneighbors.steps", "The number of steps allowed in the neighborhood graph.");
/**
* The number of steps to do.
@@ -225,7 +223,8 @@ public class ExtendedNeighborhood extends AbstractPrecomputedNeighborhood {
* @return number of steps, default 1
*/
public static int getParameterSteps(Parameterization config) {
- final IntParameter param = new IntParameter(STEPS_ID, new GreaterEqualConstraint(1));
+ final IntParameter param = new IntParameter(STEPS_ID);
+ param.addConstraint(new GreaterEqualConstraint(1));
if(config.grab(param)) {
return param.getValue();
}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/neighborhood/ExternalNeighborhood.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/neighborhood/ExternalNeighborhood.java
index 74e5bbcf..01052c1f 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/neighborhood/ExternalNeighborhood.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/neighborhood/ExternalNeighborhood.java
@@ -63,12 +63,12 @@ public class ExternalNeighborhood extends AbstractPrecomputedNeighborhood {
/**
* Logger
*/
- static final Logging logger = Logging.getLogger(ExternalNeighborhood.class);
+ private static final Logging LOG = Logging.getLogger(ExternalNeighborhood.class);
/**
* Parameter to specify the neighborhood file
*/
- public static final OptionID NEIGHBORHOOD_FILE_ID = OptionID.getOrCreateOptionID("externalneighbors.file", "The file listing the neighbors.");
+ public static final OptionID NEIGHBORHOOD_FILE_ID = new OptionID("externalneighbors.file", "The file listing the neighbors.");
/**
* Constructor.
@@ -91,7 +91,7 @@ public class ExternalNeighborhood extends AbstractPrecomputedNeighborhood {
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -136,33 +136,32 @@ public class ExternalNeighborhood extends AbstractPrecomputedNeighborhood {
private DataStore<DBIDs> loadNeighbors(Relation<?> database) {
final WritableDataStore<DBIDs> store = DataStoreUtil.makeStorage(database.getDBIDs(), DataStoreFactory.HINT_HOT | DataStoreFactory.HINT_STATIC | DataStoreFactory.HINT_TEMP, DBIDs.class);
- if(logger.isVerbose()) {
- logger.verbose("Loading external neighborhoods.");
+ if(LOG.isVerbose()) {
+ LOG.verbose("Loading external neighborhoods.");
}
- if(logger.isDebugging()) {
- logger.verbose("Building reverse label index...");
+ if(LOG.isDebugging()) {
+ LOG.verbose("Building reverse label index...");
}
// Build a map label/ExternalId -> DBID
// (i.e. a reverse index!)
// TODO: move this into the database layer to share?
- Map<String, DBID> lblmap = new HashMap<String, DBID>(database.size() * 2);
+ Map<String, DBID> lblmap = new HashMap<String, DBID>(database.size() << 1);
{
Relation<LabelList> olq = database.getDatabase().getRelation(TypeUtil.LABELLIST);
Relation<ExternalID> eidq = database.getDatabase().getRelation(TypeUtil.EXTERNALID);
for(DBIDIter iditer = database.iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
if(eidq != null) {
- ExternalID eid = eidq.get(id);
+ ExternalID eid = eidq.get(iditer);
if(eid != null) {
- lblmap.put(eid.toString(), id);
+ lblmap.put(eid.toString(), DBIDUtil.deref(iditer));
}
}
if(olq != null) {
- LabelList label = olq.get(id);
+ LabelList label = olq.get(iditer);
if(label != null) {
for(String lbl : label) {
- lblmap.put(lbl, id);
+ lblmap.put(lbl, DBIDUtil.deref(iditer));
}
}
}
@@ -170,8 +169,8 @@ public class ExternalNeighborhood extends AbstractPrecomputedNeighborhood {
}
try {
- if(logger.isDebugging()) {
- logger.verbose("Loading neighborhood file.");
+ if(LOG.isDebugging()) {
+ LOG.verbose("Loading neighborhood file.");
}
InputStream in = new FileInputStream(file);
in = FileUtil.tryGzipInput(in);
@@ -187,16 +186,16 @@ public class ExternalNeighborhood extends AbstractPrecomputedNeighborhood {
neighbours.add(neigh);
}
else {
- if(logger.isDebugging()) {
- logger.debug("No object found for label " + entries[i]);
+ if(LOG.isDebugging()) {
+ LOG.debug("No object found for label " + entries[i]);
}
}
}
store.put(id, neighbours);
}
else {
- if(logger.isDebugging()) {
- logger.warning("No object found for label " + entries[0]);
+ if(LOG.isDebugging()) {
+ LOG.warning("No object found for label " + entries[0]);
}
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/neighborhood/NeighborSetPredicate.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/neighborhood/NeighborSetPredicate.java
index 3a6d0e28..b52f8e91 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/neighborhood/NeighborSetPredicate.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/neighborhood/NeighborSetPredicate.java
@@ -24,7 +24,7 @@ package de.lmu.ifi.dbs.elki.algorithm.outlier.spatial.neighborhood;
*/
import de.lmu.ifi.dbs.elki.data.type.TypeInformation;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.result.Result;
@@ -42,7 +42,7 @@ public interface NeighborSetPredicate extends Result {
* @param reference Reference object
* @return Neighborhood
*/
- public DBIDs getNeighborDBIDs(DBID reference);
+ public DBIDs getNeighborDBIDs(DBIDRef reference);
/**
* Factory interface to produce instances.
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/neighborhood/PrecomputedKNearestNeighborNeighborhood.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/neighborhood/PrecomputedKNearestNeighborNeighborhood.java
index 9dd2dee1..f6000ef0 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/neighborhood/PrecomputedKNearestNeighborNeighborhood.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/neighborhood/PrecomputedKNearestNeighborNeighborhood.java
@@ -29,15 +29,13 @@ import de.lmu.ifi.dbs.elki.database.datastore.DataStoreFactory;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreUtil;
import de.lmu.ifi.dbs.elki.database.datastore.WritableDataStore;
import de.lmu.ifi.dbs.elki.database.ids.ArrayModifiableDBIDs;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
import de.lmu.ifi.dbs.elki.database.query.knn.KNNQuery;
-import de.lmu.ifi.dbs.elki.database.query.knn.KNNResult;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
@@ -57,7 +55,7 @@ public class PrecomputedKNearestNeighborNeighborhood<D extends Distance<D>> exte
/**
* Logger
*/
- private static final Logging logger = Logging.getLogger(PrecomputedKNearestNeighborNeighborhood.class);
+ private static final Logging LOG = Logging.getLogger(PrecomputedKNearestNeighborNeighborhood.class);
/**
* Constructor.
@@ -80,7 +78,7 @@ public class PrecomputedKNearestNeighborNeighborhood<D extends Distance<D>> exte
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -121,13 +119,12 @@ public class PrecomputedKNearestNeighborNeighborhood<D extends Distance<D>> exte
// TODO: use bulk?
WritableDataStore<DBIDs> s = DataStoreUtil.makeStorage(relation.getDBIDs(), DataStoreFactory.HINT_HOT | DataStoreFactory.HINT_STATIC, DBIDs.class);
for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
- KNNResult<D> neighbors = knnQuery.getKNNForDBID(id, k);
+ KNNResult<D> neighbors = knnQuery.getKNNForDBID(iditer, k);
ArrayModifiableDBIDs neighbours = DBIDUtil.newArray(neighbors.size());
- for(DistanceResultPair<D> dpair : neighbors) {
- neighbours.add(dpair.getDBID());
+ for (DBIDIter neighbor = neighbors.iter(); neighbor.valid(); neighbor.advance()) {
+ neighbours.add(neighbor);
}
- s.put(id, neighbours);
+ s.put(iditer, neighbours);
}
return new PrecomputedKNearestNeighborNeighborhood<D>(s);
}
@@ -151,12 +148,12 @@ public class PrecomputedKNearestNeighborNeighborhood<D extends Distance<D>> exte
/**
* Parameter k
*/
- public static final OptionID K_ID = OptionID.getOrCreateOptionID("neighborhood.k", "the number of neighbors");
+ public static final OptionID K_ID = new OptionID("neighborhood.k", "the number of neighbors");
/**
* Parameter to specify the distance function to use
*/
- public static final OptionID DISTANCEFUNCTION_ID = OptionID.getOrCreateOptionID("neighborhood.distancefunction", "the distance function to use");
+ public static final OptionID DISTANCEFUNCTION_ID = new OptionID("neighborhood.distancefunction", "the distance function to use");
/**
* Parameter k
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/neighborhood/weighted/LinearWeightedExtendedNeighborhood.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/neighborhood/weighted/LinearWeightedExtendedNeighborhood.java
index d170571f..f1c68577 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/neighborhood/weighted/LinearWeightedExtendedNeighborhood.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/neighborhood/weighted/LinearWeightedExtendedNeighborhood.java
@@ -29,10 +29,11 @@ import java.util.List;
import de.lmu.ifi.dbs.elki.algorithm.outlier.spatial.neighborhood.NeighborSetPredicate;
import de.lmu.ifi.dbs.elki.data.type.TypeInformation;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
+import de.lmu.ifi.dbs.elki.database.ids.DoubleDBIDPair;
import de.lmu.ifi.dbs.elki.database.ids.ModifiableDBIDs;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
@@ -41,7 +42,6 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterEqualCons
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ObjectParameter;
-import de.lmu.ifi.dbs.elki.utilities.pairs.DoubleObjPair;
/**
* Neighborhood obtained by computing the k-fold closure of an existing
@@ -87,29 +87,27 @@ public class LinearWeightedExtendedNeighborhood implements WeightedNeighborSetPr
}
@Override
- public Collection<DoubleObjPair<DBID>> getWeightedNeighbors(DBID reference) {
+ public Collection<DoubleDBIDPair> getWeightedNeighbors(DBIDRef reference) {
ModifiableDBIDs seen = DBIDUtil.newHashSet();
- List<DoubleObjPair<DBID>> result = new ArrayList<DoubleObjPair<DBID>>();
+ List<DoubleDBIDPair> result = new ArrayList<DoubleDBIDPair>();
// Add starting object
- result.add(new DoubleObjPair<DBID>(computeWeight(0), reference));
+ result.add(DBIDUtil.newPair(computeWeight(0), reference));
seen.add(reference);
// Extend.
- DBIDs cur = reference;
+ DBIDs cur = DBIDUtil.deref(reference);
for(int i = 1; i <= steps; i++) {
final double weight = computeWeight(i);
// Collect newly discovered IDs
ModifiableDBIDs add = DBIDUtil.newHashSet();
for(DBIDIter iter = cur.iter(); iter.valid(); iter.advance()) {
- DBID id = iter.getDBID();
- for(DBIDIter iter2 = inner.getNeighborDBIDs(id).iter(); iter2.valid(); iter2.advance()) {
- DBID nid = iter2.getDBID();
+ for(DBIDIter iter2 = inner.getNeighborDBIDs(iter).iter(); iter2.valid(); iter2.advance()) {
// Seen before?
- if(seen.contains(nid)) {
+ if(seen.contains(iter2)) {
continue;
}
- add.add(nid);
- result.add(new DoubleObjPair<DBID>(weight, nid));
+ add.add(iter2);
+ result.add(DBIDUtil.newPair(weight, iter2));
}
}
if(add.size() == 0) {
@@ -172,12 +170,12 @@ public class LinearWeightedExtendedNeighborhood implements WeightedNeighborSetPr
/**
* Parameter to specify the neighborhood predicate to use.
*/
- public static final OptionID NEIGHBORHOOD_ID = OptionID.getOrCreateOptionID("extendedneighbors.neighborhood", "The inner neighborhood predicate to use.");
+ public static final OptionID NEIGHBORHOOD_ID = new OptionID("extendedneighbors.neighborhood", "The inner neighborhood predicate to use.");
/**
* Parameter to specify the number of steps allowed
*/
- public static final OptionID STEPS_ID = OptionID.getOrCreateOptionID("extendedneighbors.steps", "The number of steps allowed in the neighborhood graph.");
+ public static final OptionID STEPS_ID = new OptionID("extendedneighbors.steps", "The number of steps allowed in the neighborhood graph.");
/**
* The number of steps to do.
@@ -217,7 +215,8 @@ public class LinearWeightedExtendedNeighborhood implements WeightedNeighborSetPr
* @return number of steps, default 1
*/
public static int getParameterSteps(Parameterization config) {
- final IntParameter param = new IntParameter(STEPS_ID, new GreaterEqualConstraint(1));
+ final IntParameter param = new IntParameter(STEPS_ID);
+ param.addConstraint(new GreaterEqualConstraint(1));
if(config.grab(param)) {
return param.getValue();
}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/neighborhood/weighted/UnweightedNeighborhoodAdapter.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/neighborhood/weighted/UnweightedNeighborhoodAdapter.java
index ce0666df..c179d81f 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/neighborhood/weighted/UnweightedNeighborhoodAdapter.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/neighborhood/weighted/UnweightedNeighborhoodAdapter.java
@@ -28,15 +28,16 @@ import java.util.Collection;
import de.lmu.ifi.dbs.elki.algorithm.outlier.spatial.neighborhood.NeighborSetPredicate;
import de.lmu.ifi.dbs.elki.data.type.TypeInformation;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
+import de.lmu.ifi.dbs.elki.database.ids.DoubleDBIDPair;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ObjectParameter;
-import de.lmu.ifi.dbs.elki.utilities.pairs.DoubleObjPair;
/**
* Adapter to use unweighted neighborhoods in an algorithm that requires
@@ -61,12 +62,11 @@ public class UnweightedNeighborhoodAdapter implements WeightedNeighborSetPredica
}
@Override
- public Collection<DoubleObjPair<DBID>> getWeightedNeighbors(DBID reference) {
+ public Collection<DoubleDBIDPair> getWeightedNeighbors(DBIDRef reference) {
DBIDs neighbors = inner.getNeighborDBIDs(reference);
- ArrayList<DoubleObjPair<DBID>> adapted = new ArrayList<DoubleObjPair<DBID>>(neighbors.size());
+ ArrayList<DoubleDBIDPair> adapted = new ArrayList<DoubleDBIDPair>(neighbors.size());
for(DBIDIter iter = neighbors.iter(); iter.valid(); iter.advance()) {
- DBID id = iter.getDBID();
- adapted.add(new DoubleObjPair<DBID>(1.0, id));
+ adapted.add(DBIDUtil.newPair(1.0, iter));
}
return adapted;
}
@@ -120,7 +120,7 @@ public class UnweightedNeighborhoodAdapter implements WeightedNeighborSetPredica
/**
* The parameter to give the non-weighted neighborhood to use.
*/
- public static final OptionID INNER_ID = OptionID.getOrCreateOptionID("neighborhood.inner", "Parameter for the non-weighted neighborhood to use.");
+ public static final OptionID INNER_ID = new OptionID("neighborhood.inner", "Parameter for the non-weighted neighborhood to use.");
/**
* The actual predicate.
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/neighborhood/weighted/WeightedNeighborSetPredicate.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/neighborhood/weighted/WeightedNeighborSetPredicate.java
index b147935a..16d37587 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/neighborhood/weighted/WeightedNeighborSetPredicate.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/spatial/neighborhood/weighted/WeightedNeighborSetPredicate.java
@@ -26,10 +26,10 @@ package de.lmu.ifi.dbs.elki.algorithm.outlier.spatial.neighborhood.weighted;
import java.util.Collection;
import de.lmu.ifi.dbs.elki.data.type.TypeInformation;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.database.ids.DoubleDBIDPair;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable;
-import de.lmu.ifi.dbs.elki.utilities.pairs.DoubleObjPair;
/**
* Neighbor predicate with weight support.
@@ -43,7 +43,7 @@ public interface WeightedNeighborSetPredicate {
* @param reference Reference object
* @return Weighted Neighborhood
*/
- public Collection<DoubleObjPair<DBID>> getWeightedNeighbors(DBID reference);
+ public Collection<DoubleDBIDPair> getWeightedNeighbors(DBIDRef reference);
/**
* Factory interface to produce instances.
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/subspace/OUTRES.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/subspace/OUTRES.java
index 573233a7..1965914d 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/subspace/OUTRES.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/subspace/OUTRES.java
@@ -23,10 +23,8 @@ package de.lmu.ifi.dbs.elki.algorithm.outlier.subspace;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
-import java.util.List;
import de.lmu.ifi.dbs.elki.algorithm.AbstractAlgorithm;
import de.lmu.ifi.dbs.elki.algorithm.outlier.OutlierAlgorithm;
@@ -37,16 +35,20 @@ import de.lmu.ifi.dbs.elki.database.QueryUtil;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreFactory;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreUtil;
import de.lmu.ifi.dbs.elki.database.datastore.WritableDoubleDataStore;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
-import de.lmu.ifi.dbs.elki.database.query.DoubleDistanceResultPair;
+import de.lmu.ifi.dbs.elki.database.ids.DistanceDBIDPair;
+import de.lmu.ifi.dbs.elki.database.ids.DoubleDistanceDBIDPair;
import de.lmu.ifi.dbs.elki.database.query.range.RangeQuery;
import de.lmu.ifi.dbs.elki.database.relation.MaterializedRelation;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.distance.distancefunction.PrimitiveDoubleDistanceFunction;
import de.lmu.ifi.dbs.elki.distance.distancefunction.subspace.SubspaceEuclideanDistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResultIter;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DoubleDistanceDBIDList;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DoubleDistanceDBIDResultIter;
import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.logging.progress.FiniteProgress;
@@ -58,7 +60,6 @@ import de.lmu.ifi.dbs.elki.math.statistics.distribution.GammaDistribution;
import de.lmu.ifi.dbs.elki.result.outlier.InvertedOutlierScoreMeta;
import de.lmu.ifi.dbs.elki.result.outlier.OutlierResult;
import de.lmu.ifi.dbs.elki.result.outlier.OutlierScoreMeta;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
@@ -89,11 +90,11 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.DoubleParameter;
* @param <V> vector type
*/
@Reference(authors = "E. Müller, M. Schiffer, T. Seidl", title = "Adaptive outlierness for subspace outlier ranking", booktitle = "Proc. 19th ACM International Conference on Information and knowledge management")
-public class OUTRES<V extends NumberVector<V, ?>> extends AbstractAlgorithm<OutlierResult> implements OutlierAlgorithm {
+public class OUTRES<V extends NumberVector<?>> extends AbstractAlgorithm<OutlierResult> implements OutlierAlgorithm {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(OUTRES.class);
+ private static final Logging LOG = Logging.getLogger(OUTRES.class);
/**
* The epsilon (in 2d) parameter
@@ -128,7 +129,7 @@ public class OUTRES<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Outl
KernelDensityEstimator kernel = new KernelDensityEstimator(relation);
BitSet subspace = new BitSet(kernel.dim);
- FiniteProgress progress = logger.isVerbose() ? new FiniteProgress("OutRank scores", relation.size(), logger) : null;
+ FiniteProgress progress = LOG.isVerbose() ? new FiniteProgress("OUTRES scores", relation.size(), LOG) : null;
for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
subspace.clear();
@@ -136,11 +137,11 @@ public class OUTRES<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Outl
ranks.putDouble(iditer, score);
minmax.put(score);
if(progress != null) {
- progress.incrementProcessed(logger);
+ progress.incrementProcessed(LOG);
}
}
if(progress != null) {
- progress.ensureCompleted(logger);
+ progress.ensureCompleted(LOG);
}
OutlierScoreMeta meta = new InvertedOutlierScoreMeta(minmax.getMin(), minmax.getMax(), 0., 1., 1.);
@@ -159,33 +160,34 @@ public class OUTRES<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Outl
*/
public double outresScore(final int s, BitSet subspace, DBIDRef id, KernelDensityEstimator kernel) {
double score = 1.0; // Initial score is 1.0
+ final SubspaceEuclideanDistanceFunction df = new SubspaceEuclideanDistanceFunction(subspace);
+ MeanVariance meanv = new MeanVariance();
for(int i = s; i < kernel.dim; i++) {
if(subspace.get(i)) { // TODO: needed? Or should we always start with i=0?
continue;
}
subspace.set(i);
- final SubspaceEuclideanDistanceFunction df = new SubspaceEuclideanDistanceFunction(subspace);
+ df.setSelectedDimensions(subspace);
final double adjustedEps = kernel.adjustedEps(kernel.dim);
// Query with a larger window, to also get neighbors of neighbors
// Subspace euclidean is metric!
- final DoubleDistance range = new DoubleDistance(adjustedEps * 2);
+ final DoubleDistance range = new DoubleDistance(adjustedEps * 2.);
RangeQuery<V, DoubleDistance> rq = QueryUtil.getRangeQuery(kernel.relation, df, range);
- List<DistanceResultPair<DoubleDistance>> neighc = rq.getRangeForDBID(id, range);
- List<DoubleDistanceResultPair> neigh = refineRange(neighc, adjustedEps);
+ DistanceDBIDResult<DoubleDistance> neighc = rq.getRangeForDBID(id, range);
+ DoubleDistanceDBIDList neigh = refineRange(neighc, adjustedEps);
if(neigh.size() > 2) {
// Relevance test
if(relevantSubspace(subspace, neigh, kernel)) {
final double density = kernel.subspaceDensity(subspace, neigh);
- final double deviation;
// Compute mean and standard deviation for densities of neighbors.
- MeanVariance meanv = new MeanVariance();
- for(DoubleDistanceResultPair pair : neigh) {
- List<DoubleDistanceResultPair> n2 = subsetNeighborhoodQuery(neighc, pair.getDBID(), df, adjustedEps, kernel);
+ meanv.reset();
+ for (DoubleDistanceDBIDResultIter neighbor = neigh.iter(); neighbor.valid(); neighbor.advance()) {
+ DoubleDistanceDBIDList n2 = subsetNeighborhoodQuery(neighc, neighbor, df, adjustedEps, kernel);
meanv.put(kernel.subspaceDensity(subspace, n2));
}
- deviation = (meanv.getMean() - density) / (2. * meanv.getSampleStddev());
+ final double deviation = (meanv.getMean() - density) / (2. * meanv.getSampleStddev());
// High deviation:
if(deviation >= 1) {
score *= (density / deviation);
@@ -206,19 +208,20 @@ public class OUTRES<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Outl
* @param adjustedEps New epsilon
* @return refined list
*/
- private List<DoubleDistanceResultPair> refineRange(List<DistanceResultPair<DoubleDistance>> neighc, double adjustedEps) {
- List<DoubleDistanceResultPair> n = new ArrayList<DoubleDistanceResultPair>(neighc.size());
+ private DoubleDistanceDBIDList refineRange(DistanceDBIDResult<DoubleDistance> neighc, double adjustedEps) {
+ DoubleDistanceDBIDList n = new DoubleDistanceDBIDList(neighc.size());
// We don't have a guarantee for this list to be sorted
- for(DistanceResultPair<DoubleDistance> p : neighc) {
- if(p instanceof DoubleDistanceResultPair) {
- if(((DoubleDistanceResultPair) p).getDoubleDistance() <= adjustedEps) {
- n.add((DoubleDistanceResultPair) p);
+ for (DistanceDBIDResultIter<DoubleDistance> neighbor = neighc.iter(); neighbor.valid(); neighbor.advance()) {
+ DistanceDBIDPair<DoubleDistance> p = neighbor.getDistancePair();
+ if(p instanceof DoubleDistanceDBIDPair) {
+ if(((DoubleDistanceDBIDPair) p).doubleDistance() <= adjustedEps) {
+ n.add((DoubleDistanceDBIDPair) p);
}
}
else {
double dist = p.getDistance().doubleValue();
if(dist <= adjustedEps) {
- n.add(new DoubleDistanceResultPair(dist, p.getDBID()));
+ n.add(dist, p);
}
}
}
@@ -235,13 +238,14 @@ public class OUTRES<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Outl
* @param kernel Kernel
* @return Neighbors of neighbor object
*/
- private List<DoubleDistanceResultPair> subsetNeighborhoodQuery(List<DistanceResultPair<DoubleDistance>> neighc, DBID dbid, PrimitiveDoubleDistanceFunction<? super V> df, double adjustedEps, KernelDensityEstimator kernel) {
- List<DoubleDistanceResultPair> n = new ArrayList<DoubleDistanceResultPair>(neighc.size());
+ private DoubleDistanceDBIDList subsetNeighborhoodQuery(DistanceDBIDResult<DoubleDistance> neighc, DBIDRef dbid, PrimitiveDoubleDistanceFunction<? super V> df, double adjustedEps, KernelDensityEstimator kernel) {
+ DoubleDistanceDBIDList n = new DoubleDistanceDBIDList(neighc.size());
V query = kernel.relation.get(dbid);
- for(DistanceResultPair<DoubleDistance> p : neighc) {
+ for (DistanceDBIDResultIter<DoubleDistance> neighbor = neighc.iter(); neighbor.valid(); neighbor.advance()) {
+ DistanceDBIDPair<DoubleDistance> p = neighbor.getDistancePair();
double dist = df.doubleDistance(query, kernel.relation.get(p));
if(dist <= adjustedEps) {
- n.add(new DoubleDistanceResultPair(dist, p.getDBID()));
+ n.add(dist, p);
}
}
return n;
@@ -255,7 +259,7 @@ public class OUTRES<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Outl
* @param kernel Kernel density estimator
* @return relevance test result
*/
- protected boolean relevantSubspace(BitSet subspace, List<DoubleDistanceResultPair> neigh, KernelDensityEstimator kernel) {
+ protected boolean relevantSubspace(BitSet subspace, DoubleDistanceDBIDList neigh, KernelDensityEstimator kernel) {
Relation<V> relation = kernel.relation;
final double crit = K_S_CRITICAL001 / Math.sqrt(neigh.size());
@@ -264,9 +268,9 @@ public class OUTRES<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Outl
double[] data = new double[neigh.size()];
{
int count = 0;
- for(DoubleDistanceResultPair object : neigh) {
- V vector = relation.get(object.getDBID());
- data[count] = vector.doubleValue(dim + 1);
+ for (DBIDIter neighbor = neigh.iter(); neighbor.valid(); neighbor.advance()) {
+ V vector = relation.get(neighbor);
+ data[count] = vector.doubleValue(dim);
count++;
}
assert (count == neigh.size());
@@ -278,7 +282,7 @@ public class OUTRES<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Outl
// Kolmogorow-Smirnow-Test against uniform distribution:
for(int j = 1; j < data.length - 2; j++) {
- double delta = (j / (data.length - 1)) - ((data[j] - min) / norm);
+ double delta = (j / (data.length - 1.)) - ((data[j] - min) / norm);
if(Math.abs(delta) > crit) {
return false;
}
@@ -326,7 +330,7 @@ public class OUTRES<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Outl
public KernelDensityEstimator(Relation<V> relation) {
super();
this.relation = relation;
- dim = DatabaseUtil.dimensionality(relation);
+ dim = RelationUtil.dimensionality(relation);
hopttwo = optimalBandwidth(2);
epsilons = new double[dim + 1];
Arrays.fill(epsilons, Double.NEGATIVE_INFINITY);
@@ -337,15 +341,15 @@ public class OUTRES<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Outl
* Compute density in the given subspace.
*
* @param subspace Subspace
- * @param neighbours Neighbor distance list
+ * @param neighbors Neighbor distance list
* @return Density
*/
- protected double subspaceDensity(BitSet subspace, List<DoubleDistanceResultPair> neighbours) {
+ protected double subspaceDensity(BitSet subspace, DoubleDistanceDBIDList neighbors) {
final double bandwidth = optimalBandwidth(subspace.cardinality());
double density = 0;
- for(DoubleDistanceResultPair pair : neighbours) {
- double v = pair.getDoubleDistance() / bandwidth;
+ for (DoubleDistanceDBIDResultIter neighbor = neighbors.iter(); neighbor.valid(); neighbor.advance()) {
+ double v = neighbor.doubleDistance() / bandwidth;
if(v < 1) {
density += 1 - (v * v);
}
@@ -363,7 +367,7 @@ public class OUTRES<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Outl
protected double optimalBandwidth(int dim) {
// Pi in the publication is redundant and cancels out!
double hopt = 8 * GammaDistribution.gamma(dim / 2.0 + 1) * (dim + 4) * Math.pow(2, dim);
- return hopt * Math.pow(relation.size(), (-1 / (dim + 4)));
+ return hopt * Math.pow(relation.size(), (-1. / (dim + 4)));
}
/**
@@ -385,7 +389,7 @@ public class OUTRES<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Outl
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
@Override
@@ -400,11 +404,11 @@ public class OUTRES<V extends NumberVector<V, ?>> extends AbstractAlgorithm<Outl
*
* @apiviz.exclude
*/
- public static class Parameterizer<O extends NumberVector<O, ?>> extends AbstractParameterizer {
+ public static class Parameterizer<O extends NumberVector<?>> extends AbstractParameterizer {
/**
* Option ID for Epsilon parameter
*/
- public static final OptionID D_ID = OptionID.getOrCreateOptionID("outres.epsilon", "Range value for OUTRES in 2 dimensions.");
+ public static final OptionID D_ID = new OptionID("outres.epsilon", "Range value for OUTRES in 2 dimensions.");
/**
* Query radius
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/subspace/OutRankS1.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/subspace/OutRankS1.java
index e370d2bf..79243213 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/subspace/OutRankS1.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/subspace/OutRankS1.java
@@ -78,7 +78,7 @@ public class OutRankS1 extends AbstractAlgorithm<OutlierResult> implements Outli
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(OutRankS1.class);
+ private static final Logging LOG = Logging.getLogger(OutRankS1.class);
/**
* Clustering algorithm to run.
@@ -110,23 +110,23 @@ public class OutRankS1 extends AbstractAlgorithm<OutlierResult> implements Outli
Clustering<? extends SubspaceModel<?>> clustering = clusteralg.run(database);
WritableDoubleDataStore score = DataStoreUtil.makeDoubleStorage(ids, DataStoreFactory.HINT_HOT);
- for(DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
+ for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
score.putDouble(iter, 0);
}
int maxdim = 0, maxsize = 0;
// Find maximum dimensionality and cluster size
- for(Cluster<? extends SubspaceModel<?>> cluster : clustering.getAllClusters()) {
+ for (Cluster<? extends SubspaceModel<?>> cluster : clustering.getAllClusters()) {
maxsize = Math.max(maxsize, cluster.size());
maxdim = Math.max(maxdim, cluster.getModel().getDimensions().cardinality());
}
// Iterate over all clusters:
DoubleMinMax minmax = new DoubleMinMax();
- for(Cluster<? extends SubspaceModel<?>> cluster : clustering.getAllClusters()) {
+ for (Cluster<? extends SubspaceModel<?>> cluster : clustering.getAllClusters()) {
double relsize = cluster.size() / (double) maxsize;
double reldim = cluster.getModel().getDimensions().cardinality() / (double) maxdim;
// Process objects in the cluster
- for(DBIDIter iter = cluster.getIDs().iter(); iter.valid(); iter.advance()) {
+ for (DBIDIter iter = cluster.getIDs().iter(); iter.valid(); iter.advance()) {
double newscore = score.doubleValue(iter) + alpha * relsize + (1 - alpha) * reldim;
score.putDouble(iter, newscore);
minmax.put(newscore);
@@ -147,7 +147,7 @@ public class OutRankS1 extends AbstractAlgorithm<OutlierResult> implements Outli
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -161,12 +161,12 @@ public class OutRankS1 extends AbstractAlgorithm<OutlierResult> implements Outli
/**
* Clustering algorithm to use.
*/
- public static final OptionID ALGORITHM_ID = OptionID.getOrCreateOptionID("outrank.algorithm", "Subspace clustering algorithm to use.");
+ public static final OptionID ALGORITHM_ID = new OptionID("outrank.algorithm", "Subspace clustering algorithm to use.");
/**
* Alpha parameter for S1
*/
- public static final OptionID ALPHA_ID = OptionID.getOrCreateOptionID("outrank.s1.alpha", "Alpha parameter for S1 score.");
+ public static final OptionID ALPHA_ID = new OptionID("outrank.s1.alpha", "Alpha parameter for S1 score.");
/**
* Clustering algorithm to run.
@@ -182,12 +182,13 @@ public class OutRankS1 extends AbstractAlgorithm<OutlierResult> implements Outli
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
ObjectParameter<SubspaceClusteringAlgorithm<? extends SubspaceModel<?>>> algP = new ObjectParameter<SubspaceClusteringAlgorithm<? extends SubspaceModel<?>>>(ALGORITHM_ID, SubspaceClusteringAlgorithm.class);
- if(config.grab(algP)) {
+ if (config.grab(algP)) {
algorithm = algP.instantiateClass(config);
}
- DoubleParameter alphaP = new DoubleParameter(ALPHA_ID, new GreaterConstraint(0), 0.25);
- if(config.grab(alphaP)) {
- alpha = alphaP.getValue();
+ DoubleParameter alphaP = new DoubleParameter(ALPHA_ID, 0.25);
+ alphaP.addConstraint(new GreaterConstraint(0));
+ if (config.grab(alphaP)) {
+ alpha = alphaP.doubleValue();
}
}
@@ -196,4 +197,4 @@ public class OutRankS1 extends AbstractAlgorithm<OutlierResult> implements Outli
return new OutRankS1(algorithm, alpha);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/subspace/SOD.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/subspace/SOD.java
index 7fef95e0..35a780cd 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/subspace/SOD.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/subspace/SOD.java
@@ -36,14 +36,15 @@ import de.lmu.ifi.dbs.elki.database.datastore.DataStoreFactory;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreUtil;
import de.lmu.ifi.dbs.elki.database.datastore.WritableDataStore;
import de.lmu.ifi.dbs.elki.database.ids.ArrayModifiableDBIDs;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
+import de.lmu.ifi.dbs.elki.database.ids.DoubleDBIDPair;
import de.lmu.ifi.dbs.elki.database.query.similarity.SimilarityQuery;
import de.lmu.ifi.dbs.elki.database.relation.MaterializedRelation;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.distance.distancefunction.subspace.SubspaceEuclideanDistanceFunction;
import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance;
import de.lmu.ifi.dbs.elki.distance.similarityfunction.SharedNearestNeighborSimilarityFunction;
@@ -57,7 +58,6 @@ import de.lmu.ifi.dbs.elki.result.outlier.OutlierResult;
import de.lmu.ifi.dbs.elki.result.outlier.OutlierScoreMeta;
import de.lmu.ifi.dbs.elki.result.textwriter.TextWriteable;
import de.lmu.ifi.dbs.elki.result.textwriter.TextWriterStream;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.Heap;
import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.TiedTopBoundedHeap;
import de.lmu.ifi.dbs.elki.utilities.documentation.Description;
@@ -70,10 +70,10 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameteriz
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.DoubleParameter;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ObjectParameter;
-import de.lmu.ifi.dbs.elki.utilities.pairs.DoubleObjPair;
/**
- * Subspace Outlier Degree. Outlier detection method for axis-parallel subspaces.
+ * Subspace Outlier Degree. Outlier detection method for axis-parallel
+ * subspaces.
*
* Reference:
* <p>
@@ -89,34 +89,35 @@ import de.lmu.ifi.dbs.elki.utilities.pairs.DoubleObjPair;
* @apiviz.has SharedNearestNeighborSimilarityFunction
*
* @param <V> the type of NumberVector handled by this Algorithm
+ * @param <D> distance type
*/
// todo arthur comment
@Title("SOD: Subspace outlier degree")
@Description("Outlier Detection in Axis-Parallel Subspaces of High Dimensional Data")
@Reference(authors = "H.-P. Kriegel, P. Kröger, E. Schubert, A. Zimek", title = "Outlier Detection in Axis-Parallel Subspaces of High Dimensional Data", booktitle = "Proceedings of the 13th Pacific-Asia Conference on Knowledge Discovery and Data Mining (PAKDD), Bangkok, Thailand, 2009", url = "http://dx.doi.org/10.1007/978-3-642-01307-2")
-public class SOD<V extends NumberVector<V, ?>, D extends NumberDistance<D, ?>> extends AbstractAlgorithm<OutlierResult> implements OutlierAlgorithm {
+public class SOD<V extends NumberVector<?>, D extends NumberDistance<D, ?>> extends AbstractAlgorithm<OutlierResult> implements OutlierAlgorithm {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(SOD.class);
+ private static final Logging LOG = Logging.getLogger(SOD.class);
/**
* Parameter to specify the number of shared nearest neighbors to be
* considered for learning the subspace properties., must be an integer
* greater than 0.
*/
- public static final OptionID KNN_ID = OptionID.getOrCreateOptionID("sod.knn", "The number of most snn-similar objects to use as reference set for learning the subspace properties.");
+ public static final OptionID KNN_ID = new OptionID("sod.knn", "The number of most snn-similar objects to use as reference set for learning the subspace properties.");
/**
* Parameter to indicate the multiplier for the discriminance value for
* discerning small from large variances.
*/
- public static final OptionID ALPHA_ID = OptionID.getOrCreateOptionID("sod.alpha", "The multiplier for the discriminance value for discerning small from large variances.");
+ public static final OptionID ALPHA_ID = new OptionID("sod.alpha", "The multiplier for the discriminance value for discerning small from large variances.");
/**
* Parameter for the similarity function.
*/
- public static final OptionID SIM_ID = OptionID.getOrCreateOptionID("sod.similarity", "The similarity function used for the neighborhood set.");
+ public static final OptionID SIM_ID = new OptionID("sod.similarity", "The similarity function used for the neighborhood set.");
/**
* Holds the value of {@link #KNN_ID}.
@@ -155,20 +156,20 @@ public class SOD<V extends NumberVector<V, ?>, D extends NumberDistance<D, ?>> e
*/
public OutlierResult run(Relation<V> relation) {
SimilarityQuery<V, D> snnInstance = similarityFunction.instantiate(relation);
- FiniteProgress progress = logger.isVerbose() ? new FiniteProgress("Assigning Subspace Outlier Degree", relation.size(), logger) : null;
+ FiniteProgress progress = LOG.isVerbose() ? new FiniteProgress("Assigning Subspace Outlier Degree", relation.size(), LOG) : null;
WritableDataStore<SODModel<?>> sod_models = DataStoreUtil.makeStorage(relation.getDBIDs(), DataStoreFactory.HINT_STATIC, SODModel.class);
DoubleMinMax minmax = new DoubleMinMax();
- for(DBIDIter iter = relation.iterDBIDs(); iter.valid(); iter.advance()) {
- if(progress != null) {
- progress.incrementProcessed(logger);
+ for (DBIDIter iter = relation.iterDBIDs(); iter.valid(); iter.advance()) {
+ if (progress != null) {
+ progress.incrementProcessed(LOG);
}
DBIDs knnList = getNearestNeighbors(relation, snnInstance, iter);
SODModel<V> model = new SODModel<V>(relation, knnList, alpha, relation.get(iter));
sod_models.put(iter, model);
minmax.put(model.getSod());
}
- if(progress != null) {
- progress.ensureCompleted(logger);
+ if (progress != null) {
+ progress.ensureCompleted(LOG);
}
// combine results.
Relation<SODModel<?>> models = new MaterializedRelation<SODModel<?>>("Subspace Outlier Model", "sod-outlier", new SimpleTypeInformation<SODModel<?>>(SODModel.class), sod_models, relation.getDBIDs());
@@ -193,20 +194,19 @@ public class SOD<V extends NumberVector<V, ?>, D extends NumberDistance<D, ?>> e
*/
private DBIDs getNearestNeighbors(Relation<V> relation, SimilarityQuery<V, D> simQ, DBIDRef queryObject) {
// similarityFunction.getPreprocessor().getParameters();
- Heap<DoubleObjPair<DBID>> nearestNeighbors = new TiedTopBoundedHeap<DoubleObjPair<DBID>>(knn);
- for(DBIDIter iter = relation.iterDBIDs(); iter.valid(); iter.advance()) {
- if(!iter.sameDBID(queryObject)) {
+ Heap<DoubleDBIDPair> nearestNeighbors = new TiedTopBoundedHeap<DoubleDBIDPair>(knn);
+ for (DBIDIter iter = relation.iterDBIDs(); iter.valid(); iter.advance()) {
+ if (!DBIDUtil.equal(iter, queryObject)) {
double sim = simQ.similarity(queryObject, iter).doubleValue();
- if(sim > 0) {
- nearestNeighbors.add(new DoubleObjPair<DBID>(sim, iter.getDBID()));
+ if (sim > 0) {
+ nearestNeighbors.add(DBIDUtil.newPair(sim, iter));
}
}
}
// Collect DBIDs
ArrayModifiableDBIDs dbids = DBIDUtil.newArray(nearestNeighbors.size());
- while(nearestNeighbors.size() > 0) {
- final DoubleObjPair<DBID> next = nearestNeighbors.poll();
- dbids.add(next.second);
+ while (nearestNeighbors.size() > 0) {
+ dbids.add(nearestNeighbors.poll());
}
return dbids;
}
@@ -218,17 +218,17 @@ public class SOD<V extends NumberVector<V, ?>, D extends NumberDistance<D, ?>> e
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
- *
+ * SOD Model class
*
* @author Arthur Zimek
* @param <V> the type of DatabaseObjects handled by this Result
*/
// TODO: arthur comment
- public static class SODModel<V extends NumberVector<V, ?>> implements TextWriteable, Comparable<SODModel<?>> {
+ public static class SODModel<V extends NumberVector<?>> implements TextWriteable, Comparable<SODModel<?>> {
private double[] centerValues;
private V center;
@@ -250,61 +250,60 @@ public class SOD<V extends NumberVector<V, ?>, D extends NumberDistance<D, ?>> e
* @param queryObject Query object
*/
public SODModel(Relation<V> relation, DBIDs neighborhood, double alpha, V queryObject) {
- if(neighborhood.size() > 0) {
+ if (neighborhood.size() > 0) {
// TODO: store database link?
- centerValues = new double[DatabaseUtil.dimensionality(relation)];
+ centerValues = new double[RelationUtil.dimensionality(relation)];
variances = new double[centerValues.length];
- for(DBIDIter iter = neighborhood.iter(); iter.valid(); iter.advance()) {
+ for (DBIDIter iter = neighborhood.iter(); iter.valid(); iter.advance()) {
V databaseObject = relation.get(iter);
- for(int d = 0; d < centerValues.length; d++) {
- centerValues[d] += databaseObject.doubleValue(d + 1);
+ for (int d = 0; d < centerValues.length; d++) {
+ centerValues[d] += databaseObject.doubleValue(d);
}
}
- for(int d = 0; d < centerValues.length; d++) {
+ for (int d = 0; d < centerValues.length; d++) {
centerValues[d] /= neighborhood.size();
}
- for(DBIDIter iter = neighborhood.iter(); iter.valid(); iter.advance()) {
+ for (DBIDIter iter = neighborhood.iter(); iter.valid(); iter.advance()) {
V databaseObject = relation.get(iter);
- for(int d = 0; d < centerValues.length; d++) {
+ for (int d = 0; d < centerValues.length; d++) {
// distance
- double distance = centerValues[d] - databaseObject.doubleValue(d + 1);
+ double distance = centerValues[d] - databaseObject.doubleValue(d);
// variance
variances[d] += distance * distance;
}
}
expectationOfVariance = 0;
- for(int d = 0; d < variances.length; d++) {
+ for (int d = 0; d < variances.length; d++) {
variances[d] /= neighborhood.size();
expectationOfVariance += variances[d];
}
expectationOfVariance /= variances.length;
weightVector = new BitSet(variances.length);
- for(int d = 0; d < variances.length; d++) {
- if(variances[d] < alpha * expectationOfVariance) {
+ for (int d = 0; d < variances.length; d++) {
+ if (variances[d] < alpha * expectationOfVariance) {
weightVector.set(d, true);
}
}
- center = DatabaseUtil.assumeVectorField(relation).getFactory().newNumberVector(centerValues);
+ center = RelationUtil.getNumberVectorFactory(relation).newNumberVector(centerValues);
sod = subspaceOutlierDegree(queryObject, center, weightVector);
- }
- else {
+ } else {
center = queryObject;
sod = 0.0;
}
}
/**
- * Compute SOD score
+ * Compute SOD score.
*
- * @param queryObject
- * @param center
- * @param weightVector
- * @return sod value
+ * @param queryObject Query object
+ * @param center Center vector
+ * @param weightVector Weight vector
+ * @return sod score
*/
private double subspaceOutlierDegree(V queryObject, V center, BitSet weightVector) {
final SubspaceEuclideanDistanceFunction df = new SubspaceEuclideanDistanceFunction(weightVector);
final int card = weightVector.cardinality();
- if(card == 0) {
+ if (card == 0) {
return 0;
}
double distance = df.distance(queryObject, center).doubleValue();
@@ -352,7 +351,7 @@ public class SOD<V extends NumberVector<V, ?>, D extends NumberDistance<D, ?>> e
Relation<SODModel<?>> models;
/**
- * The IDs we are defined for
+ * The IDs we are defined for.
*/
DBIDs dbids;
@@ -436,7 +435,7 @@ public class SOD<V extends NumberVector<V, ?>, D extends NumberDistance<D, ?>> e
*
* @apiviz.exclude
*/
- public static class Parameterizer<V extends NumberVector<V, ?>, D extends NumberDistance<D, ?>> extends AbstractParameterizer {
+ public static class Parameterizer<V extends NumberVector<?>, D extends NumberDistance<D, ?>> extends AbstractParameterizer {
/**
* Holds the value of {@link #KNN_ID}.
*/
@@ -456,18 +455,20 @@ public class SOD<V extends NumberVector<V, ?>, D extends NumberDistance<D, ?>> e
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
final ObjectParameter<SimilarityFunction<V, D>> simP = new ObjectParameter<SimilarityFunction<V, D>>(SIM_ID, SimilarityFunction.class, SharedNearestNeighborSimilarityFunction.class);
- if(config.grab(simP)) {
+ if (config.grab(simP)) {
similarityFunction = simP.instantiateClass(config);
}
- final IntParameter knnP = new IntParameter(KNN_ID, new GreaterConstraint(0));
- if(config.grab(knnP)) {
+ final IntParameter knnP = new IntParameter(KNN_ID);
+ knnP.addConstraint(new GreaterConstraint(0));
+ if (config.grab(knnP)) {
knn = knnP.getValue();
}
- final DoubleParameter alphaP = new DoubleParameter(ALPHA_ID, new GreaterConstraint(0), 1.1);
- if(config.grab(alphaP)) {
- alpha = alphaP.getValue();
+ final DoubleParameter alphaP = new DoubleParameter(ALPHA_ID, 1.1);
+ alphaP.addConstraint(new GreaterConstraint(0));
+ if (config.grab(alphaP)) {
+ alpha = alphaP.doubleValue();
}
}
@@ -476,4 +477,4 @@ public class SOD<V extends NumberVector<V, ?>, D extends NumberDistance<D, ?>> e
return new SOD<V, D>(knn, alpha, similarityFunction);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/trivial/ByLabelOutlier.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/trivial/ByLabelOutlier.java
index 66a89cf5..ae95abfa 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/trivial/ByLabelOutlier.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/trivial/ByLabelOutlier.java
@@ -57,7 +57,7 @@ public class ByLabelOutlier extends AbstractAlgorithm<OutlierResult> implements
/**
* Our logger.
*/
- private static final Logging logger = Logging.getLogger(ByLabelOutlier.class);
+ private static final Logging LOG = Logging.getLogger(ByLabelOutlier.class);
/**
* The default pattern to use.
@@ -124,7 +124,7 @@ public class ByLabelOutlier extends AbstractAlgorithm<OutlierResult> implements
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -145,7 +145,7 @@ public class ByLabelOutlier extends AbstractAlgorithm<OutlierResult> implements
* Key: {@code -outlier.pattern}
* </p>
*/
- public static final OptionID OUTLIER_PATTERN_ID = OptionID.getOrCreateOptionID("outlier.pattern", "Label pattern to match outliers.");
+ public static final OptionID OUTLIER_PATTERN_ID = new OptionID("outlier.pattern", "Label pattern to match outliers.");
/**
* Stores the "outlier" class.
*/
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/trivial/TrivialAllOutlier.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/trivial/TrivialAllOutlier.java
index b50226f1..35a85d51 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/trivial/TrivialAllOutlier.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/trivial/TrivialAllOutlier.java
@@ -48,7 +48,7 @@ public class TrivialAllOutlier extends AbstractAlgorithm<OutlierResult> implemen
/**
* Our logger.
*/
- private static final Logging logger = Logging.getLogger(TrivialAllOutlier.class);
+ private static final Logging LOG = Logging.getLogger(TrivialAllOutlier.class);
/**
* Constructor.
@@ -80,6 +80,6 @@ public class TrivialAllOutlier extends AbstractAlgorithm<OutlierResult> implemen
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/trivial/TrivialGeneratedOutlier.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/trivial/TrivialGeneratedOutlier.java
index d1c2e076..e4c3861f 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/trivial/TrivialGeneratedOutlier.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/trivial/TrivialGeneratedOutlier.java
@@ -65,12 +65,12 @@ public class TrivialGeneratedOutlier extends AbstractAlgorithm<OutlierResult> im
/**
* Class logger
*/
- private static final Logging logger = Logging.getLogger(TrivialGeneratedOutlier.class);
+ private static final Logging LOG = Logging.getLogger(TrivialGeneratedOutlier.class);
/**
* Expected share of outliers
*/
- public static final OptionID EXPECT_ID = OptionID.getOrCreateOptionID("modeloutlier.expect", "Expected amount of outliers, for making the scores more intuitive.");
+ public static final OptionID EXPECT_ID = new OptionID("modeloutlier.expect", "Expected amount of outliers, for making the scores more intuitive.");
/**
* Expected share of outliers.
@@ -101,7 +101,7 @@ public class TrivialGeneratedOutlier extends AbstractAlgorithm<OutlierResult> im
@Override
public OutlierResult run(Database database) {
- Relation<NumberVector<?, ?>> vecs = database.getRelation(TypeUtil.NUMBER_VECTOR_FIELD);
+ Relation<NumberVector<?>> vecs = database.getRelation(TypeUtil.NUMBER_VECTOR_FIELD);
Relation<Model> models = database.getRelation(new SimpleTypeInformation<Model>(Model.class));
// Prefer a true class label
try {
@@ -122,7 +122,7 @@ public class TrivialGeneratedOutlier extends AbstractAlgorithm<OutlierResult> im
* @param labels Label relation
* @return Outlier result
*/
- public OutlierResult run(Relation<Model> models, Relation<NumberVector<?, ?>> vecs, Relation<?> labels) {
+ public OutlierResult run(Relation<Model> models, Relation<NumberVector<?>> vecs, Relation<?> labels) {
WritableDoubleDataStore scores = DataStoreUtil.makeDoubleStorage(models.getDBIDs(), DataStoreFactory.HINT_HOT);
// Adjustment constant
@@ -136,7 +136,7 @@ public class TrivialGeneratedOutlier extends AbstractAlgorithm<OutlierResult> im
}
}
if(generators.size() == 0) {
- logger.warning("No generator models found for dataset - all points will be considered outliers.");
+ LOG.warning("No generator models found for dataset - all points will be considered outliers.");
}
for(DBIDIter iditer = models.iterDBIDs(); iditer.valid(); iditer.advance()) {
@@ -179,7 +179,7 @@ public class TrivialGeneratedOutlier extends AbstractAlgorithm<OutlierResult> im
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/trivial/TrivialNoOutlier.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/trivial/TrivialNoOutlier.java
index 6d8e9f46..695ff112 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/trivial/TrivialNoOutlier.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/trivial/TrivialNoOutlier.java
@@ -48,7 +48,7 @@ public class TrivialNoOutlier extends AbstractAlgorithm<OutlierResult> implement
/**
* Our logger.
*/
- private static final Logging logger = Logging.getLogger(TrivialNoOutlier.class);
+ private static final Logging LOG = Logging.getLogger(TrivialNoOutlier.class);
/**
* Constructor.
@@ -80,6 +80,6 @@ public class TrivialNoOutlier extends AbstractAlgorithm<OutlierResult> implement
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/package-info.java b/src/de/lmu/ifi/dbs/elki/algorithm/package-info.java
index c18579f0..44eb2aba 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/package-info.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/package-info.java
@@ -5,6 +5,9 @@
* the {@link de.lmu.ifi.dbs.elki.algorithm.Algorithm}-Interface.
* Basic functions are already provided within {@link de.lmu.ifi.dbs.elki.algorithm.AbstractAlgorithm},
* see there for basic instructions of how to implement an algorithm suitable to the framework.
+ *
+ * @apiviz.exclude workflow.AlgorithmStep
+ * @apiviz.exclude database.query.knn.KNNQuery
*/
/*
This file is part of ELKI:
@@ -28,4 +31,4 @@ GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package de.lmu.ifi.dbs.elki.algorithm; \ No newline at end of file
+package de.lmu.ifi.dbs.elki.algorithm;
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/statistics/AddSingleScale.java b/src/de/lmu/ifi/dbs/elki/algorithm/statistics/AddSingleScale.java
index 481261b3..159fb691 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/statistics/AddSingleScale.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/statistics/AddSingleScale.java
@@ -27,30 +27,45 @@ import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.data.type.TypeInformation;
import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
import de.lmu.ifi.dbs.elki.database.Database;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.math.DoubleMinMax;
import de.lmu.ifi.dbs.elki.math.scales.LinearScale;
import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
import de.lmu.ifi.dbs.elki.result.ScalesResult;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
+import de.lmu.ifi.dbs.elki.utilities.datastructures.arraylike.ArrayLikeUtil;
import de.lmu.ifi.dbs.elki.utilities.documentation.Description;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.ListSizeConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.DoubleListParameter;
/**
- * Pseudo "algorith" that computes the global min/max for a relation across all
+ * Pseudo "algorithm" that computes the global min/max for a relation across all
* attributes.
*
+ * FIXME: this should become part of relation metadata.
+ *
* @author Erich Schubert
*/
@Description("Setup a scaling so that all dimensions are scaled equally in visualization.")
public class AddSingleScale implements Algorithm {
/**
+ * Minimum and maximum to use.
+ */
+ double[] minmax = null;
+
+ /**
* Constructor.
+ *
+ * @param minmax Minimum and maximum values
*/
- public AddSingleScale() {
+ public AddSingleScale(double[] minmax) {
super();
+ this.minmax = minmax;
}
@SuppressWarnings("unchecked")
@@ -58,7 +73,7 @@ public class AddSingleScale implements Algorithm {
public Result run(Database database) {
for(Relation<?> rel : database.getRelations()) {
if(TypeUtil.NUMBER_VECTOR_FIELD.isAssignableFromType(rel.getDataTypeInformation())) {
- ScalesResult res = run((Relation<? extends NumberVector<?, ?>>) rel);
+ ScalesResult res = run((Relation<? extends NumberVector<?>>) rel);
ResultUtil.addChildResult(rel, res);
}
}
@@ -71,20 +86,28 @@ public class AddSingleScale implements Algorithm {
* @param rel Relation
* @return Scales
*/
- private ScalesResult run(Relation<? extends NumberVector<?, ?>> rel) {
- final int dim = DatabaseUtil.dimensionality(rel);
- DoubleMinMax minmax = new DoubleMinMax();
- for(DBIDIter iditer = rel.iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
- NumberVector<?, ?> vec = rel.get(id);
- for(int d = 1; d <= dim; d++) {
- minmax.put(vec.doubleValue(d));
+ private ScalesResult run(Relation<? extends NumberVector<?>> rel) {
+ final int dim = RelationUtil.dimensionality(rel);
+ LinearScale[] scales = new LinearScale[dim];
+ if(minmax == null) {
+ DoubleMinMax mm = new DoubleMinMax();
+ for(DBIDIter iditer = rel.iterDBIDs(); iditer.valid(); iditer.advance()) {
+ NumberVector<?> vec = rel.get(iditer);
+ for(int d = 0; d < dim; d++) {
+ mm.put(vec.doubleValue(d));
+ }
+ }
+ LinearScale scale = new LinearScale(mm.getMin(), mm.getMax());
+ for(int i = 0; i < dim; i++) {
+ scales[i] = scale;
}
}
- LinearScale scale = new LinearScale(minmax.getMin(), minmax.getMax());
- LinearScale[] scales = new LinearScale[dim];
- for(int i = 0; i < dim; i++) {
- scales[i] = scale;
+ else {
+ // Use predefined.
+ LinearScale scale = new LinearScale(minmax[0], minmax[1]);
+ for(int i = 0; i < dim; i++) {
+ scales[i] = scale;
+ }
}
ScalesResult res = new ScalesResult(scales);
return res;
@@ -94,4 +117,39 @@ public class AddSingleScale implements Algorithm {
public TypeInformation[] getInputTypeRestriction() {
return TypeUtil.array(TypeUtil.NUMBER_VECTOR_FIELD);
}
+
+ /**
+ * Parameterization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public static class Parameterizer extends AbstractParameterizer {
+ /**
+ * Minimum and maximum to use.
+ */
+ double[] minmax = null;
+
+ /**
+ * Minimum and maximum values.
+ */
+ public static final OptionID MINMAX_ID = new OptionID("scales.minmax", "Forcibly set the scales to the given range.");
+
+ @Override
+ protected void makeOptions(Parameterization config) {
+ super.makeOptions(config);
+ DoubleListParameter minmaxP = new DoubleListParameter(MINMAX_ID);
+ minmaxP.setOptional(true);
+ minmaxP.addConstraint(new ListSizeConstraint(2));
+ if(config.grab(minmaxP)) {
+ minmax = ArrayLikeUtil.toPrimitiveDoubleArray(minmaxP.getValue());
+ }
+ }
+
+ @Override
+ protected AddSingleScale makeInstance() {
+ return new AddSingleScale(minmax);
+ }
+ }
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/statistics/AveragePrecisionAtK.java b/src/de/lmu/ifi/dbs/elki/algorithm/statistics/AveragePrecisionAtK.java
index f6f1d16f..e8165afc 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/statistics/AveragePrecisionAtK.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/statistics/AveragePrecisionAtK.java
@@ -25,7 +25,6 @@ package de.lmu.ifi.dbs.elki.algorithm.statistics;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Iterator;
import de.lmu.ifi.dbs.elki.algorithm.AbstractDistanceBasedAlgorithm;
import de.lmu.ifi.dbs.elki.data.DoubleVector;
@@ -33,16 +32,14 @@ import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.data.type.TypeInformation;
import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
import de.lmu.ifi.dbs.elki.database.Database;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.database.query.knn.KNNQuery;
-import de.lmu.ifi.dbs.elki.database.query.knn.KNNResult;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.logging.progress.FiniteProgress;
@@ -50,9 +47,9 @@ import de.lmu.ifi.dbs.elki.math.MeanVariance;
import de.lmu.ifi.dbs.elki.result.CollectionResult;
import de.lmu.ifi.dbs.elki.result.HistogramResult;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterConstraint;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterEqualConstraint;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.IntervalConstraint;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.IntervalConstraint.IntervalBoundary;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.LessEqualConstraint;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.DoubleParameter;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
@@ -70,7 +67,7 @@ public class AveragePrecisionAtK<V extends Object, D extends NumberDistance<D, ?
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(AveragePrecisionAtK.class);
+ private static final Logging LOG = Logging.getLogger(AveragePrecisionAtK.class);
/**
* The parameter k - the number of neighbors to retrieve.
@@ -91,6 +88,7 @@ public class AveragePrecisionAtK<V extends Object, D extends NumberDistance<D, ?
* Constructor.
*
* @param distanceFunction Distance function
+ * @param k K parameter
* @param sampling Sampling rate
* @param seed Random sampling seed (may be null)
*/
@@ -118,21 +116,18 @@ public class AveragePrecisionAtK<V extends Object, D extends NumberDistance<D, ?
ids = relation.getDBIDs();
}
- if(logger.isVerbose()) {
- logger.verbose("Processing points...");
+ if(LOG.isVerbose()) {
+ LOG.verbose("Processing points...");
}
- FiniteProgress objloop = logger.isVerbose() ? new FiniteProgress("Computing nearest neighbors", ids.size(), logger) : null;
+ FiniteProgress objloop = LOG.isVerbose() ? new FiniteProgress("Computing nearest neighbors", ids.size(), LOG) : null;
// sort neighbors
for(DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
- DBID id = iter.getDBID();
- KNNResult<D> knn = knnQuery.getKNNForDBID(id, k);
- Object label = lrelation.get(id);
-
- int positive = 0;
- Iterator<DistanceResultPair<D>> ri = knn.iterator();
- for(int i = 0; i < k && ri.hasNext(); i++) {
- DBID nid = ri.next().getDBID();
- Object olabel = lrelation.get(nid);
+ KNNResult<D> knn = knnQuery.getKNNForDBID(iter, k);
+ Object label = lrelation.get(iter);
+
+ int positive = 0, i = 0;
+ for (DBIDIter ri = knn.iter(); i < k && ri.valid(); ri.advance(), i++) {
+ Object olabel = lrelation.get(ri);
if(label == null) {
if(olabel == null) {
positive += 1;
@@ -147,11 +142,11 @@ public class AveragePrecisionAtK<V extends Object, D extends NumberDistance<D, ?
mvs[i].put(precision);
}
if(objloop != null) {
- objloop.incrementProcessed(logger);
+ objloop.incrementProcessed(LOG);
}
}
if(objloop != null) {
- objloop.ensureCompleted(logger);
+ objloop.ensureCompleted(LOG);
}
// Collections.sort(results);
@@ -171,7 +166,7 @@ public class AveragePrecisionAtK<V extends Object, D extends NumberDistance<D, ?
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -181,29 +176,29 @@ public class AveragePrecisionAtK<V extends Object, D extends NumberDistance<D, ?
*
* @apiviz.exclude
*/
- public static class Parameterizer<V extends NumberVector<V, ?>, D extends NumberDistance<D, ?>> extends AbstractDistanceBasedAlgorithm.Parameterizer<V, D> {
+ public static class Parameterizer<V extends NumberVector<?>, D extends NumberDistance<D, ?>> extends AbstractDistanceBasedAlgorithm.Parameterizer<V, D> {
/**
* Parameter k to compute the average precision at.
*/
- private static final OptionID K_ID = OptionID.getOrCreateOptionID("avep.k", "K to compute the average precision at.");
+ private static final OptionID K_ID = new OptionID("avep.k", "K to compute the average precision at.");
/**
- * Parameter to enable sampling
+ * Parameter to enable sampling.
*/
- public static final OptionID SAMPLING_ID = OptionID.getOrCreateOptionID("avep.sampling", "Relative amount of object to sample.");
+ public static final OptionID SAMPLING_ID = new OptionID("avep.sampling", "Relative amount of object to sample.");
/**
- * Parameter to control the sampling random seed
+ * Parameter to control the sampling random seed.
*/
- public static final OptionID SEED_ID = OptionID.getOrCreateOptionID("avep.sampling-seed", "Random seed for deterministic sampling.");
+ public static final OptionID SEED_ID = new OptionID("avep.sampling-seed", "Random seed for deterministic sampling.");
/**
- * Neighborhood size
+ * Neighborhood size.
*/
protected int k = 20;
/**
- * Relative amount of data to sample
+ * Relative amount of data to sample.
*/
protected double sampling = 1.0;
@@ -215,17 +210,22 @@ public class AveragePrecisionAtK<V extends Object, D extends NumberDistance<D, ?
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- final IntParameter kP = new IntParameter(K_ID, new GreaterEqualConstraint(2));
+ final IntParameter kP = new IntParameter(K_ID);
+ kP.addConstraint(new GreaterEqualConstraint(2));
if(config.grab(kP)) {
k = kP.getValue();
}
- final DoubleParameter samplingP = new DoubleParameter(SAMPLING_ID, new IntervalConstraint(0.0, IntervalBoundary.OPEN, 1.0, IntervalBoundary.CLOSE), true);
+ final DoubleParameter samplingP = new DoubleParameter(SAMPLING_ID);
+ samplingP.addConstraint(new GreaterConstraint(0.0));
+ samplingP.addConstraint(new LessEqualConstraint(1.0));
+ samplingP.setOptional(true);
if (config.grab(samplingP)) {
sampling = samplingP.getValue();
}
- final LongParameter seedP = new LongParameter(SEED_ID, true);
- if (config.grab(seedP)) {
- seed = seedP.getValue();
+ final LongParameter rndP = new LongParameter(SEED_ID);
+ rndP.setOptional(true);
+ if (config.grab(rndP)) {
+ seed = rndP.getValue();
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/statistics/DistanceStatisticsWithClasses.java b/src/de/lmu/ifi/dbs/elki/algorithm/statistics/DistanceStatisticsWithClasses.java
index d6ce6a15..ebf588b6 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/statistics/DistanceStatisticsWithClasses.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/statistics/DistanceStatisticsWithClasses.java
@@ -42,6 +42,7 @@ import de.lmu.ifi.dbs.elki.database.ids.ArrayModifiableDBIDs;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
+import de.lmu.ifi.dbs.elki.database.ids.DoubleDBIDPair;
import de.lmu.ifi.dbs.elki.database.ids.ModifiableDBIDs;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
@@ -52,10 +53,11 @@ import de.lmu.ifi.dbs.elki.logging.progress.FiniteProgress;
import de.lmu.ifi.dbs.elki.logging.progress.StepProgress;
import de.lmu.ifi.dbs.elki.math.DoubleMinMax;
import de.lmu.ifi.dbs.elki.math.MeanVariance;
-import de.lmu.ifi.dbs.elki.math.histograms.AggregatingHistogram;
-import de.lmu.ifi.dbs.elki.math.histograms.FlexiHistogram;
import de.lmu.ifi.dbs.elki.result.CollectionResult;
import de.lmu.ifi.dbs.elki.result.HistogramResult;
+import de.lmu.ifi.dbs.elki.utilities.datastructures.histogram.AbstractObjDynamicHistogram;
+import de.lmu.ifi.dbs.elki.utilities.datastructures.histogram.LongArrayStaticHistogram;
+import de.lmu.ifi.dbs.elki.utilities.datastructures.histogram.ObjHistogram;
import de.lmu.ifi.dbs.elki.utilities.documentation.Description;
import de.lmu.ifi.dbs.elki.utilities.documentation.Title;
import de.lmu.ifi.dbs.elki.utilities.exceptions.ExceptionMessages;
@@ -66,14 +68,13 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameteriz
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.Flag;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.Parameter;
-import de.lmu.ifi.dbs.elki.utilities.pairs.DoubleObjPair;
-import de.lmu.ifi.dbs.elki.utilities.pairs.Pair;
/**
* Algorithm to gather statistics over the distance distribution in the data
* set.
*
* @author Erich Schubert
+ *
* @param <O> Object type
* @param <D> Distance type
*/
@@ -84,22 +85,22 @@ public class DistanceStatisticsWithClasses<O, D extends NumberDistance<D, ?>> ex
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(DistanceStatisticsWithClasses.class);
+ private static final Logging LOG = Logging.getLogger(DistanceStatisticsWithClasses.class);
/**
* Flag to compute exact value range for binning.
*/
- public static final OptionID EXACT_ID = OptionID.getOrCreateOptionID("diststat.exact", "In a first pass, compute the exact minimum and maximum, at the cost of O(2*n*n) instead of O(n*n). The number of resulting bins is guaranteed to be as requested.");
+ public static final OptionID EXACT_ID = new OptionID("diststat.exact", "In a first pass, compute the exact minimum and maximum, at the cost of O(2*n*n) instead of O(n*n). The number of resulting bins is guaranteed to be as requested.");
/**
- * Flag to enable sampling
+ * Flag to enable sampling.
*/
- public static final OptionID SAMPLING_ID = OptionID.getOrCreateOptionID("diststat.sampling", "Enable sampling of O(n) size to determine the minimum and maximum distances approximately. The resulting number of bins can be larger than the given n.");
+ public static final OptionID SAMPLING_ID = new OptionID("diststat.sampling", "Enable sampling of O(n) size to determine the minimum and maximum distances approximately. The resulting number of bins can be larger than the given n.");
/**
* Option to configure the number of bins to use.
*/
- public static final OptionID HISTOGRAM_BINS_ID = OptionID.getOrCreateOptionID("diststat.bins", "Number of bins to use in the histogram. By default, it is only guaranteed to be within 1*n and 2*n of the given number.");
+ public static final OptionID HISTOGRAM_BINS_ID = new OptionID("diststat.bins", "Number of bins to use in the histogram. By default, it is only guaranteed to be within 1*n and 2*n of the given number.");
/**
* Number of bins to use in sampling.
@@ -107,12 +108,12 @@ public class DistanceStatisticsWithClasses<O, D extends NumberDistance<D, ?>> ex
private int numbin;
/**
- * Sampling
+ * Sampling flag.
*/
private boolean sampling = false;
/**
- * Sampling
+ * Compute exactly (slower).
*/
private boolean exact = false;
@@ -136,7 +137,7 @@ public class DistanceStatisticsWithClasses<O, D extends NumberDistance<D, ?>> ex
final Relation<O> relation = database.getRelation(getInputTypeRestriction()[0]);
final DistanceQuery<O, D> distFunc = database.getDistanceQuery(relation, getDistanceFunction());
- final StepProgress stepprog = logger.isVerbose() ? new StepProgress("Distance statistics", 2) : null;
+ final StepProgress stepprog = LOG.isVerbose() ? new StepProgress("Distance statistics", 2) : null;
// determine binning ranges.
DoubleMinMax gminmax = new DoubleMinMax();
@@ -157,43 +158,71 @@ public class DistanceStatisticsWithClasses<O, D extends NumberDistance<D, ?>> ex
MeanVariance momax = new MeanVariance();
MeanVariance modif = new MeanVariance();
// Histogram
- final AggregatingHistogram<Pair<Long, Long>, Pair<Long, Long>> histogram;
- if(stepprog != null) {
- stepprog.beginStep(1, "Prepare histogram.", logger);
+ final ObjHistogram<long[]> histogram;
+ if (stepprog != null) {
+ stepprog.beginStep(1, "Prepare histogram.", LOG);
}
- if(exact) {
+ if (exact) {
gminmax = exactMinMax(relation, distFunc);
- histogram = AggregatingHistogram.LongSumLongSumHistogram(numbin, gminmax.getMin(), gminmax.getMax());
- }
- else if(sampling) {
+ histogram = new LongArrayStaticHistogram(numbin, gminmax.getMin(), gminmax.getMax(), 2);
+ } else if (sampling) {
gminmax = sampleMinMax(relation, distFunc);
- histogram = AggregatingHistogram.LongSumLongSumHistogram(numbin, gminmax.getMin(), gminmax.getMax());
- }
- else {
- histogram = FlexiHistogram.LongSumLongSumHistogram(numbin);
+ histogram = new LongArrayStaticHistogram(numbin, gminmax.getMin(), gminmax.getMax(), 2);
+ } else {
+ histogram = new AbstractObjDynamicHistogram<long[]>(numbin) {
+ @Override
+ protected long[] downsample(Object[] data, int start, int end, int size) {
+ long[] ret = new long[2];
+ for (int i = start; i < end; i++) {
+ long[] existing = (long[]) data[i];
+ if (existing != null) {
+ for (int c = 0; c < 2; c++) {
+ ret[c] += existing[c];
+ }
+ }
+ }
+ return ret;
+ }
+
+ @Override
+ protected long[] aggregate(long[] first, long[] second) {
+ for (int c = 0; c < 2; c++) {
+ first[c] += second[c];
+ }
+ return first;
+ }
+
+ @Override
+ protected long[] cloneForCache(long[] data) {
+ return data.clone();
+ }
+
+ @Override
+ protected long[] makeObject() {
+ return new long[2];
+ }
+ };
}
- if(stepprog != null) {
- stepprog.beginStep(2, "Build histogram.", logger);
+ if (stepprog != null) {
+ stepprog.beginStep(2, "Build histogram.", LOG);
}
- final FiniteProgress progress = logger.isVerbose() ? new FiniteProgress("Distance computations", relation.size(), logger) : null;
+ final FiniteProgress progress = LOG.isVerbose() ? new FiniteProgress("Distance computations", relation.size(), LOG) : null;
// iterate per cluster
- final Pair<Long, Long> incFirst = new Pair<Long, Long>(1L, 0L);
- final Pair<Long, Long> incSecond = new Pair<Long, Long>(0L, 1L);
- for(Cluster<?> c1 : split) {
- for(DBIDIter iter = c1.getIDs().iter(); iter.valid(); iter.advance()) {
- DBID id1 = iter.getDBID();
+ final long[] incFirst = new long[] { 1L, 0L };
+ final long[] incSecond = new long[] { 0L, 1L };
+ for (Cluster<?> c1 : split) {
+ for (DBIDIter id1 = c1.getIDs().iter(); id1.valid(); id1.advance()) {
// in-cluster distances
DoubleMinMax iminmax = new DoubleMinMax();
- for(DBIDIter iter2 = c1.getIDs().iter(); iter2.valid(); iter2.advance()) {
- DBID id2 = iter2.getDBID();
+ for (DBIDIter iter2 = c1.getIDs().iter(); iter2.valid(); iter2.advance()) {
// skip the point itself.
- if(id1.sameDBID(id2)) {
+ if (DBIDUtil.equal(id1, iter2)) {
continue;
}
- double d = distFunc.distance(id1, id2).doubleValue();
+ double d = distFunc.distance(id1, iter2).doubleValue();
- histogram.aggregate(d, incFirst);
+ histogram.putData(d, incFirst);
iminmax.put(d);
}
@@ -207,19 +236,18 @@ public class DistanceStatisticsWithClasses<O, D extends NumberDistance<D, ?>> ex
// other-cluster distances
DoubleMinMax ominmax = new DoubleMinMax();
- for(Cluster<?> c2 : split) {
- if(c2 == c1) {
+ for (Cluster<?> c2 : split) {
+ if (c2 == c1) {
continue;
}
- for(DBIDIter iter2 = c2.getIDs().iter(); iter2.valid(); iter2.advance()) {
- DBID id2 = iter2.getDBID();
+ for (DBIDIter iter2 = c2.getIDs().iter(); iter2.valid(); iter2.advance()) {
// skip the point itself (shouldn't happen though)
- if(id1.sameDBID(id2)) {
+ if (DBIDUtil.equal(id1, iter2)) {
continue;
}
- double d = distFunc.distance(id1, id2).doubleValue();
+ double d = distFunc.distance(id1, iter2).doubleValue();
- histogram.aggregate(d, incSecond);
+ histogram.putData(d, incSecond);
ominmax.put(d);
}
@@ -231,38 +259,39 @@ public class DistanceStatisticsWithClasses<O, D extends NumberDistance<D, ?>> ex
// min/max
gominmax.put(ominmax.getMin());
gominmax.put(ominmax.getMax());
- if(progress != null) {
- progress.incrementProcessed(logger);
+ if (progress != null) {
+ progress.incrementProcessed(LOG);
}
}
}
- if(progress != null) {
- progress.ensureCompleted(logger);
+ if (progress != null) {
+ progress.ensureCompleted(LOG);
}
// Update values (only needed for sampling case).
gminmax.setFirst(Math.min(giminmax.getMin(), gominmax.getMin()));
gminmax.setSecond(Math.max(giminmax.getMax(), gominmax.getMax()));
- if(stepprog != null) {
- stepprog.setCompleted(logger);
+ if (stepprog != null) {
+ stepprog.setCompleted(LOG);
}
// count the number of samples we have in the data
long inum = 0;
long onum = 0;
- for(DoubleObjPair<Pair<Long, Long>> ppair : histogram) {
- inum += ppair.getSecond().getFirst();
- onum += ppair.getSecond().getSecond();
+ for (ObjHistogram.Iter<long[]> iter = histogram.iter(); iter.valid(); iter.advance()) {
+ inum += iter.getValue()[0];
+ onum += iter.getValue()[1];
}
long bnum = inum + onum;
Collection<DoubleVector> binstat = new ArrayList<DoubleVector>(numbin);
- for(DoubleObjPair<Pair<Long, Long>> ppair : histogram) {
- final double icof = (inum == 0) ? 0 : ((double) ppair.getSecond().getFirst()) / inum / histogram.getBinsize();
- final double icaf = ((double) ppair.getSecond().getFirst()) / bnum / histogram.getBinsize();
- final double ocof = (onum == 0) ? 0 : ((double) ppair.getSecond().getSecond()) / onum / histogram.getBinsize();
- final double ocaf = ((double) ppair.getSecond().getSecond()) / bnum / histogram.getBinsize();
- DoubleVector row = new DoubleVector(new double[] { ppair.first, icof, icaf, ocof, ocaf });
+ for (ObjHistogram.Iter<long[]> iter = histogram.iter(); iter.valid(); iter.advance()) {
+ final long[] value = iter.getValue();
+ final double icof = (inum == 0) ? 0 : ((double) value[0]) / inum / histogram.getBinsize();
+ final double icaf = ((double) value[0]) / bnum / histogram.getBinsize();
+ final double ocof = (onum == 0) ? 0 : ((double) value[1]) / onum / histogram.getBinsize();
+ final double ocaf = ((double) value[1]) / bnum / histogram.getBinsize();
+ DoubleVector row = new DoubleVector(new double[] { iter.getCenter(), icof, icaf, ocof, ocaf });
binstat.add(row);
}
HistogramResult<DoubleVector> result = new HistogramResult<DoubleVector>("Distance Histogram", "distance-histogram", binstat);
@@ -278,111 +307,121 @@ public class DistanceStatisticsWithClasses<O, D extends NumberDistance<D, ?>> ex
return result;
}
- private DoubleMinMax sampleMinMax(Relation<O> database, DistanceQuery<O, D> distFunc) {
- int size = database.size();
+ /**
+ * Estimate minimum and maximum via sampling.
+ *
+ * @param relation Relation to process
+ * @param distFunc Distance function to use
+ * @return Minimum and maximum
+ */
+ private DoubleMinMax sampleMinMax(Relation<O> relation, DistanceQuery<O, D> distFunc) {
+ int size = relation.size();
Random rnd = new Random();
// estimate minimum and maximum.
- int k = (int) Math.max(25, Math.pow(database.size(), 0.2));
- TreeSet<DoubleObjPair<DBID>> minhotset = new TreeSet<DoubleObjPair<DBID>>();
- TreeSet<DoubleObjPair<DBID>> maxhotset = new TreeSet<DoubleObjPair<DBID>>(Collections.reverseOrder());
+ int k = (int) Math.max(25, Math.pow(relation.size(), 0.2));
+ TreeSet<DoubleDBIDPair> minhotset = new TreeSet<DoubleDBIDPair>();
+ TreeSet<DoubleDBIDPair> maxhotset = new TreeSet<DoubleDBIDPair>(Collections.reverseOrder());
- int randomsize = (int) Math.max(25, Math.pow(database.size(), 0.2));
+ int randomsize = (int) Math.max(25, Math.pow(relation.size(), 0.2));
double rprob = ((double) randomsize) / size;
ArrayModifiableDBIDs randomset = DBIDUtil.newArray(randomsize);
- DBIDIter iter = database.iterDBIDs();
- if(!iter.valid()) {
+ DBIDIter iter = relation.iterDBIDs();
+ if (!iter.valid()) {
throw new IllegalStateException(ExceptionMessages.DATABASE_EMPTY);
}
- DBID firstid = iter.getDBID();
+ DBID firstid = DBIDUtil.deref(iter);
iter.advance();
- minhotset.add(new DoubleObjPair<DBID>(Double.MAX_VALUE, firstid));
- maxhotset.add(new DoubleObjPair<DBID>(Double.MIN_VALUE, firstid));
- while(iter.valid()) {
- DBID id1 = iter.getDBID();
- iter.advance();
+ minhotset.add(DBIDUtil.newPair(Double.MAX_VALUE, firstid));
+ maxhotset.add(DBIDUtil.newPair(Double.MIN_VALUE, firstid));
+ for (; iter.valid(); iter.advance()) {
// generate candidates for min distance.
- ArrayList<DoubleObjPair<DBID>> np = new ArrayList<DoubleObjPair<DBID>>(k * 2 + randomsize * 2);
- for(DoubleObjPair<DBID> pair : minhotset) {
- DBID id2 = pair.getSecond();
+ ArrayList<DoubleDBIDPair> np = new ArrayList<DoubleDBIDPair>(k * 2 + randomsize * 2);
+ for (DoubleDBIDPair pair : minhotset) {
// skip the object itself
- if(id1.compareTo(id2) == 0) {
+ if (DBIDUtil.equal(iter, pair)) {
continue;
}
- double d = distFunc.distance(id1, id2).doubleValue();
- np.add(new DoubleObjPair<DBID>(d, id1));
- np.add(new DoubleObjPair<DBID>(d, id2));
+ double d = distFunc.distance(iter, pair).doubleValue();
+ np.add(DBIDUtil.newPair(d, iter));
+ np.add(DBIDUtil.newPair(d, pair));
}
- for(DBIDIter iter2 = randomset.iter(); iter2.valid(); iter2.advance()) {
- DBID id2 = iter2.getDBID();
- double d = distFunc.distance(id1, id2).doubleValue();
- np.add(new DoubleObjPair<DBID>(d, id1));
- np.add(new DoubleObjPair<DBID>(d, id2));
+ for (DBIDIter iter2 = randomset.iter(); iter2.valid(); iter2.advance()) {
+ double d = distFunc.distance(iter, iter2).doubleValue();
+ np.add(DBIDUtil.newPair(d, iter));
+ np.add(DBIDUtil.newPair(d, iter2));
}
minhotset.addAll(np);
shrinkHeap(minhotset, k);
// generate candidates for max distance.
- ArrayList<DoubleObjPair<DBID>> np2 = new ArrayList<DoubleObjPair<DBID>>(k * 2 + randomsize * 2);
- for(DoubleObjPair<DBID> pair : minhotset) {
- DBID id2 = pair.getSecond();
+ ArrayList<DoubleDBIDPair> np2 = new ArrayList<DoubleDBIDPair>(k * 2 + randomsize * 2);
+ for (DoubleDBIDPair pair : minhotset) {
// skip the object itself
- if(id1.compareTo(id2) == 0) {
+ if (DBIDUtil.equal(iter, pair)) {
continue;
}
- double d = distFunc.distance(id1, id2).doubleValue();
- np2.add(new DoubleObjPair<DBID>(d, id1));
- np2.add(new DoubleObjPair<DBID>(d, id2));
+ double d = distFunc.distance(iter, pair).doubleValue();
+ np2.add(DBIDUtil.newPair(d, iter));
+ np2.add(DBIDUtil.newPair(d, pair));
}
- for(DBIDIter iter2 = randomset.iter(); iter2.valid(); iter2.advance()) {
- DBID id2 = iter2.getDBID();
- double d = distFunc.distance(id1, id2).doubleValue();
- np.add(new DoubleObjPair<DBID>(d, id1));
- np.add(new DoubleObjPair<DBID>(d, id2));
+ for (DBIDIter iter2 = randomset.iter(); iter2.valid(); iter2.advance()) {
+ double d = distFunc.distance(iter, iter2).doubleValue();
+ np.add(DBIDUtil.newPair(d, iter));
+ np.add(DBIDUtil.newPair(d, iter2));
}
maxhotset.addAll(np2);
shrinkHeap(maxhotset, k);
// update random set
- if(randomset.size() < randomsize) {
- randomset.add(id1);
- }
- else if(rnd.nextDouble() < rprob) {
- randomset.set((int) Math.floor(rnd.nextDouble() * randomsize), id1);
+ if (randomset.size() < randomsize) {
+ randomset.add(iter);
+ } else if (rnd.nextDouble() < rprob) {
+ randomset.set((int) Math.floor(rnd.nextDouble() * randomsize), iter);
}
}
- return new DoubleMinMax(minhotset.first().first, maxhotset.first().first);
+ return new DoubleMinMax(minhotset.first().doubleValue(), maxhotset.first().doubleValue());
}
- private DoubleMinMax exactMinMax(Relation<O> database, DistanceQuery<O, D> distFunc) {
+ /**
+ * Compute the exact maximum and minimum.
+ *
+ * @param relation Relation to process
+ * @param distFunc Distance function
+ * @return Exact maximum and minimum
+ */
+ private DoubleMinMax exactMinMax(Relation<O> relation, DistanceQuery<O, D> distFunc) {
DoubleMinMax minmax = new DoubleMinMax();
// find exact minimum and maximum first.
- for(DBIDIter iditer = database.iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id1 = iditer.getDBID();
- for(DBIDIter iditer2 = database.iterDBIDs(); iditer2.valid(); iditer2.advance()) {
- DBID id2 = iditer2.getDBID();
+ for (DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
+ for (DBIDIter iditer2 = relation.iterDBIDs(); iditer2.valid(); iditer2.advance()) {
// skip the point itself.
- if(id1.compareTo(id2) == 0) {
+ if (DBIDUtil.equal(iditer, iditer2)) {
continue;
}
- double d = distFunc.distance(id1, id2).doubleValue();
+ double d = distFunc.distance(iditer, iditer2).doubleValue();
minmax.put(d);
}
}
return minmax;
}
- private void shrinkHeap(TreeSet<DoubleObjPair<DBID>> hotset, int k) {
+ /**
+ * Shrink the heap of "hot" (extreme) items.
+ *
+ * @param hotset Set of hot items
+ * @param k target size
+ */
+ private static void shrinkHeap(TreeSet<DoubleDBIDPair> hotset, int k) {
// drop duplicates
ModifiableDBIDs seenids = DBIDUtil.newHashSet(2 * k);
int cnt = 0;
- for(Iterator<DoubleObjPair<DBID>> i = hotset.iterator(); i.hasNext();) {
- DoubleObjPair<DBID> p = i.next();
- if(cnt > k || seenids.contains(p.getSecond())) {
+ for (Iterator<DoubleDBIDPair> i = hotset.iterator(); i.hasNext();) {
+ DoubleDBIDPair p = i.next();
+ if (cnt > k || seenids.contains(p)) {
i.remove();
- }
- else {
- seenids.add(p.getSecond());
+ } else {
+ seenids.add(p);
cnt++;
}
}
@@ -395,7 +434,7 @@ public class DistanceStatisticsWithClasses<O, D extends NumberDistance<D, ?>> ex
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -412,36 +451,37 @@ public class DistanceStatisticsWithClasses<O, D extends NumberDistance<D, ?>> ex
private int numbin = 20;
/**
- * Sampling
+ * Sampling.
*/
private boolean sampling = false;
/**
- * Sampling
+ * Exactness flag.
*/
private boolean exact = false;
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- final IntParameter numbinP = new IntParameter(HISTOGRAM_BINS_ID, new GreaterEqualConstraint(2), 20);
- if(config.grab(numbinP)) {
+ final IntParameter numbinP = new IntParameter(HISTOGRAM_BINS_ID, 20);
+ numbinP.addConstraint(new GreaterEqualConstraint(2));
+ if (config.grab(numbinP)) {
numbin = numbinP.getValue();
}
- final Flag EXACT_FLAG = new Flag(EXACT_ID);
- if(config.grab(EXACT_FLAG)) {
- exact = EXACT_FLAG.getValue();
+ final Flag exactF = new Flag(EXACT_ID);
+ if (config.grab(exactF)) {
+ exact = exactF.getValue();
}
- final Flag SAMPLING_FLAG = new Flag(SAMPLING_ID);
- if(config.grab(SAMPLING_FLAG)) {
- sampling = SAMPLING_FLAG.getValue();
+ final Flag samplingF = new Flag(SAMPLING_ID);
+ if (config.grab(samplingF)) {
+ sampling = samplingF.getValue();
}
- ArrayList<Parameter<?, ?>> exclusive = new ArrayList<Parameter<?, ?>>();
- exclusive.add(EXACT_FLAG);
- exclusive.add(SAMPLING_FLAG);
+ ArrayList<Parameter<?>> exclusive = new ArrayList<Parameter<?>>();
+ exclusive.add(exactF);
+ exclusive.add(samplingF);
config.checkConstraint(new OnlyOneIsAllowedToBeSetGlobalConstraint(exclusive));
}
@@ -450,4 +490,4 @@ public class DistanceStatisticsWithClasses<O, D extends NumberDistance<D, ?>> ex
return new DistanceStatisticsWithClasses<O, D>(distanceFunction, numbin, exact, sampling);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/statistics/EvaluateRankingQuality.java b/src/de/lmu/ifi/dbs/elki/algorithm/statistics/EvaluateRankingQuality.java
index 353c1b02..1643d378 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/statistics/EvaluateRankingQuality.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/statistics/EvaluateRankingQuality.java
@@ -38,33 +38,31 @@ import de.lmu.ifi.dbs.elki.data.type.CombinedTypeInformation;
import de.lmu.ifi.dbs.elki.data.type.TypeInformation;
import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
import de.lmu.ifi.dbs.elki.database.Database;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
+import de.lmu.ifi.dbs.elki.database.ids.DoubleDBIDPair;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.database.query.knn.KNNQuery;
-import de.lmu.ifi.dbs.elki.database.query.knn.KNNResult;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance;
import de.lmu.ifi.dbs.elki.evaluation.roc.ROC;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.logging.progress.FiniteProgress;
import de.lmu.ifi.dbs.elki.math.MathUtil;
-import de.lmu.ifi.dbs.elki.math.MeanVariance;
-import de.lmu.ifi.dbs.elki.math.histograms.AggregatingHistogram;
+import de.lmu.ifi.dbs.elki.math.linearalgebra.CovarianceMatrix;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Matrix;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector;
import de.lmu.ifi.dbs.elki.result.CollectionResult;
import de.lmu.ifi.dbs.elki.result.HistogramResult;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
+import de.lmu.ifi.dbs.elki.utilities.datastructures.histogram.MeanVarianceStaticHistogram;
import de.lmu.ifi.dbs.elki.utilities.documentation.Description;
import de.lmu.ifi.dbs.elki.utilities.documentation.Title;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterEqualConstraint;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
-import de.lmu.ifi.dbs.elki.utilities.pairs.DoubleObjPair;
-import de.lmu.ifi.dbs.elki.utilities.pairs.FCPair;
/**
* Evaluate a distance function with respect to kNN queries. For each point, the
@@ -88,22 +86,22 @@ import de.lmu.ifi.dbs.elki.utilities.pairs.FCPair;
*/
@Title("Evaluate Ranking Quality")
@Description("Evaluates the effectiveness of a distance function via the obtained rankings.")
-public class EvaluateRankingQuality<V extends NumberVector<V, ?>, D extends NumberDistance<D, ?>> extends AbstractDistanceBasedAlgorithm<V, D, CollectionResult<DoubleVector>> {
+public class EvaluateRankingQuality<V extends NumberVector<?>, D extends NumberDistance<D, ?>> extends AbstractDistanceBasedAlgorithm<V, D, CollectionResult<DoubleVector>> {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(EvaluateRankingQuality.class);
+ private static final Logging LOG = Logging.getLogger(EvaluateRankingQuality.class);
/**
* Option to configure the number of bins to use.
*/
- public static final OptionID HISTOGRAM_BINS_ID = OptionID.getOrCreateOptionID("rankqual.bins", "Number of bins to use in the histogram");
+ public static final OptionID HISTOGRAM_BINS_ID = new OptionID("rankqual.bins", "Number of bins to use in the histogram");
/**
* Constructor.
*
- * @param distanceFunction
- * @param numbins
+ * @param distanceFunction Distance function
+ * @param numbins Number of bins
*/
public EvaluateRankingQuality(DistanceFunction<? super V, D> distanceFunction, int numbins) {
super(distanceFunction);
@@ -121,61 +119,60 @@ public class EvaluateRankingQuality<V extends NumberVector<V, ?>, D extends Numb
final DistanceQuery<V, D> distQuery = database.getDistanceQuery(relation, getDistanceFunction());
final KNNQuery<V, D> knnQuery = database.getKNNQuery(distQuery, relation.size());
- if(logger.isVerbose()) {
- logger.verbose("Preprocessing clusters...");
+ if (LOG.isVerbose()) {
+ LOG.verbose("Preprocessing clusters...");
}
// Cluster by labels
Collection<Cluster<Model>> split = (new ByLabelOrAllInOneClustering()).run(database).getAllClusters();
// Compute cluster averages and covariance matrix
- HashMap<Cluster<?>, V> averages = new HashMap<Cluster<?>, V>(split.size());
+ HashMap<Cluster<?>, Vector> averages = new HashMap<Cluster<?>, Vector>(split.size());
HashMap<Cluster<?>, Matrix> covmats = new HashMap<Cluster<?>, Matrix>(split.size());
- for(Cluster<?> clus : split) {
- averages.put(clus, DatabaseUtil.centroid(relation, clus.getIDs()));
- covmats.put(clus, DatabaseUtil.covarianceMatrix(relation, clus.getIDs()));
+ for (Cluster<?> clus : split) {
+ CovarianceMatrix covmat = CovarianceMatrix.make(relation, clus.getIDs());
+ averages.put(clus, covmat.getMeanVector());
+ covmats.put(clus, covmat.destroyToNaiveMatrix());
}
- AggregatingHistogram<MeanVariance, Double> hist = AggregatingHistogram.MeanVarianceHistogram(numbins, 0.0, 1.0);
+ MeanVarianceStaticHistogram hist = new MeanVarianceStaticHistogram(numbins, 0.0, 1.0);
- if(logger.isVerbose()) {
- logger.verbose("Processing points...");
+ if (LOG.isVerbose()) {
+ LOG.verbose("Processing points...");
}
- FiniteProgress rocloop = logger.isVerbose() ? new FiniteProgress("Computing ROC AUC values", relation.size(), logger) : null;
+ FiniteProgress rocloop = LOG.isVerbose() ? new FiniteProgress("Computing ROC AUC values", relation.size(), LOG) : null;
// sort neighbors
- for(Cluster<?> clus : split) {
- ArrayList<FCPair<Double, DBID>> cmem = new ArrayList<FCPair<Double, DBID>>(clus.size());
- Vector av = averages.get(clus).getColumnVector();
+ for (Cluster<?> clus : split) {
+ ArrayList<DoubleDBIDPair> cmem = new ArrayList<DoubleDBIDPair>(clus.size());
+ Vector av = averages.get(clus);
Matrix covm = covmats.get(clus);
- for(DBIDIter iter = clus.getIDs().iter(); iter.valid(); iter.advance()) {
- DBID i1 = iter.getDBID();
- Double d = MathUtil.mahalanobisDistance(covm, av.minus(relation.get(i1).getColumnVector()));
- cmem.add(new FCPair<Double, DBID>(d, i1));
+ for (DBIDIter iter = clus.getIDs().iter(); iter.valid(); iter.advance()) {
+ double d = MathUtil.mahalanobisDistance(covm, relation.get(iter).getColumnVector().minusEquals(av));
+ cmem.add(DBIDUtil.newPair(d, iter));
}
Collections.sort(cmem);
- for(int ind = 0; ind < cmem.size(); ind++) {
- DBID i1 = cmem.get(ind).getSecond();
- KNNResult<D> knn = knnQuery.getKNNForDBID(i1, relation.size());
+ for (int ind = 0; ind < cmem.size(); ind++) {
+ KNNResult<D> knn = knnQuery.getKNNForDBID(cmem.get(ind), relation.size());
double result = ROC.computeROCAUCDistanceResult(relation.size(), clus, knn);
- hist.aggregate(((double) ind) / clus.size(), result);
+ hist.put(((double) ind) / clus.size(), result);
- if(rocloop != null) {
- rocloop.incrementProcessed(logger);
+ if (rocloop != null) {
+ rocloop.incrementProcessed(LOG);
}
}
}
- if(rocloop != null) {
- rocloop.ensureCompleted(logger);
+ if (rocloop != null) {
+ rocloop.ensureCompleted(LOG);
}
// Collections.sort(results);
// Transform Histogram into a Double Vector array.
Collection<DoubleVector> res = new ArrayList<DoubleVector>(relation.size());
- for(DoubleObjPair<MeanVariance> pair : hist) {
- DoubleVector row = new DoubleVector(new double[] { pair.first, pair.getSecond().getCount(), pair.getSecond().getMean(), pair.getSecond().getSampleVariance() });
+ for (MeanVarianceStaticHistogram.Iter iter = hist.iter(); iter.valid(); iter.advance()) {
+ DoubleVector row = new DoubleVector(new double[] { iter.getCenter(), iter.getValue().getCount(), iter.getValue().getMean(), iter.getValue().getSampleVariance() });
res.add(row);
}
return new HistogramResult<DoubleVector>("Ranking Quality Histogram", "ranking-histogram", res);
@@ -188,7 +185,7 @@ public class EvaluateRankingQuality<V extends NumberVector<V, ?>, D extends Numb
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -198,14 +195,18 @@ public class EvaluateRankingQuality<V extends NumberVector<V, ?>, D extends Numb
*
* @apiviz.exclude
*/
- public static class Parameterizer<V extends NumberVector<V, ?>, D extends NumberDistance<D, ?>> extends AbstractDistanceBasedAlgorithm.Parameterizer<V, D> {
+ public static class Parameterizer<V extends NumberVector<?>, D extends NumberDistance<D, ?>> extends AbstractDistanceBasedAlgorithm.Parameterizer<V, D> {
+ /**
+ * Number of bins to use.
+ */
protected int numbins = 20;
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- final IntParameter param = new IntParameter(HISTOGRAM_BINS_ID, new GreaterEqualConstraint(2), 20);
- if(config.grab(param)) {
+ final IntParameter param = new IntParameter(HISTOGRAM_BINS_ID, 20);
+ param.addConstraint(new GreaterEqualConstraint(2));
+ if (config.grab(param)) {
numbins = param.getValue();
}
}
@@ -215,4 +216,4 @@ public class EvaluateRankingQuality<V extends NumberVector<V, ?>, D extends Numb
return new EvaluateRankingQuality<V, D>(distanceFunction, numbins);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/statistics/RankingQualityHistogram.java b/src/de/lmu/ifi/dbs/elki/algorithm/statistics/RankingQualityHistogram.java
index 4305bbca..6d8167a5 100644
--- a/src/de/lmu/ifi/dbs/elki/algorithm/statistics/RankingQualityHistogram.java
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/statistics/RankingQualityHistogram.java
@@ -34,28 +34,26 @@ import de.lmu.ifi.dbs.elki.data.model.Model;
import de.lmu.ifi.dbs.elki.data.type.TypeInformation;
import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
import de.lmu.ifi.dbs.elki.database.Database;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.database.query.knn.KNNQuery;
-import de.lmu.ifi.dbs.elki.database.query.knn.KNNResult;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance;
import de.lmu.ifi.dbs.elki.evaluation.roc.ROC;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.logging.progress.FiniteProgress;
import de.lmu.ifi.dbs.elki.math.MeanVariance;
-import de.lmu.ifi.dbs.elki.math.histograms.AggregatingHistogram;
import de.lmu.ifi.dbs.elki.result.CollectionResult;
import de.lmu.ifi.dbs.elki.result.HistogramResult;
+import de.lmu.ifi.dbs.elki.utilities.datastructures.histogram.DoubleStaticHistogram;
import de.lmu.ifi.dbs.elki.utilities.documentation.Description;
import de.lmu.ifi.dbs.elki.utilities.documentation.Title;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterEqualConstraint;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
-import de.lmu.ifi.dbs.elki.utilities.pairs.DoubleObjPair;
/**
* Evaluate a distance function with respect to kNN queries. For each point, the
@@ -77,12 +75,12 @@ public class RankingQualityHistogram<O, D extends NumberDistance<D, ?>> extends
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(RankingQualityHistogram.class);
+ private static final Logging LOG = Logging.getLogger(RankingQualityHistogram.class);
/**
* Option to configure the number of bins to use.
*/
- public static final OptionID HISTOGRAM_BINS_ID = OptionID.getOrCreateOptionID("rankqual.bins", "Number of bins to use in the histogram");
+ public static final OptionID HISTOGRAM_BINS_ID = new OptionID("rankqual.bins", "Number of bins to use in the histogram");
/**
* Number of bins to use.
@@ -100,47 +98,53 @@ public class RankingQualityHistogram<O, D extends NumberDistance<D, ?>> extends
this.numbins = numbins;
}
+ /**
+ * Process a database
+ *
+ * @param database Database to process
+ * @param relation Relation to process
+ * @return Histogram of ranking qualities
+ */
public HistogramResult<DoubleVector> run(Database database, Relation<O> relation) {
final DistanceQuery<O, D> distanceQuery = database.getDistanceQuery(relation, getDistanceFunction());
final KNNQuery<O, D> knnQuery = database.getKNNQuery(distanceQuery, relation.size());
- if(logger.isVerbose()) {
- logger.verbose("Preprocessing clusters...");
+ if (LOG.isVerbose()) {
+ LOG.verbose("Preprocessing clusters...");
}
// Cluster by labels
Collection<Cluster<Model>> split = (new ByLabelOrAllInOneClustering()).run(database).getAllClusters();
- AggregatingHistogram<Double, Double> hist = AggregatingHistogram.DoubleSumHistogram(numbins, 0.0, 1.0);
+ DoubleStaticHistogram hist = new DoubleStaticHistogram(numbins, 0.0, 1.0);
- if(logger.isVerbose()) {
- logger.verbose("Processing points...");
+ if (LOG.isVerbose()) {
+ LOG.verbose("Processing points...");
}
- FiniteProgress progress = logger.isVerbose() ? new FiniteProgress("Computing ROC AUC values", relation.size(), logger) : null;
+ FiniteProgress progress = LOG.isVerbose() ? new FiniteProgress("Computing ROC AUC values", relation.size(), LOG) : null;
MeanVariance mv = new MeanVariance();
// sort neighbors
- for(Cluster<?> clus : split) {
- for(DBIDIter iter = clus.getIDs().iter(); iter.valid(); iter.advance()) {
- DBID i1 = iter.getDBID();
- KNNResult<D> knn = knnQuery.getKNNForDBID(i1, relation.size());
+ for (Cluster<?> clus : split) {
+ for (DBIDIter iter = clus.getIDs().iter(); iter.valid(); iter.advance()) {
+ KNNResult<D> knn = knnQuery.getKNNForDBID(iter, relation.size());
double result = ROC.computeROCAUCDistanceResult(relation.size(), clus, knn);
mv.put(result);
- hist.aggregate(result, 1. / relation.size());
+ hist.increment(result, 1. / relation.size());
- if(progress != null) {
- progress.incrementProcessed(logger);
+ if (progress != null) {
+ progress.incrementProcessed(LOG);
}
}
}
- if(progress != null) {
- progress.ensureCompleted(logger);
+ if (progress != null) {
+ progress.ensureCompleted(LOG);
}
// Transform Histogram into a Double Vector array.
Collection<DoubleVector> res = new ArrayList<DoubleVector>(relation.size());
- for(DoubleObjPair<Double> pair : hist) {
- DoubleVector row = new DoubleVector(new double[] { pair.first, pair.getSecond() });
+ for (DoubleStaticHistogram.Iter iter = hist.iter(); iter.valid(); iter.advance()) {
+ DoubleVector row = new DoubleVector(new double[] { iter.getCenter(), iter.getValue() });
res.add(row);
}
HistogramResult<DoubleVector> result = new HistogramResult<DoubleVector>("Ranking Quality Histogram", "ranking-histogram", res);
@@ -152,10 +156,10 @@ public class RankingQualityHistogram<O, D extends NumberDistance<D, ?>> extends
public TypeInformation[] getInputTypeRestriction() {
return TypeUtil.array(getDistanceFunction().getInputTypeRestriction());
}
-
+
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -166,13 +170,17 @@ public class RankingQualityHistogram<O, D extends NumberDistance<D, ?>> extends
* @apiviz.exclude
*/
public static class Parameterizer<O, D extends NumberDistance<D, ?>> extends AbstractDistanceBasedAlgorithm.Parameterizer<O, D> {
+ /**
+ * Number of bins.
+ */
protected int numbins = 20;
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- final IntParameter param = new IntParameter(HISTOGRAM_BINS_ID, new GreaterEqualConstraint(2), 100);
- if(config.grab(param)) {
+ final IntParameter param = new IntParameter(HISTOGRAM_BINS_ID, 100);
+ param.addConstraint(new GreaterEqualConstraint(2));
+ if (config.grab(param)) {
numbins = param.getValue();
}
}
@@ -182,4 +190,4 @@ public class RankingQualityHistogram<O, D extends NumberDistance<D, ?>> extends
return new RankingQualityHistogram<O, D>(distanceFunction, numbins);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/application/AbstractApplication.java b/src/de/lmu/ifi/dbs/elki/application/AbstractApplication.java
index 2775c928..c0c166bc 100644
--- a/src/de/lmu/ifi/dbs/elki/application/AbstractApplication.java
+++ b/src/de/lmu/ifi/dbs/elki/application/AbstractApplication.java
@@ -59,13 +59,13 @@ import de.lmu.ifi.dbs.elki.utilities.pairs.Pair;
* @author Elke Achtert
* @author Erich Schubert
*
- * @apiviz.uses LoggingConfiguration
+ * @apiviz.uses LoggingConfiguration oneway
*/
public abstract class AbstractApplication implements Parameterizable {
/**
* We need a static logger in this class, for code used in "main" methods.
*/
- protected static Logging STATIC_LOGGER = Logging.getLogger(AbstractApplication.class);
+ private static final Logging LOG = Logging.getLogger(AbstractApplication.class);
/**
* The newline string according to system.
@@ -75,51 +75,13 @@ public abstract class AbstractApplication implements Parameterizable {
/**
* Information for citation and version.
*/
- public static final String INFORMATION = "ELKI Version 0.5.0~beta1 (2012, April)" + NEWLINE + NEWLINE + "published in:" + NEWLINE + "E. Achtert, S. Goldhofer, H.-P. Kriegel, E. Schubert, A. Zimek:" + NEWLINE + "Evaluation of Clusterings – Metrics and Visual Support." + NEWLINE + "In Proceedings of the 28th"+NEWLINE+"International Conference on Data Engineering (ICDE), Washington, DC, 2012." + NEWLINE;
-
- /**
- * Parameter that specifies the name of the output file.
- * <p>
- * Key: {@code -app.out}
- * </p>
- */
- public static final OptionID OUTPUT_ID = OptionID.getOrCreateOptionID("app.out", "");
-
- /**
- * Parameter that specifies the name of the input file.
- * <p>
- * Key: {@code -app.in}
- * </p>
- */
- public static final OptionID INPUT_ID = OptionID.getOrCreateOptionID("app.in", "");
-
- /**
- * Value of verbose flag.
- */
- private boolean verbose;
+ public static final String INFORMATION = "ELKI Version 0.5.5 (2012, December)" + NEWLINE + NEWLINE + "published in:" + NEWLINE + "E. Achtert, S. Goldhofer, H.-P. Kriegel, E. Schubert, A. Zimek:" + NEWLINE + "Evaluation of Clusterings – Metrics and Visual Support." + NEWLINE + "In Proceedings of the 28th" + NEWLINE + "International Conference on Data Engineering (ICDE), Washington, DC, 2012." + NEWLINE;
/**
* Constructor.
- *
- * @param verbose Verbose flag.
*/
- public AbstractApplication(boolean verbose) {
- if(verbose) {
- // Note: do not unset verbose if not --verbose - someone else might
- // have set it intentionally. So don't setVerbose(verbose)!
- LoggingConfiguration.setVerbose(true);
- }
- }
-
- /**
- * Returns whether verbose messages should be printed while executing the
- * application.
- *
- * @return whether verbose messages should be printed while executing the
- * application
- */
- public final boolean isVerbose() {
- return verbose;
+ public AbstractApplication() {
+ super();
}
/**
@@ -131,32 +93,32 @@ public abstract class AbstractApplication implements Parameterizable {
* @param args the arguments to run this application with
*/
public static void runCLIApplication(Class<?> cls, String[] args) {
- final Flag HELP_FLAG = new Flag(OptionID.HELP);
- final Flag HELP_LONG_FLAG = new Flag(OptionID.HELP_LONG);
- final ClassParameter<Object> DESCRIPTION_PARAM = new ClassParameter<Object>(OptionID.DESCRIPTION, Object.class, true);
- final StringParameter DEBUG_PARAM = new StringParameter(OptionID.DEBUG, true);
+ final Flag helpF = new Flag(OptionID.HELP);
+ final Flag helpLongF = new Flag(OptionID.HELP_LONG);
+ final ClassParameter<Object> descriptionP = new ClassParameter<Object>(OptionID.DESCRIPTION, Object.class, true);
+ final StringParameter debugP = new StringParameter(OptionID.DEBUG);
+ debugP.setOptional(true);
SerializedParameterization params = new SerializedParameterization(args);
try {
- params.grab(HELP_FLAG);
- params.grab(HELP_LONG_FLAG);
- params.grab(DESCRIPTION_PARAM);
- params.grab(DEBUG_PARAM);
- if(DESCRIPTION_PARAM.isDefined()) {
+ params.grab(helpF);
+ params.grab(helpLongF);
+ params.grab(descriptionP);
+ params.grab(debugP);
+ if (descriptionP.isDefined()) {
params.clearErrors();
- printDescription(DESCRIPTION_PARAM.getValue());
+ printDescription(descriptionP.getValue());
return;
}
// Fail silently on errors.
- if(params.getErrors().size() > 0) {
+ if (params.getErrors().size() > 0) {
params.logAndClearReportedErrors();
return;
}
- if(DEBUG_PARAM.isDefined()) {
- LoggingUtil.parseDebugParameter(DEBUG_PARAM);
+ if (debugP.isDefined()) {
+ LoggingUtil.parseDebugParameter(debugP);
}
- }
- catch(Exception e) {
+ } catch (Exception e) {
printErrorMessage(e);
return;
}
@@ -164,27 +126,24 @@ public abstract class AbstractApplication implements Parameterizable {
TrackParameters config = new TrackParameters(params);
AbstractApplication task = ClassGenericsUtil.tryInstantiate(AbstractApplication.class, cls, config);
- if((HELP_FLAG.isDefined() && HELP_FLAG.getValue()) || (HELP_LONG_FLAG.isDefined() && HELP_LONG_FLAG.getValue())) {
+ if ((helpF.isDefined() && helpF.getValue()) || (helpLongF.isDefined() && helpLongF.getValue())) {
LoggingConfiguration.setVerbose(true);
- STATIC_LOGGER.verbose(usage(config.getAllParameters()));
- }
- else {
+ LOG.verbose(usage(config.getAllParameters()));
+ } else {
params.logUnusedParameters();
- if(params.getErrors().size() > 0) {
+ if (params.getErrors().size() > 0) {
LoggingConfiguration.setVerbose(true);
- STATIC_LOGGER.verbose("The following configuration errors prevented execution:\n");
- for(ParameterException e : params.getErrors()) {
- STATIC_LOGGER.verbose(e.getMessage());
+ LOG.verbose("The following configuration errors prevented execution:\n");
+ for (ParameterException e : params.getErrors()) {
+ LOG.verbose(e.getMessage());
}
- STATIC_LOGGER.verbose("\n");
- STATIC_LOGGER.verbose("Stopping execution because of configuration errors.");
- }
- else {
+ LOG.verbose("\n");
+ LOG.verbose("Stopping execution because of configuration errors.");
+ } else {
task.run();
}
}
- }
- catch(Exception e) {
+ } catch (Exception e) {
printErrorMessage(e);
}
}
@@ -195,15 +154,15 @@ public abstract class AbstractApplication implements Parameterizable {
* @param options Options to show in usage.
* @return a usage message explaining all known options
*/
- public static String usage(Collection<Pair<Object, Parameter<?, ?>>> options) {
- StringBuffer usage = new StringBuffer();
+ public static String usage(Collection<Pair<Object, Parameter<?>>> options) {
+ StringBuilder usage = new StringBuilder();
usage.append(INFORMATION);
// Collect options
usage.append(NEWLINE).append("Parameters:").append(NEWLINE);
OptionUtil.formatForConsole(usage, FormatUtil.getConsoleWidth(), " ", options);
- // FIXME: re-add constraints!
+ // FIXME: re-add global constraints!
return usage.toString();
}
@@ -213,19 +172,16 @@ public abstract class AbstractApplication implements Parameterizable {
* @param e Error Exception.
*/
protected static void printErrorMessage(Exception e) {
- if(e instanceof AbortException) {
+ if (e instanceof AbortException) {
// ensure we actually show the message:
LoggingConfiguration.setVerbose(true);
- STATIC_LOGGER.verbose(e.getMessage());
- }
- else if(e instanceof UnspecifiedParameterException) {
- STATIC_LOGGER.error(e.getMessage());
- }
- else if(e instanceof ParameterException) {
- STATIC_LOGGER.error(e.getMessage());
- }
- else {
- STATIC_LOGGER.exception(e);
+ LOG.verbose(e.getMessage());
+ } else if (e instanceof UnspecifiedParameterException) {
+ LOG.error(e.getMessage());
+ } else if (e instanceof ParameterException) {
+ LOG.error(e.getMessage());
+ } else {
+ LOG.exception(e);
}
}
@@ -233,17 +189,17 @@ public abstract class AbstractApplication implements Parameterizable {
* Print the description for the given parameter
*/
private static void printDescription(Class<?> descriptionClass) {
- if(descriptionClass != null) {
+ if (descriptionClass != null) {
LoggingConfiguration.setVerbose(true);
- STATIC_LOGGER.verbose(OptionUtil.describeParameterizable(new StringBuffer(), descriptionClass, FormatUtil.getConsoleWidth(), " ").toString());
+ LOG.verbose(OptionUtil.describeParameterizable(new StringBuilder(), descriptionClass, FormatUtil.getConsoleWidth(), " ").toString());
}
}
/**
* Runs the application.
*
- * @throws de.lmu.ifi.dbs.elki.utilities.exceptions.UnableToComplyException if
- * an error occurs during running the application
+ * @throws UnableToComplyException if an error occurs during running the
+ * application
*/
public abstract void run() throws UnableToComplyException;
@@ -254,11 +210,22 @@ public abstract class AbstractApplication implements Parameterizable {
*
* @apiviz.exclude
*/
- public static abstract class Parameterizer extends AbstractParameterizer {
+ public abstract static class Parameterizer extends AbstractParameterizer {
+ /**
+ * Parameter that specifies the name of the output file.
+ * <p>
+ * Key: {@code -app.out}
+ * </p>
+ */
+ public static final OptionID OUTPUT_ID = new OptionID("app.out", "");
+
/**
- * Verbose flag
+ * Parameter that specifies the name of the input file.
+ * <p>
+ * Key: {@code -app.in}
+ * </p>
*/
- protected boolean verbose = false;
+ public static final OptionID INPUT_ID = new OptionID("app.in", "");
@Override
protected void makeOptions(Parameterization config) {
@@ -275,8 +242,8 @@ public abstract class AbstractApplication implements Parameterizable {
*/
protected void configVerbose(Parameterization config) {
final Flag verboseF = new Flag(OptionID.VERBOSE_FLAG);
- if(config.grab(verboseF)) {
- verbose = verboseF.getValue();
+ if (config.grab(verboseF)) {
+ LoggingConfiguration.setVerbose(verboseF.isTrue());
}
}
@@ -300,7 +267,7 @@ public abstract class AbstractApplication implements Parameterizable {
protected File getParameterOutputFile(Parameterization config, String description) {
final FileParameter outputP = new FileParameter(OUTPUT_ID, FileParameter.FileType.OUTPUT_FILE);
outputP.setShortDescription(description);
- if(config.grab(outputP)) {
+ if (config.grab(outputP)) {
return outputP.getValue();
}
return null;
@@ -326,7 +293,7 @@ public abstract class AbstractApplication implements Parameterizable {
protected File getParameterInputFile(Parameterization config, String description) {
final FileParameter inputP = new FileParameter(INPUT_ID, FileParameter.FileType.INPUT_FILE);
inputP.setShortDescription(description);
- if(config.grab(inputP)) {
+ if (config.grab(inputP)) {
return inputP.getValue();
}
return null;
@@ -335,4 +302,4 @@ public abstract class AbstractApplication implements Parameterizable {
@Override
protected abstract AbstractApplication makeInstance();
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/application/ComputeSingleColorHistogram.java b/src/de/lmu/ifi/dbs/elki/application/ComputeSingleColorHistogram.java
index e6fe5231..4ef544ab 100644
--- a/src/de/lmu/ifi/dbs/elki/application/ComputeSingleColorHistogram.java
+++ b/src/de/lmu/ifi/dbs/elki/application/ComputeSingleColorHistogram.java
@@ -50,7 +50,7 @@ public class ComputeSingleColorHistogram extends AbstractApplication {
* Key: {@code -colorhist.generator}
* </p>
*/
- public static OptionID COLORHIST_ID = OptionID.getOrCreateOptionID("colorhist.generator", "Class that is used to generate a color histogram.");
+ public static OptionID COLORHIST_ID = new OptionID("colorhist.generator", "Class that is used to generate a color histogram.");
/**
* Parameter that specifies the name of the input file.
@@ -58,7 +58,7 @@ public class ComputeSingleColorHistogram extends AbstractApplication {
* Key: {@code -colorhist.in}
* </p>
*/
- public static final OptionID INPUT_ID = OptionID.getOrCreateOptionID("colorhist.in", "Input image file for color histogram.");
+ public static final OptionID INPUT_ID = new OptionID("colorhist.in", "Input image file for color histogram.");
/**
* Parameter that specifies the name of the mask input file.
@@ -66,7 +66,7 @@ public class ComputeSingleColorHistogram extends AbstractApplication {
* Key: {@code -colorhist.mask}
* </p>
*/
- public static final OptionID MASK_ID = OptionID.getOrCreateOptionID("colorhist.mask", "Input mask image file.");
+ public static final OptionID MASK_ID = new OptionID("colorhist.mask", "Input mask image file.");
/**
* Class that will compute the actual histogram
@@ -86,13 +86,12 @@ public class ComputeSingleColorHistogram extends AbstractApplication {
/**
* Constructor.
*
- * @param verbose Verbose flag
* @param histogrammaker Class to compute histograms with
* @param inputFile Input file
* @param maskFile Mask file
*/
- public ComputeSingleColorHistogram(boolean verbose, ComputeColorHistogram histogrammaker, File inputFile, File maskFile) {
- super(verbose);
+ public ComputeSingleColorHistogram(ComputeColorHistogram histogrammaker, File inputFile, File maskFile) {
+ super();
this.histogrammaker = histogrammaker;
this.inputFile = inputFile;
this.maskFile = maskFile;
@@ -152,7 +151,7 @@ public class ComputeSingleColorHistogram extends AbstractApplication {
@Override
protected ComputeSingleColorHistogram makeInstance() {
- return new ComputeSingleColorHistogram(verbose, histogrammaker, inputFile, maskFile);
+ return new ComputeSingleColorHistogram(histogrammaker, inputFile, maskFile);
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/application/ConvertToBundleApplication.java b/src/de/lmu/ifi/dbs/elki/application/ConvertToBundleApplication.java
new file mode 100644
index 00000000..451fc572
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/application/ConvertToBundleApplication.java
@@ -0,0 +1,139 @@
+package de.lmu.ifi.dbs.elki.application;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.channels.FileChannel;
+
+import de.lmu.ifi.dbs.elki.datasource.DatabaseConnection;
+import de.lmu.ifi.dbs.elki.datasource.FileBasedDatabaseConnection;
+import de.lmu.ifi.dbs.elki.datasource.bundle.BundleWriter;
+import de.lmu.ifi.dbs.elki.datasource.bundle.MultipleObjectsBundle;
+import de.lmu.ifi.dbs.elki.datasource.bundle.StreamFromBundle;
+import de.lmu.ifi.dbs.elki.logging.Logging;
+import de.lmu.ifi.dbs.elki.utilities.exceptions.UnableToComplyException;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ObjectParameter;
+
+/**
+ * Convert an input file to the more efficient ELKI bundle format.
+ *
+ * @author Erich Schubert
+ */
+public class ConvertToBundleApplication extends AbstractApplication {
+ /**
+ * Logging class.
+ */
+ private static final Logging LOG = Logging.getLogger(ConvertToBundleApplication.class);
+
+ /**
+ * The data input step.
+ */
+ private DatabaseConnection input;
+
+ /**
+ * Output filename.
+ */
+ private File outfile;
+
+ /**
+ * Constructor.
+ *
+ * @param input Data source configuration
+ * @param outfile Output filename
+ */
+ public ConvertToBundleApplication(DatabaseConnection input, File outfile) {
+ super();
+ this.input = input;
+ this.outfile = outfile;
+ }
+
+ @Override
+ public void run() throws UnableToComplyException {
+ if (LOG.isVerbose()) {
+ LOG.verbose("Loading data.");
+ }
+ MultipleObjectsBundle bundle = input.loadData();
+ if (LOG.isVerbose()) {
+ LOG.verbose("Serializing to output file: " + outfile.toString());
+ }
+ // TODO: make configurable?
+ BundleWriter writer = new BundleWriter();
+ try {
+ FileOutputStream fos = new FileOutputStream(outfile);
+ FileChannel channel = fos.getChannel();
+ writer.writeBundleStream(new StreamFromBundle(bundle), channel);
+ channel.close();
+ fos.close();
+ } catch (IOException e) {
+ LOG.exception("IO Error", e);
+ }
+ }
+
+ /**
+ * Parameterization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public static class Parameterizer extends AbstractApplication.Parameterizer {
+ /**
+ * The data input step.
+ */
+ private DatabaseConnection input;
+
+ /**
+ * Output filename.
+ */
+ private File outfile;
+
+ @Override
+ protected void makeOptions(Parameterization config) {
+ super.makeOptions(config);
+ ObjectParameter<DatabaseConnection> inputP = new ObjectParameter<DatabaseConnection>(OptionID.DATABASE_CONNECTION, DatabaseConnection.class, FileBasedDatabaseConnection.class);
+ if (config.grab(inputP)) {
+ input = inputP.instantiateClass(config);
+ }
+ outfile = super.getParameterOutputFile(config, "File name to serialize the bundle to.");
+ }
+
+ @Override
+ protected ConvertToBundleApplication makeInstance() {
+ return new ConvertToBundleApplication(input, outfile);
+ }
+ }
+
+ /**
+ * Run command line application.
+ *
+ * @param args Command line parameters
+ */
+ public static void main(String[] args) {
+ runCLIApplication(ConvertToBundleApplication.class, args);
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/application/ELKILauncher.java b/src/de/lmu/ifi/dbs/elki/application/ELKILauncher.java
new file mode 100644
index 00000000..2cc84b2b
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/application/ELKILauncher.java
@@ -0,0 +1,76 @@
+package de.lmu.ifi.dbs.elki.application;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+
+import de.lmu.ifi.dbs.elki.gui.minigui.MiniGUI;
+import de.lmu.ifi.dbs.elki.logging.LoggingUtil;
+
+/**
+ * Class to launch ELKI.
+ *
+ * @author Erich Schubert
+ */
+public class ELKILauncher {
+ /**
+ * Application to run by default.
+ */
+ public static final Class<? extends AbstractApplication> DEFAULT_APPLICATION = MiniGUI.class;
+
+ /**
+ * Launch ELKI.
+ *
+ * @param args Command line arguments.
+ */
+ public static void main(String[] args) {
+ if (args.length > 0 && args[0].charAt(0) != '-') {
+ try {
+ Class<?> cls = Class.forName(args[0]);
+ Method m = cls.getMethod("main", String[].class);
+ Object a = Arrays.copyOfRange(args, 1, args.length);
+ m.invoke(null, a);
+ return;
+ } catch (Exception e) {
+ // Ignore
+ }
+ try {
+ Class<?> cls = Class.forName(AbstractApplication.class.getPackage().getName() + '.' + args[0]);
+ Method m = cls.getMethod("main", String[].class);
+ Object a = Arrays.copyOfRange(args, 1, args.length);
+ m.invoke(null, a);
+ return;
+ } catch (Exception e) {
+ // Ignore
+ }
+ }
+ try {
+ Method m = DEFAULT_APPLICATION.getMethod("main", String[].class);
+ m.invoke(null, (Object) args);
+ } catch (Exception e) {
+ LoggingUtil.exception(e);
+ }
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/application/GeneratorByModel.xsd b/src/de/lmu/ifi/dbs/elki/application/GeneratorByModel.xsd
index 4e09ddb5..9f54a3a0 100644
--- a/src/de/lmu/ifi/dbs/elki/application/GeneratorByModel.xsd
+++ b/src/de/lmu/ifi/dbs/elki/application/GeneratorByModel.xsd
@@ -21,6 +21,7 @@
<xs:element ref="uniform" />
<xs:element ref="normal" />
<xs:element ref="gamma" />
+ <xs:element ref="halton" />
</xs:choice>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element ref="translate" />
@@ -55,6 +56,13 @@
</xs:complexType>
</xs:element>
+ <xs:element name="halton">
+ <xs:complexType>
+ <xs:attribute name="max" type="xs:decimal" use="optional" />
+ <xs:attribute name="min" type="xs:decimal" use="optional" />
+ </xs:complexType>
+ </xs:element>
+
<xs:element name="clip">
<xs:complexType>
<xs:attribute name="max" type="xs:string" use="required" />
diff --git a/src/de/lmu/ifi/dbs/elki/application/GeneratorXMLSpec.java b/src/de/lmu/ifi/dbs/elki/application/GeneratorXMLSpec.java
index 19207c5c..ecb7ed4c 100644
--- a/src/de/lmu/ifi/dbs/elki/application/GeneratorXMLSpec.java
+++ b/src/de/lmu/ifi/dbs/elki/application/GeneratorXMLSpec.java
@@ -61,12 +61,12 @@ public class GeneratorXMLSpec extends AbstractApplication {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(GeneratorXMLSpec.class);
+ private static final Logging LOG = Logging.getLogger(GeneratorXMLSpec.class);
/**
* Line separator for output
*/
- public final static String LINE_SEPARATOR = System.getProperty("line.separator");
+ public static final String LINE_SEPARATOR = System.getProperty("line.separator");
/**
* Output file.
@@ -81,12 +81,11 @@ public class GeneratorXMLSpec extends AbstractApplication {
/**
* Constructor.
*
- * @param verbose Verbose flag
* @param output Output file
* @param generator GeneratorXMLDatabaseConnection
*/
- public GeneratorXMLSpec(boolean verbose, File output, GeneratorXMLDatabaseConnection generator) {
- super(verbose);
+ public GeneratorXMLSpec(File output, GeneratorXMLDatabaseConnection generator) {
+ super();
this.outputFile = output;
this.generator = generator;
}
@@ -97,13 +96,13 @@ public class GeneratorXMLSpec extends AbstractApplication {
@Override
public void run() throws UnableToComplyException {
MultipleObjectsBundle data = generator.loadData();
- if(logger.isVerbose()) {
- logger.verbose("Writing output ...");
+ if(LOG.isVerbose()) {
+ LOG.verbose("Writing output ...");
}
try {
if(outputFile.exists()) {
- if(logger.isVerbose()) {
- logger.verbose("The file " + outputFile + " already exists, " + "the generator result will be appended.");
+ if(LOG.isVerbose()) {
+ LOG.verbose("The file " + outputFile + " already exists, " + "the generator result will be appended.");
}
}
@@ -119,8 +118,8 @@ public class GeneratorXMLSpec extends AbstractApplication {
catch(IOException e) {
throw new UnableToComplyException(e);
}
- if(logger.isVerbose()) {
- logger.verbose("Done.");
+ if(LOG.isVerbose()) {
+ LOG.verbose("Done.");
}
}
@@ -242,7 +241,7 @@ public class GeneratorXMLSpec extends AbstractApplication {
@Override
protected GeneratorXMLSpec makeInstance() {
- return new GeneratorXMLSpec(verbose, outputFile, generator);
+ return new GeneratorXMLSpec(outputFile, generator);
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/application/KDDCLIApplication.java b/src/de/lmu/ifi/dbs/elki/application/KDDCLIApplication.java
index 9184df58..5e0e0ef1 100644
--- a/src/de/lmu/ifi/dbs/elki/application/KDDCLIApplication.java
+++ b/src/de/lmu/ifi/dbs/elki/application/KDDCLIApplication.java
@@ -48,11 +48,10 @@ public class KDDCLIApplication extends AbstractApplication {
/**
* Constructor.
*
- * @param verbose Verbose flag
* @param task Task to run
*/
- public KDDCLIApplication(boolean verbose, KDDTask task) {
- super(verbose);
+ public KDDCLIApplication(KDDTask task) {
+ super();
this.task = task;
}
@@ -82,7 +81,7 @@ public class KDDCLIApplication extends AbstractApplication {
@Override
protected KDDCLIApplication makeInstance() {
- return new KDDCLIApplication(verbose, task);
+ return new KDDCLIApplication(task);
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/application/cache/CacheDoubleDistanceInOnDiskMatrix.java b/src/de/lmu/ifi/dbs/elki/application/cache/CacheDoubleDistanceInOnDiskMatrix.java
index 19a22699..07907313 100644
--- a/src/de/lmu/ifi/dbs/elki/application/cache/CacheDoubleDistanceInOnDiskMatrix.java
+++ b/src/de/lmu/ifi/dbs/elki/application/cache/CacheDoubleDistanceInOnDiskMatrix.java
@@ -29,8 +29,9 @@ import java.io.IOException;
import de.lmu.ifi.dbs.elki.application.AbstractApplication;
import de.lmu.ifi.dbs.elki.database.Database;
import de.lmu.ifi.dbs.elki.database.StaticArrayDatabase;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDFactory;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
@@ -60,7 +61,7 @@ public class CacheDoubleDistanceInOnDiskMatrix<O, D extends NumberDistance<D, ?>
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(CacheDoubleDistanceInOnDiskMatrix.class);
+ private static final Logging LOG = Logging.getLogger(CacheDoubleDistanceInOnDiskMatrix.class);
/**
* Parameter that specifies the name of the directory to be re-parsed.
@@ -68,7 +69,7 @@ public class CacheDoubleDistanceInOnDiskMatrix<O, D extends NumberDistance<D, ?>
* Key: {@code -loader.diskcache}
* </p>
*/
- public static final OptionID CACHE_ID = OptionID.getOrCreateOptionID("loader.diskcache", "File name of the disk cache to create.");
+ public static final OptionID CACHE_ID = new OptionID("loader.diskcache", "File name of the disk cache to create.");
/**
* Parameter that specifies the name of the directory to be re-parsed.
@@ -76,7 +77,7 @@ public class CacheDoubleDistanceInOnDiskMatrix<O, D extends NumberDistance<D, ?>
* Key: {@code -loader.distance}
* </p>
*/
- public static final OptionID DISTANCE_ID = OptionID.getOrCreateOptionID("loader.distance", "Distance function to cache.");
+ public static final OptionID DISTANCE_ID = new OptionID("loader.distance", "Distance function to cache.");
/**
* Debug flag, to double-check all write operations.
@@ -101,13 +102,12 @@ public class CacheDoubleDistanceInOnDiskMatrix<O, D extends NumberDistance<D, ?>
/**
* Constructor.
*
- * @param verbose Verbose flag
* @param database Database
* @param distance Distance function
* @param out Matrix output file
*/
- public CacheDoubleDistanceInOnDiskMatrix(boolean verbose, Database database, DistanceFunction<O, D> distance, File out) {
- super(verbose);
+ public CacheDoubleDistanceInOnDiskMatrix(Database database, DistanceFunction<O, D> distance, File out) {
+ super();
this.database = database;
this.distance = distance;
this.out = out;
@@ -121,9 +121,9 @@ public class CacheDoubleDistanceInOnDiskMatrix<O, D extends NumberDistance<D, ?>
int matrixsize = 0;
for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
- matrixsize = Math.max(matrixsize, id.getIntegerID() + 1);
- if(id.getIntegerID() < 0) {
+ int intid = DBIDUtil.asInteger(iditer);
+ matrixsize = Math.max(matrixsize, intid + 1);
+ if(intid < 0) {
throw new AbortException("OnDiskMatrixCache does not allow negative DBIDs.");
}
}
@@ -136,23 +136,21 @@ public class CacheDoubleDistanceInOnDiskMatrix<O, D extends NumberDistance<D, ?>
throw new AbortException("Error creating output matrix.", e);
}
- for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id1 = iditer.getDBID();
- for(DBIDIter iditer2 = relation.iterDBIDs(); iditer2.valid(); iditer2.advance()) {
- DBID id2 = iditer2.getDBID();
- if(id2.getIntegerID() >= id1.getIntegerID()) {
+ for(DBIDIter id1 = relation.iterDBIDs(); id1.valid(); id1.advance()) {
+ for(DBIDIter id2 = relation.iterDBIDs(); id2.valid(); id2.advance()) {
+ if(DBIDUtil.asInteger(id2) >= DBIDUtil.asInteger(id1)) {
double d = distanceQuery.distance(id1, id2).doubleValue();
if(debugExtraCheckSymmetry) {
double d2 = distanceQuery.distance(id2, id1).doubleValue();
if(Math.abs(d - d2) > 0.0000001) {
- logger.warning("Distance function doesn't appear to be symmetric!");
+ LOG.warning("Distance function doesn't appear to be symmetric!");
}
}
try {
- matrix.getRecordBuffer(id1.getIntegerID(), id2.getIntegerID()).putDouble(d);
+ matrix.getRecordBuffer(DBIDUtil.asInteger(id1), DBIDUtil.asInteger(id2)).putDouble(d);
}
catch(IOException e) {
- throw new AbortException("Error writing distance record " + id1 + "," + id2 + " to matrix.", e);
+ throw new AbortException("Error writing distance record " + DBIDFactory.FACTORY.toString(id1) + "," + DBIDFactory.FACTORY.toString(id2) + " to matrix.", e);
}
}
}
@@ -205,7 +203,7 @@ public class CacheDoubleDistanceInOnDiskMatrix<O, D extends NumberDistance<D, ?>
@Override
protected CacheDoubleDistanceInOnDiskMatrix<O, D> makeInstance() {
- return new CacheDoubleDistanceInOnDiskMatrix<O, D>(verbose, database, distance, out);
+ return new CacheDoubleDistanceInOnDiskMatrix<O, D>(database, distance, out);
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/application/cache/CacheFloatDistanceInOnDiskMatrix.java b/src/de/lmu/ifi/dbs/elki/application/cache/CacheFloatDistanceInOnDiskMatrix.java
index 78a64442..144b4b70 100644
--- a/src/de/lmu/ifi/dbs/elki/application/cache/CacheFloatDistanceInOnDiskMatrix.java
+++ b/src/de/lmu/ifi/dbs/elki/application/cache/CacheFloatDistanceInOnDiskMatrix.java
@@ -29,8 +29,8 @@ import java.io.IOException;
import de.lmu.ifi.dbs.elki.application.AbstractApplication;
import de.lmu.ifi.dbs.elki.database.Database;
import de.lmu.ifi.dbs.elki.database.StaticArrayDatabase;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
@@ -60,7 +60,7 @@ public class CacheFloatDistanceInOnDiskMatrix<O, D extends NumberDistance<D, ?>>
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(CacheFloatDistanceInOnDiskMatrix.class);
+ private static final Logging LOG = Logging.getLogger(CacheFloatDistanceInOnDiskMatrix.class);
/**
* Parameter that specifies the name of the directory to be re-parsed.
@@ -68,7 +68,7 @@ public class CacheFloatDistanceInOnDiskMatrix<O, D extends NumberDistance<D, ?>>
* Key: {@code -loader.diskcache}
* </p>
*/
- public static final OptionID CACHE_ID = OptionID.getOrCreateOptionID("loader.diskcache", "File name of the disk cache to create.");
+ public static final OptionID CACHE_ID = new OptionID("loader.diskcache", "File name of the disk cache to create.");
/**
* Parameter that specifies the name of the directory to be re-parsed.
@@ -76,7 +76,7 @@ public class CacheFloatDistanceInOnDiskMatrix<O, D extends NumberDistance<D, ?>>
* Key: {@code -loader.distance}
* </p>
*/
- public static final OptionID DISTANCE_ID = OptionID.getOrCreateOptionID("loader.distance", "Distance function to cache.");
+ public static final OptionID DISTANCE_ID = new OptionID("loader.distance", "Distance function to cache.");
/**
* Debug flag, to double-check all write operations.
@@ -106,13 +106,12 @@ public class CacheFloatDistanceInOnDiskMatrix<O, D extends NumberDistance<D, ?>>
/**
* Constructor.
*
- * @param verbose Verbose flag
* @param database Database
* @param distance Distance function
* @param out Matrix output file
*/
- public CacheFloatDistanceInOnDiskMatrix(boolean verbose, Database database, DistanceFunction<O, D> distance, File out) {
- super(verbose);
+ public CacheFloatDistanceInOnDiskMatrix(Database database, DistanceFunction<O, D> distance, File out) {
+ super();
this.database = database;
this.distance = distance;
this.out = out;
@@ -126,9 +125,9 @@ public class CacheFloatDistanceInOnDiskMatrix<O, D extends NumberDistance<D, ?>>
int matrixsize = 0;
for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
- matrixsize = Math.max(matrixsize, id.getIntegerID() + 1);
- if(id.getIntegerID() < 0) {
+ final int intid = DBIDUtil.asInteger(iditer);
+ matrixsize = Math.max(matrixsize, intid + 1);
+ if(intid < 0) {
throw new AbortException("OnDiskMatrixCache does not allow negative DBIDs.");
}
}
@@ -141,20 +140,18 @@ public class CacheFloatDistanceInOnDiskMatrix<O, D extends NumberDistance<D, ?>>
throw new AbortException("Error creating output matrix.", e);
}
- for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id1 = iditer.getDBID();
- for(DBIDIter iditer2 = relation.iterDBIDs(); iditer2.valid(); iditer2.advance()) {
- DBID id2 = iditer2.getDBID();
- if(id2.getIntegerID() >= id1.getIntegerID()) {
+ for(DBIDIter id1 = relation.iterDBIDs(); id1.valid(); id1.advance()) {
+ for(DBIDIter id2 = relation.iterDBIDs(); id2.valid(); id2.advance()) {
+ if(DBIDUtil.asInteger(id2) >= DBIDUtil.asInteger(id1)) {
float d = distanceQuery.distance(id1, id2).floatValue();
if(debugExtraCheckSymmetry) {
float d2 = distanceQuery.distance(id2, id1).floatValue();
if(Math.abs(d - d2) > 0.0000001) {
- logger.warning("Distance function doesn't appear to be symmetric!");
+ LOG.warning("Distance function doesn't appear to be symmetric!");
}
}
try {
- matrix.getRecordBuffer(id1.getIntegerID(), id2.getIntegerID()).putFloat(d);
+ matrix.getRecordBuffer(DBIDUtil.asInteger(id1), DBIDUtil.asInteger(id2)).putFloat(d);
}
catch(IOException e) {
throw new AbortException("Error writing distance record " + id1 + "," + id2 + " to matrix.", e);
@@ -210,7 +207,7 @@ public class CacheFloatDistanceInOnDiskMatrix<O, D extends NumberDistance<D, ?>>
@Override
protected CacheFloatDistanceInOnDiskMatrix<O, D> makeInstance() {
- return new CacheFloatDistanceInOnDiskMatrix<O, D>(verbose, database, distance, out);
+ return new CacheFloatDistanceInOnDiskMatrix<O, D>(database, distance, out);
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/application/geo/VisualizeGeodesicDistances.java b/src/de/lmu/ifi/dbs/elki/application/geo/VisualizeGeodesicDistances.java
new file mode 100644
index 00000000..bdc0f39a
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/application/geo/VisualizeGeodesicDistances.java
@@ -0,0 +1,268 @@
+package de.lmu.ifi.dbs.elki.application.geo;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+
+import javax.imageio.ImageIO;
+
+import de.lmu.ifi.dbs.elki.application.AbstractApplication;
+import de.lmu.ifi.dbs.elki.data.DoubleVector;
+import de.lmu.ifi.dbs.elki.data.ModifiableHyperBoundingBox;
+import de.lmu.ifi.dbs.elki.logging.Logging;
+import de.lmu.ifi.dbs.elki.math.GeoUtil;
+import de.lmu.ifi.dbs.elki.utilities.exceptions.AbortException;
+import de.lmu.ifi.dbs.elki.utilities.exceptions.UnableToComplyException;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterEqualConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.EnumParameter;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
+
+/**
+ * Visualization function for Cross-track distance function
+ *
+ * TODO: make resolution configurable.
+ *
+ * TODO: make origin point / rectangle configurable.
+ *
+ * @author Niels Dörre
+ * @author Erich Schubert
+ */
+public class VisualizeGeodesicDistances extends AbstractApplication {
+ /**
+ * Get a logger for this class.
+ */
+ private final static Logging LOG = Logging.getLogger(VisualizeGeodesicDistances.class);
+
+ /**
+ * Visualization mode.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public static enum Mode {
+ /** Cross track distance */
+ CTD,
+ /** Along track distance */
+ ATD,
+ /** Mindist */
+ MINDIST
+ }
+
+ /**
+ * Holds the file to print results to.
+ */
+ private File out;
+
+ /**
+ * Image size.
+ */
+ final int width = 2000, height = 1000;
+
+ /**
+ * Number of steps for shades.
+ */
+ protected int steps = 10;
+
+ /**
+ * Visualization mode
+ */
+ private Mode mode = Mode.CTD;
+
+ /**
+ * Constructor.
+ *
+ * @param out Output filename
+ * @param steps Number of steps in the color map
+ * @param mode Visualization mode
+ */
+ public VisualizeGeodesicDistances(File out, int steps, Mode mode) {
+ super();
+ this.out = out;
+ this.steps = steps;
+ this.mode = mode;
+ }
+
+ @Override
+ public void run() throws UnableToComplyException {
+ // Format: Latitude, Longitude
+ // München:
+ DoubleVector stap = new DoubleVector(new double[] { 48.133333, 11.566667 });
+ // New York:
+ DoubleVector endp = new DoubleVector(new double[] { 40.712778, -74.005833 });
+ // Bavaria:
+ ModifiableHyperBoundingBox bb = new ModifiableHyperBoundingBox(new double[] { 47.27011150, 8.97634970 }, new double[] { 50.56471420, 13.83963710 });
+ // Bavaria slice on lat
+ // bb = new ModifiableHyperBoundingBox(new double[] { 47.27011150, -80 }, //
+ // new double[] { 50.56471420, 80 });
+ // Bavaria slice on lon
+ // bb = new ModifiableHyperBoundingBox(new double[] { -10, 8.97634970 }, //
+ // new double[] { 50, 13.83963710 });
+
+ BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
+
+ final double max;
+ switch(mode) {
+ case ATD:
+ // Currently half the circumference - we're missing the sign
+ max = GeoUtil.EARTH_RADIUS * Math.PI;
+ break;
+ case CTD:
+ // Quarter (!) the circumference is the maximum CTD!
+ max = .5 * GeoUtil.EARTH_RADIUS * Math.PI;
+ break;
+ case MINDIST:
+ // Half the circumference
+ max = GeoUtil.EARTH_RADIUS * Math.PI;
+ break;
+ default:
+ throw new AbortException("Invalid mode: " + mode);
+ }
+ // Red: left off-course, green: right off-course
+ int red = 0xffff0000;
+ int green = 0xff00ff00;
+
+ for (int x = 0; x < width; x++) {
+ final double lon = x * 360. / width - 180.;
+ for (int y = 0; y < height; y++) {
+ final double lat = y * -180. / height + 90.;
+ switch(mode) {
+ case ATD: {
+ final double atd = GeoUtil.alongTrackDistance(stap.doubleValue(0), stap.doubleValue(1), endp.doubleValue(0), endp.doubleValue(1), lat, lon);
+ if (atd < 0) {
+ img.setRGB(x, y, colorMultiply(red, -atd / max, false));
+ } else {
+ img.setRGB(x, y, colorMultiply(green, atd / max, false));
+ }
+ break;
+ }
+ case CTD: {
+ final double ctd = GeoUtil.crossTrackDistance(stap.doubleValue(0), stap.doubleValue(1), endp.doubleValue(0), endp.doubleValue(1), lat, lon);
+ if (ctd < 0) {
+ img.setRGB(x, y, colorMultiply(red, -ctd / max, false));
+ } else {
+ img.setRGB(x, y, colorMultiply(green, ctd / max, false));
+ }
+ break;
+ }
+ case MINDIST: {
+ final double dist = GeoUtil.latlngMinDistDeg(lat, lon, bb.getMin(0), bb.getMin(1), bb.getMax(0), bb.getMax(1));
+ if (dist < 0) {
+ img.setRGB(x, y, colorMultiply(red, -dist / max, true));
+ } else {
+ img.setRGB(x, y, colorMultiply(green, dist / max, true));
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ try {
+ ImageIO.write(img, "png", out);
+ } catch (IOException e) {
+ LOG.exception(e);
+ }
+ }
+
+ private int colorMultiply(int col, double reldist, boolean ceil) {
+ if (steps > 0) {
+ if (!ceil) {
+ reldist = Math.round(reldist * steps) * 1. / steps;
+ } else {
+ reldist = Math.ceil(reldist * steps) / steps;
+ }
+ }
+ int a = (col >> 24) & 0xFF, r = (col >> 16) & 0xFF, g = (col >> 8) & 0xFF, b = (col) & 0xFF;
+ a = (int) (a * Math.sqrt(reldist)) & 0xFF;
+ return a << 24 | r << 16 | g << 8 | b;
+ }
+
+ /**
+ * Main method for application.
+ *
+ * @param args Parameters
+ */
+ public static void main(String[] args) {
+ VisualizeGeodesicDistances.runCLIApplication(VisualizeGeodesicDistances.class, args);
+ }
+
+ /**
+ * Parameterization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public static class Parameterizer extends AbstractApplication.Parameterizer {
+ /**
+ * Number of steps in the distance map.
+ */
+ public static final OptionID STEPS_ID = new OptionID("ctdvis.steps", "Number of steps for the distance map.");
+
+ /**
+ * Visualization mode.
+ */
+ public static final OptionID MODE_ID = new OptionID("ctdvis.mode", "Visualization mode.");
+
+ /**
+ * Holds the file to print results to.
+ */
+ protected File out = null;
+
+ /**
+ * Number of steps in the color map
+ */
+ protected int steps = 0;
+
+ /**
+ * Visualization mode
+ */
+ protected Mode mode = Mode.CTD;
+
+ @Override
+ protected void makeOptions(Parameterization config) {
+ super.makeOptions(config);
+ out = super.getParameterOutputFile(config, "Output image file");
+ IntParameter stepsP = new IntParameter(STEPS_ID);
+ stepsP.setOptional(true);
+ stepsP.addConstraint(new GreaterEqualConstraint(0));
+ if (config.grab(stepsP)) {
+ steps = stepsP.intValue();
+ }
+ EnumParameter<Mode> modeP = new EnumParameter<Mode>(MODE_ID, Mode.class, Mode.CTD);
+ if (config.grab(modeP)) {
+ mode = modeP.getValue();
+ }
+ }
+
+ @Override
+ protected VisualizeGeodesicDistances makeInstance() {
+ return new VisualizeGeodesicDistances(out, steps, mode);
+ }
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/math/histograms/package-info.java b/src/de/lmu/ifi/dbs/elki/application/geo/package-info.java
index b4b23466..5937fc7d 100644
--- a/src/de/lmu/ifi/dbs/elki/math/histograms/package-info.java
+++ b/src/de/lmu/ifi/dbs/elki/application/geo/package-info.java
@@ -1,5 +1,5 @@
/**
- * <p>Classes for computing histograms.</p>
+ * <p>Application for exploring geo data.</p>
*/
/*
This file is part of ELKI:
@@ -23,4 +23,4 @@ GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package de.lmu.ifi.dbs.elki.math.histograms; \ No newline at end of file
+package de.lmu.ifi.dbs.elki.application.geo; \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/application/greedyensemble/ComputeKNNOutlierScores.java b/src/de/lmu/ifi/dbs/elki/application/greedyensemble/ComputeKNNOutlierScores.java
index de1ddbec..5f5074a2 100644
--- a/src/de/lmu/ifi/dbs/elki/application/greedyensemble/ComputeKNNOutlierScores.java
+++ b/src/de/lmu/ifi/dbs/elki/application/greedyensemble/ComputeKNNOutlierScores.java
@@ -43,8 +43,8 @@ import de.lmu.ifi.dbs.elki.application.AbstractApplication;
import de.lmu.ifi.dbs.elki.data.DoubleVector;
import de.lmu.ifi.dbs.elki.database.Database;
import de.lmu.ifi.dbs.elki.database.QueryUtil;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.query.knn.KNNQuery;
import de.lmu.ifi.dbs.elki.database.query.knn.PreprocessorKNNQuery;
@@ -94,7 +94,7 @@ public class ComputeKNNOutlierScores<O, D extends NumberDistance<D, ?>> extends
/**
* Our logger class.
*/
- private static final Logging logger = Logging.getLogger(ComputeKNNOutlierScores.class);
+ private static final Logging LOG = Logging.getLogger(ComputeKNNOutlierScores.class);
/**
* Input step
@@ -139,7 +139,6 @@ public class ComputeKNNOutlierScores<O, D extends NumberDistance<D, ?>> extends
/**
* Constructor.
*
- * @param verbose Verbose flag
* @param inputstep Input step
* @param distf Distance function
* @param startk Starting value of k
@@ -148,8 +147,8 @@ public class ComputeKNNOutlierScores<O, D extends NumberDistance<D, ?>> extends
* @param bylabel By label outlier (reference)
* @param outfile Output file
*/
- public ComputeKNNOutlierScores(boolean verbose, InputStep inputstep, DistanceFunction<? super O, D> distf, int startk, int stepk, int maxk, ByLabelOutlier bylabel, File outfile) {
- super(verbose);
+ public ComputeKNNOutlierScores(InputStep inputstep, DistanceFunction<? super O, D> distf, int startk, int stepk, int maxk, ByLabelOutlier bylabel, File outfile) {
+ super();
this.distf = distf;
this.startk = startk;
this.stepk = stepk;
@@ -163,14 +162,14 @@ public class ComputeKNNOutlierScores<O, D extends NumberDistance<D, ?>> extends
public void run() {
final Database database = inputstep.getDatabase();
final Relation<O> relation = database.getRelation(distf.getInputTypeRestriction());
- logger.verbose("Running preprocessor ...");
+ LOG.verbose("Running preprocessor ...");
MaterializeKNNPreprocessor<O, D> preproc = new MaterializeKNNPreprocessor<O, D>(relation, distf, maxk + 2);
database.addIndex(preproc);
// Test that we did get a proper index query
KNNQuery<O, D> knnq = QueryUtil.getKNNQuery(relation, distf);
if(!(knnq instanceof PreprocessorKNNQuery)) {
- logger.warning("Not using preprocessor knn query -- KNN queries using class: " + knnq.getClass());
+ LOG.warning("Not using preprocessor knn query -- KNN queries using class: " + knnq.getClass());
}
final DBIDs ids = relation.getDBIDs();
@@ -187,9 +186,8 @@ public class ComputeKNNOutlierScores<O, D extends NumberDistance<D, ?>> extends
try {
MessageDigest md = MessageDigest.getInstance("MD5");
for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
- DBID id = iter.getDBID();
md.update(" ".getBytes());
- md.update(id.toString().getBytes());
+ md.update(DBIDUtil.toString(iter).getBytes());
}
fout.append("# DBID-series MD5:");
fout.append(Base64.encodeBase64(md.digest()));
@@ -214,7 +212,7 @@ public class ComputeKNNOutlierScores<O, D extends NumberDistance<D, ?>> extends
}
// KNN
- logger.verbose("Running KNN");
+ LOG.verbose("Running KNN");
runForEachK(new AlgRunner() {
@Override
public void run(int k, String kstr) {
@@ -228,7 +226,7 @@ public class ComputeKNNOutlierScores<O, D extends NumberDistance<D, ?>> extends
}
});
// KNN Weight
- logger.verbose("Running KNNweight");
+ LOG.verbose("Running KNNweight");
runForEachK(new AlgRunner() {
@Override
public void run(int k, String kstr) {
@@ -242,7 +240,7 @@ public class ComputeKNNOutlierScores<O, D extends NumberDistance<D, ?>> extends
}
});
// Run LOF
- logger.verbose("Running LOF");
+ LOG.verbose("Running LOF");
runForEachK(new AlgRunner() {
@Override
public void run(int k, String kstr) {
@@ -256,7 +254,7 @@ public class ComputeKNNOutlierScores<O, D extends NumberDistance<D, ?>> extends
}
});
// LoOP
- logger.verbose("Running LoOP");
+ LOG.verbose("Running LoOP");
runForEachK(new AlgRunner() {
@Override
public void run(int k, String kstr) {
@@ -267,7 +265,7 @@ public class ComputeKNNOutlierScores<O, D extends NumberDistance<D, ?>> extends
}
});
// LDOF
- logger.verbose("Running LDOF");
+ LOG.verbose("Running LDOF");
runForEachK(new AlgRunner() {
@Override
public void run(int k, String kstr) {
@@ -286,7 +284,7 @@ public class ComputeKNNOutlierScores<O, D extends NumberDistance<D, ?>> extends
final PolynomialKernelFunction poly = new PolynomialKernelFunction(PolynomialKernelFunction.DEFAULT_DEGREE);
@SuppressWarnings("unchecked")
final DistanceFunction<DoubleVector, DoubleDistance> df = DistanceFunction.class.cast(distf);
- logger.verbose("Running ABOD");
+ LOG.verbose("Running ABOD");
runForEachK(new AlgRunner() {
@Override
public void run(int k, String kstr) {
@@ -302,7 +300,7 @@ public class ComputeKNNOutlierScores<O, D extends NumberDistance<D, ?>> extends
}
catch(ClassCastException e) {
// ABOD might just be not appropriate.
- logger.warning("Running ABOD failed - probably not appropriate to this data type / distance?", e);
+ LOG.warning("Running ABOD failed - probably not appropriate to this data type / distance?", e);
}
}
}
@@ -375,17 +373,17 @@ public class ComputeKNNOutlierScores<O, D extends NumberDistance<D, ?>> extends
/**
* Option ID for k step size.
*/
- public static final OptionID STEPK_ID = OptionID.getOrCreateOptionID("stepk", "Step size for k.");
+ public static final OptionID STEPK_ID = new OptionID("stepk", "Step size for k.");
/**
* Option ID for k start size.
*/
- public static final OptionID STARTK_ID = OptionID.getOrCreateOptionID("startk", "Minimum value for k.");
+ public static final OptionID STARTK_ID = new OptionID("startk", "Minimum value for k.");
/**
* Option ID for k step size.
*/
- public static final OptionID MAXK_ID = OptionID.getOrCreateOptionID("maxk", "Maximum value for k.");
+ public static final OptionID MAXK_ID = new OptionID("maxk", "Maximum value for k.");
/**
* k step size
@@ -433,18 +431,21 @@ public class ComputeKNNOutlierScores<O, D extends NumberDistance<D, ?>> extends
distf = distP.instantiateClass(config);
}
// k parameters
- IntParameter stepkP = new IntParameter(STEPK_ID, new GreaterConstraint(0));
+ IntParameter stepkP = new IntParameter(STEPK_ID);
+ stepkP.addConstraint(new GreaterConstraint(0));
if(config.grab(stepkP)) {
stepk = stepkP.getValue();
}
- IntParameter startkP = new IntParameter(STARTK_ID, true);
+ IntParameter startkP = new IntParameter(STARTK_ID);
+ startkP.setOptional(true);
if(config.grab(startkP)) {
startk = startkP.getValue();
}
else {
startk = stepk;
}
- IntParameter maxkP = new IntParameter(MAXK_ID, new GreaterConstraint(0));
+ IntParameter maxkP = new IntParameter(MAXK_ID);
+ maxkP.addConstraint(new GreaterConstraint(0));
if(config.grab(maxkP)) {
maxk = maxkP.getValue();
}
@@ -455,7 +456,7 @@ public class ComputeKNNOutlierScores<O, D extends NumberDistance<D, ?>> extends
@Override
protected AbstractApplication makeInstance() {
- return new ComputeKNNOutlierScores<O, D>(verbose, inputstep, distf, startk, stepk, maxk, bylabel, outfile);
+ return new ComputeKNNOutlierScores<O, D>(inputstep, distf, startk, stepk, maxk, bylabel, outfile);
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/application/greedyensemble/GreedyEnsembleExperiment.java b/src/de/lmu/ifi/dbs/elki/application/greedyensemble/GreedyEnsembleExperiment.java
index 6b2f9e13..f3f9c101 100644
--- a/src/de/lmu/ifi/dbs/elki/application/greedyensemble/GreedyEnsembleExperiment.java
+++ b/src/de/lmu/ifi/dbs/elki/application/greedyensemble/GreedyEnsembleExperiment.java
@@ -34,11 +34,14 @@ import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
import de.lmu.ifi.dbs.elki.database.Database;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
+import de.lmu.ifi.dbs.elki.database.ids.DoubleDBIDPair;
import de.lmu.ifi.dbs.elki.database.ids.HashSetModifiableDBIDs;
import de.lmu.ifi.dbs.elki.database.ids.ModifiableDBIDs;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.distance.distancefunction.PrimitiveDoubleDistanceFunction;
import de.lmu.ifi.dbs.elki.distance.distancefunction.correlation.WeightedPearsonCorrelationDistanceFunction;
import de.lmu.ifi.dbs.elki.evaluation.roc.ROC;
@@ -52,7 +55,6 @@ import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
import de.lmu.ifi.dbs.elki.utilities.exceptions.AbortException;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
import de.lmu.ifi.dbs.elki.utilities.pairs.DoubleIntPair;
-import de.lmu.ifi.dbs.elki.utilities.pairs.DoubleObjPair;
import de.lmu.ifi.dbs.elki.workflow.InputStep;
/**
@@ -84,9 +86,9 @@ import de.lmu.ifi.dbs.elki.workflow.InputStep;
@Reference(authors = "E. Schubert, R. Wojdanowski, A. Zimek, H.-P. Kriegel", title = "On Evaluation of Outlier Rankings and Outlier Scores", booktitle = "Proc. 12th SIAM International Conference on Data Mining (SDM), Anaheim, CA, 2012.")
public class GreedyEnsembleExperiment extends AbstractApplication {
/**
- * Get static logger
+ * Get static logger.
*/
- private static final Logging logger = Logging.getLogger(GreedyEnsembleExperiment.class);
+ private static final Logging LOG = Logging.getLogger(GreedyEnsembleExperiment.class);
/**
* The data input part.
@@ -101,11 +103,10 @@ public class GreedyEnsembleExperiment extends AbstractApplication {
/**
* Constructor.
*
- * @param verbose Verbosity
* @param inputstep Input step
*/
- public GreedyEnsembleExperiment(boolean verbose, InputStep inputstep) {
- super(verbose);
+ public GreedyEnsembleExperiment(InputStep inputstep) {
+ super();
this.inputstep = inputstep;
}
@@ -114,22 +115,23 @@ public class GreedyEnsembleExperiment extends AbstractApplication {
// Note: the database contains the *result vectors*, not the original data
// points.
final Database database = inputstep.getDatabase();
- final Relation<NumberVector<?, ?>> relation = database.getRelation(TypeUtil.NUMBER_VECTOR_FIELD);
+ final Relation<NumberVector<?>> relation = database.getRelation(TypeUtil.NUMBER_VECTOR_FIELD);
+ final NumberVector.Factory<NumberVector<?>, ?> factory = RelationUtil.getNumberVectorFactory(relation);
final Relation<String> labels = DatabaseUtil.guessLabelRepresentation(database);
- final DBID firstid = labels.iterDBIDs().getDBID();
+ final DBID firstid = DBIDUtil.deref(labels.iterDBIDs());
final String firstlabel = labels.get(firstid);
if(!firstlabel.matches("bylabel")) {
throw new AbortException("No 'by label' reference outlier found, which is needed for weighting!");
}
// Dimensionality and reference vector
- final int dim = DatabaseUtil.dimensionality(relation);
- final NumberVector<?, ?> refvec = relation.get(firstid);
+ final int dim = RelationUtil.dimensionality(relation);
+ final NumberVector<?> refvec = relation.get(firstid);
// Build the positive index set for ROC AUC.
Set<Integer> positive = new TreeSet<Integer>();
for(int d = 0; d < dim; d++) {
- if(refvec.doubleValue(d + 1) > 0) {
+ if(refvec.doubleValue(d) > 0) {
positive.add(d);
}
}
@@ -140,18 +142,17 @@ public class GreedyEnsembleExperiment extends AbstractApplication {
// Find the top-k for each ensemble member
{
for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
// Skip "by label", obviously
- if(firstid.sameDBID(id)) {
+ if(DBIDUtil.equal(firstid, iditer)) {
continue;
}
- final NumberVector<?, ?> vec = relation.get(id);
+ final NumberVector<?> vec = relation.get(iditer);
TiedTopBoundedHeap<DoubleIntPair> heap = new TiedTopBoundedHeap<DoubleIntPair>(estimated_outliers, Collections.reverseOrder());
for(int i = 0; i < dim; i++) {
- heap.add(new DoubleIntPair(vec.doubleValue(i + 1), i));
+ heap.add(new DoubleIntPair(vec.doubleValue(i), i));
}
if(heap.size() >= 2 * estimated_outliers) {
- logger.warning("Too many ties. Expected: " + estimated_outliers + " got: " + heap.size());
+ LOG.warning("Too many ties. Expected: " + estimated_outliers + " got: " + heap.size());
}
for(DoubleIntPair pair : heap) {
if(outliers_seen[pair.second] == 0) {
@@ -164,34 +165,33 @@ public class GreedyEnsembleExperiment extends AbstractApplication {
}
}
}
- logger.verbose("Merged top " + estimated_outliers + " outliers to: " + union_outliers + " outliers");
+ LOG.verbose("Merged top " + estimated_outliers + " outliers to: " + union_outliers + " outliers");
// Build the final weight vector.
final double[] estimated_weights = new double[dim];
final double[] estimated_truth = new double[dim];
updateEstimations(outliers_seen, union_outliers, estimated_weights, estimated_truth);
- NumberVector<?, ?> estimated_truth_vec = refvec.newNumberVector(estimated_truth);
+ NumberVector<?> estimated_truth_vec = factory.newNumberVector(estimated_truth);
- PrimitiveDoubleDistanceFunction<NumberVector<?, ?>> wdist = getDistanceFunction(estimated_weights);
- PrimitiveDoubleDistanceFunction<NumberVector<?, ?>> tdist = wdist;
+ PrimitiveDoubleDistanceFunction<NumberVector<?>> wdist = getDistanceFunction(estimated_weights);
+ PrimitiveDoubleDistanceFunction<NumberVector<?>> tdist = wdist;
// Build the naive ensemble:
final double[] naiveensemble = new double[dim];
{
for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
- if(firstid.equals(id)) {
+ if(DBIDUtil.equal(firstid, iditer)) {
continue;
}
- final NumberVector<?, ?> vec = relation.get(id);
+ final NumberVector<?> vec = relation.get(iditer);
for(int d = 0; d < dim; d++) {
- naiveensemble[d] += vec.doubleValue(d + 1);
+ naiveensemble[d] += vec.doubleValue(d);
}
}
for(int d = 0; d < dim; d++) {
naiveensemble[d] /= (relation.size() - 1);
}
}
- NumberVector<?, ?> naivevec = refvec.newNumberVector(naiveensemble);
+ NumberVector<?> naivevec = factory.newNumberVector(naiveensemble);
// Compute single AUC scores and estimations.
// Remember the method most similar to the estimation
@@ -204,72 +204,70 @@ public class GreedyEnsembleExperiment extends AbstractApplication {
{
// Compute individual scores
for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
- if(firstid.equals(id)) {
+ if(DBIDUtil.equal(firstid, iditer)) {
continue;
}
// fout.append(labels.get(id));
- final NumberVector<?, ?> vec = relation.get(id);
+ final NumberVector<?> vec = relation.get(iditer);
double auc = computeROCAUC(vec, positive, dim);
double estimated = wdist.doubleDistance(vec, estimated_truth_vec);
double cost = tdist.doubleDistance(vec, refvec);
- logger.verbose("ROC AUC: " + auc + " estimated " + estimated + " cost " + cost + " " + labels.get(id));
+ LOG.verbose("ROC AUC: " + auc + " estimated " + estimated + " cost " + cost + " " + labels.get(iditer));
if(auc > bestauc) {
bestauc = auc;
- bestaucstr = labels.get(id);
+ bestaucstr = labels.get(iditer);
}
if(cost < bestcost) {
bestcost = cost;
- bestcoststr = labels.get(id);
+ bestcoststr = labels.get(iditer);
}
if(estimated < bestest) {
bestest = estimated;
- bestid = id;
+ bestid = DBIDUtil.deref(iditer);
}
}
}
// Initialize ensemble with "best" method
- logger.verbose("Distance function: " + wdist);
- logger.verbose("Initial estimation of outliers: " + union_outliers);
- logger.verbose("Initializing ensemble with: " + labels.get(bestid));
+ LOG.verbose("Distance function: " + wdist);
+ LOG.verbose("Initial estimation of outliers: " + union_outliers);
+ LOG.verbose("Initializing ensemble with: " + labels.get(bestid));
ModifiableDBIDs ensemble = DBIDUtil.newArray(bestid);
ModifiableDBIDs enscands = DBIDUtil.newHashSet(relation.getDBIDs());
enscands.remove(bestid);
enscands.remove(firstid);
final double[] greedyensemble = new double[dim];
{
- final NumberVector<?, ?> vec = relation.get(bestid);
+ final NumberVector<?> vec = relation.get(bestid);
for(int i = 0; i < dim; i++) {
- greedyensemble[i] = vec.doubleValue(i + 1);
+ greedyensemble[i] = vec.doubleValue(i);
}
}
// Greedily grow the ensemble
final double[] testensemble = new double[dim];
while(enscands.size() > 0) {
- NumberVector<?, ?> greedyvec = refvec.newNumberVector(greedyensemble);
+ NumberVector<?> greedyvec = factory.newNumberVector(greedyensemble);
// Weighting factors for combining:
double s1 = ensemble.size() / (ensemble.size() + 1.);
double s2 = 1. / (ensemble.size() + 1.);
final int heapsize = enscands.size();
- TopBoundedHeap<DoubleObjPair<DBID>> heap = new TopBoundedHeap<DoubleObjPair<DBID>>(heapsize, Collections.reverseOrder());
+ TopBoundedHeap<DoubleDBIDPair> heap = new TopBoundedHeap<DoubleDBIDPair>(heapsize, Collections.reverseOrder());
for (DBIDIter iter = enscands.iter(); iter.valid(); iter.advance()) {
- DBID id = iter.getDBID();
- final NumberVector<?, ?> vec = relation.get(id);
+ final NumberVector<?> vec = relation.get(iter);
double diversity = wdist.doubleDistance(vec, greedyvec);
- heap.add(new DoubleObjPair<DBID>(diversity, id));
+ heap.add(DBIDUtil.newPair(diversity, iter));
}
while(heap.size() > 0) {
- DBID bestadd = heap.poll().second;
+ DBIDRef bestadd = heap.poll();
enscands.remove(bestadd);
// Update ensemble:
- final NumberVector<?, ?> vec = relation.get(bestadd);
+ final NumberVector<?> vec = relation.get(bestadd);
for(int i = 0; i < dim; i++) {
- testensemble[i] = greedyensemble[i] * s1 + vec.doubleValue(i + 1) * s2;
+ testensemble[i] = greedyensemble[i] * s1 + vec.doubleValue(i) * s2;
}
- NumberVector<?, ?> testvec = refvec.newNumberVector(testensemble);
+ NumberVector<?> testvec = factory.newNumberVector(testensemble);
double oldd = wdist.doubleDistance(estimated_truth_vec, greedyvec);
double newd = wdist.doubleDistance(estimated_truth_vec, testvec);
// logger.verbose("Distances: " + oldd + " vs. " + newd);
@@ -286,7 +284,7 @@ public class GreedyEnsembleExperiment extends AbstractApplication {
// Update target vectors and weights
TiedTopBoundedHeap<DoubleIntPair> oheap = new TiedTopBoundedHeap<DoubleIntPair>(estimated_outliers, Collections.reverseOrder());
for(int i = 0; i < dim; i++) {
- oheap.add(new DoubleIntPair(vec.doubleValue(i + 1), i));
+ oheap.add(new DoubleIntPair(vec.doubleValue(i), i));
}
for(DoubleIntPair pair : oheap) {
assert (outliers_seen[pair.second] > 0);
@@ -298,14 +296,14 @@ public class GreedyEnsembleExperiment extends AbstractApplication {
}
if(refresh) {
updateEstimations(outliers_seen, union_outliers, estimated_weights, estimated_truth);
- estimated_truth_vec = refvec.newNumberVector(estimated_truth);
+ estimated_truth_vec = factory.newNumberVector(estimated_truth);
}
}
}
}
}
// Build the improved ensemble:
- StringBuffer greedylbl = new StringBuffer();
+ StringBuilder greedylbl = new StringBuilder();
{
for (DBIDIter iter = ensemble.iter(); iter.valid(); iter.advance()) {
if(greedylbl.length() > 0) {
@@ -314,27 +312,27 @@ public class GreedyEnsembleExperiment extends AbstractApplication {
greedylbl.append(labels.get(iter));
}
}
- NumberVector<?, ?> greedyvec = refvec.newNumberVector(greedyensemble);
- logger.verbose("Estimated outliers remaining: " + union_outliers);
- logger.verbose("Greedy ensemble: " + greedylbl.toString());
+ NumberVector<?> greedyvec = factory.newNumberVector(greedyensemble);
+ LOG.verbose("Estimated outliers remaining: " + union_outliers);
+ LOG.verbose("Greedy ensemble: " + greedylbl.toString());
- logger.verbose("Best single ROC AUC: " + bestauc + " (" + bestaucstr + ")");
- logger.verbose("Best single cost: " + bestcost + " (" + bestcoststr + ")");
+ LOG.verbose("Best single ROC AUC: " + bestauc + " (" + bestaucstr + ")");
+ LOG.verbose("Best single cost: " + bestcost + " (" + bestcoststr + ")");
// Evaluate the naive ensemble and the "shrunk" ensemble
double naiveauc, naivecost;
{
naiveauc = computeROCAUC(naivevec, positive, dim);
naivecost = tdist.doubleDistance(naivevec, refvec);
- logger.verbose("Naive ensemble AUC: " + naiveauc + " cost: " + naivecost);
- logger.verbose("Naive ensemble Gain: " + gain(naiveauc, bestauc, 1) + " cost gain: " + gain(naivecost, bestcost, 0));
+ LOG.verbose("Naive ensemble AUC: " + naiveauc + " cost: " + naivecost);
+ LOG.verbose("Naive ensemble Gain: " + gain(naiveauc, bestauc, 1) + " cost gain: " + gain(naivecost, bestcost, 0));
}
double greedyauc, greedycost;
{
greedyauc = computeROCAUC(greedyvec, positive, dim);
greedycost = tdist.doubleDistance(greedyvec, refvec);
- logger.verbose("Greedy ensemble AUC: " + greedyauc + " cost: " + greedycost);
- logger.verbose("Greedy ensemble Gain to best: " + gain(greedyauc, bestauc, 1) + " cost gain: " + gain(greedycost, bestcost, 0));
- logger.verbose("Greedy ensemble Gain to naive: " + gain(greedyauc, naiveauc, 1) + " cost gain: " + gain(greedycost, naivecost, 0));
+ LOG.verbose("Greedy ensemble AUC: " + greedyauc + " cost: " + greedycost);
+ LOG.verbose("Greedy ensemble Gain to best: " + gain(greedyauc, bestauc, 1) + " cost gain: " + gain(greedycost, bestcost, 0));
+ LOG.verbose("Greedy ensemble Gain to naive: " + gain(greedyauc, naiveauc, 1) + " cost gain: " + gain(greedycost, naivecost, 0));
}
{
MeanVariance meanauc = new MeanVariance();
@@ -347,33 +345,32 @@ public class GreedyEnsembleExperiment extends AbstractApplication {
{
DBIDs random = DBIDUtil.randomSample(candidates, ensemble.size(), (long)i);
for (DBIDIter iter = random.iter(); iter.valid(); iter.advance()) {
- DBID id = iter.getDBID();
- assert (!firstid.equals(id));
+ assert (!DBIDUtil.equal(firstid, iter));
// logger.verbose("Using: "+labels.get(id));
- final NumberVector<?, ?> vec = relation.get(id);
+ final NumberVector<?> vec = relation.get(iter);
for(int d = 0; d < dim; d++) {
- randomensemble[d] += vec.doubleValue(d + 1);
+ randomensemble[d] += vec.doubleValue(d);
}
}
for(int d = 0; d < dim; d++) {
randomensemble[d] /= ensemble.size();
}
}
- NumberVector<?, ?> randomvec = refvec.newNumberVector(randomensemble);
+ NumberVector<?> randomvec = factory.newNumberVector(randomensemble);
double auc = computeROCAUC(randomvec, positive, dim);
meanauc.put(auc);
double cost = tdist.doubleDistance(randomvec, refvec);
meancost.put(cost);
}
- logger.verbose("Random ensemble AUC: " + meanauc.getMean() + " + stddev: " + meanauc.getSampleStddev() + " = " + (meanauc.getMean() + meanauc.getSampleStddev()));
- logger.verbose("Random ensemble Gain: " + gain(meanauc.getMean(), bestauc, 1));
- logger.verbose("Greedy improvement: " + (greedyauc - meanauc.getMean()) / meanauc.getSampleStddev() + " standard deviations.");
- logger.verbose("Random ensemble Cost: " + meancost.getMean() + " + stddev: " + meancost.getSampleStddev() + " = " + (meancost.getMean() + meanauc.getSampleStddev()));
- logger.verbose("Random ensemble Gain: " + gain(meancost.getMean(), bestcost, 0));
- logger.verbose("Greedy improvement: " + (meancost.getMean() - greedycost) / meancost.getSampleStddev() + " standard deviations.");
- logger.verbose("Naive ensemble Gain to random: " + gain(naiveauc, meanauc.getMean(), 1) + " cost gain: " + gain(naivecost, meancost.getMean(), 0));
- logger.verbose("Random ensemble Gain to naive: " + gain(meanauc.getMean(), naiveauc, 1) + " cost gain: " + gain(meancost.getMean(), naivecost, 0));
- logger.verbose("Greedy ensemble Gain to random: " + gain(greedyauc, meanauc.getMean(), 1) + " cost gain: " + gain(greedycost, meancost.getMean(), 0));
+ LOG.verbose("Random ensemble AUC: " + meanauc.getMean() + " + stddev: " + meanauc.getSampleStddev() + " = " + (meanauc.getMean() + meanauc.getSampleStddev()));
+ LOG.verbose("Random ensemble Gain: " + gain(meanauc.getMean(), bestauc, 1));
+ LOG.verbose("Greedy improvement: " + (greedyauc - meanauc.getMean()) / meanauc.getSampleStddev() + " standard deviations.");
+ LOG.verbose("Random ensemble Cost: " + meancost.getMean() + " + stddev: " + meancost.getSampleStddev() + " = " + (meancost.getMean() + meanauc.getSampleStddev()));
+ LOG.verbose("Random ensemble Gain: " + gain(meancost.getMean(), bestcost, 0));
+ LOG.verbose("Greedy improvement: " + (meancost.getMean() - greedycost) / meancost.getSampleStddev() + " standard deviations.");
+ LOG.verbose("Naive ensemble Gain to random: " + gain(naiveauc, meanauc.getMean(), 1) + " cost gain: " + gain(naivecost, meancost.getMean(), 0));
+ LOG.verbose("Random ensemble Gain to naive: " + gain(meanauc.getMean(), naiveauc, 1) + " cost gain: " + gain(meancost.getMean(), naivecost, 0));
+ LOG.verbose("Greedy ensemble Gain to random: " + gain(greedyauc, meanauc.getMean(), 1) + " cost gain: " + gain(greedycost, meancost.getMean(), 0));
}
}
@@ -390,21 +387,29 @@ public class GreedyEnsembleExperiment extends AbstractApplication {
}
}
- private PrimitiveDoubleDistanceFunction<NumberVector<?, ?>> getDistanceFunction(double[] estimated_weights) {
+ private PrimitiveDoubleDistanceFunction<NumberVector<?>> getDistanceFunction(double[] estimated_weights) {
// return new WeightedSquaredEuclideanDistanceFunction(estimated_weights);
// return new WeightedLPNormDistanceFunction(1.0, estimated_weights);
return new WeightedPearsonCorrelationDistanceFunction(estimated_weights);
}
- private double computeROCAUC(NumberVector<?, ?> vec, Set<Integer> positive, int dim) {
+ private double computeROCAUC(NumberVector<?> vec, Set<Integer> positive, int dim) {
final DoubleIntPair[] scores = new DoubleIntPair[dim];
for(int d = 0; d < dim; d++) {
- scores[d] = new DoubleIntPair(vec.doubleValue(d + 1), d);
+ scores[d] = new DoubleIntPair(vec.doubleValue(d), d);
}
Arrays.sort(scores, Collections.reverseOrder(DoubleIntPair.BYFIRST_COMPARATOR));
return XYCurve.areaUnderCurve(ROC.materializeROC(dim, positive, Arrays.asList(scores).iterator()));
}
+ /**
+ * Compute the gain coefficient.
+ *
+ * @param score New score
+ * @param ref Reference score
+ * @param optimal Maximum score possible
+ * @return Gain
+ */
double gain(double score, double ref, double optimal) {
return 1 - ((optimal - score) / (optimal - ref));
}
@@ -418,7 +423,7 @@ public class GreedyEnsembleExperiment extends AbstractApplication {
*/
public static class Parameterizer extends AbstractApplication.Parameterizer {
/**
- * Data source
+ * Data source.
*/
InputStep inputstep;
@@ -431,7 +436,7 @@ public class GreedyEnsembleExperiment extends AbstractApplication {
@Override
protected AbstractApplication makeInstance() {
- return new GreedyEnsembleExperiment(verbose, inputstep);
+ return new GreedyEnsembleExperiment(inputstep);
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/application/greedyensemble/VisualizePairwiseGainMatrix.java b/src/de/lmu/ifi/dbs/elki/application/greedyensemble/VisualizePairwiseGainMatrix.java
index 105eeabc..afb9c122 100644
--- a/src/de/lmu/ifi/dbs/elki/application/greedyensemble/VisualizePairwiseGainMatrix.java
+++ b/src/de/lmu/ifi/dbs/elki/application/greedyensemble/VisualizePairwiseGainMatrix.java
@@ -38,8 +38,11 @@ import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
import de.lmu.ifi.dbs.elki.database.Database;
import de.lmu.ifi.dbs.elki.database.ids.ArrayModifiableDBIDs;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDArrayIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.evaluation.roc.ROC;
import de.lmu.ifi.dbs.elki.evaluation.similaritymatrix.ComputeSimilarityMatrixImage;
import de.lmu.ifi.dbs.elki.evaluation.similaritymatrix.ComputeSimilarityMatrixImage.SimilarityMatrix;
@@ -80,13 +83,16 @@ import de.lmu.ifi.dbs.elki.workflow.InputStep;
* </p>
*
* @author Erich Schubert
+ *
+ * @apiviz.composedOf VisualizerParameterizer
+ * @apiviz.composedOf SimilarityMatrixVisualizer
*/
@Reference(authors = "E. Schubert, R. Wojdanowski, A. Zimek, H.-P. Kriegel", title = "On Evaluation of Outlier Rankings and Outlier Scores", booktitle = "Proc. 12th SIAM International Conference on Data Mining (SDM), Anaheim, CA, 2012.")
public class VisualizePairwiseGainMatrix extends AbstractApplication {
/**
- * Get static logger
+ * Get static logger.
*/
- private static final Logging logger = Logging.getLogger(VisualizePairwiseGainMatrix.class);
+ private static final Logging LOG = Logging.getLogger(VisualizePairwiseGainMatrix.class);
/**
* The data input part.
@@ -94,19 +100,18 @@ public class VisualizePairwiseGainMatrix extends AbstractApplication {
private InputStep inputstep;
/**
- * Parameterizer for visualizers
+ * Parameterizer for visualizers.
*/
private VisualizerParameterizer vispar;
/**
* Constructor.
*
- * @param verbose Verbosity
* @param inputstep Input step
* @param vispar Visualizer parameterizer
*/
- public VisualizePairwiseGainMatrix(boolean verbose, InputStep inputstep, VisualizerParameterizer vispar) {
- super(verbose);
+ public VisualizePairwiseGainMatrix(InputStep inputstep, VisualizerParameterizer vispar) {
+ super();
this.inputstep = inputstep;
this.vispar = vispar;
}
@@ -114,17 +119,17 @@ public class VisualizePairwiseGainMatrix extends AbstractApplication {
@Override
public void run() {
final Database database = inputstep.getDatabase();
- final Relation<NumberVector<?, ?>> relation = database.getRelation(TypeUtil.NUMBER_VECTOR_FIELD);
+ final Relation<NumberVector<?>> relation = database.getRelation(TypeUtil.NUMBER_VECTOR_FIELD);
final Relation<String> labels = DatabaseUtil.guessLabelRepresentation(database);
- final DBID firstid = labels.iterDBIDs().getDBID();
+ final DBID firstid = DBIDUtil.deref(labels.iterDBIDs());
final String firstlabel = labels.get(firstid);
if(!firstlabel.matches("bylabel")) {
throw new AbortException("No 'by label' reference outlier found, which is needed for weighting!");
}
// Dimensionality and reference vector
- final int dim = DatabaseUtil.dimensionality(relation);
- final NumberVector<?, ?> refvec = relation.get(firstid);
+ final int dim = RelationUtil.dimensionality(relation);
+ final NumberVector<?> refvec = relation.get(firstid);
// Build the truth vector
Set<Integer> pos = new TreeSet<Integer>();
@@ -132,7 +137,7 @@ public class VisualizePairwiseGainMatrix extends AbstractApplication {
{
for(int d = 0; d < dim; d++) {
combined[d] = new DoubleIntPair(0, d);
- if(refvec.doubleValue(d + 1) > 0) {
+ if(refvec.doubleValue(d) > 0) {
pos.add(d);
}
}
@@ -145,34 +150,39 @@ public class VisualizePairwiseGainMatrix extends AbstractApplication {
double[][] data = new double[size][size];
DoubleMinMax minmax = new DoubleMinMax();
- for(int a = 0; a < size; a++) {
- final NumberVector<?, ?> veca = relation.get(ids.get(a));
- // Direct AUC score:
- {
- for(int d = 0; d < dim; d++) {
- combined[d].first = veca.doubleValue(d + 1);
- combined[d].second = d;
+ {
+ int a = 0;
+ for(DBIDIter id = ids.iter(); id.valid(); id.advance(), a++) {
+ final NumberVector<?> veca = relation.get(id);
+ // Direct AUC score:
+ {
+ for(int d = 0; d < dim; d++) {
+ combined[d].first = veca.doubleValue(d);
+ combined[d].second = d;
+ }
+ Arrays.sort(combined, Collections.reverseOrder(DoubleIntPair.BYFIRST_COMPARATOR));
+ double auc = XYCurve.areaUnderCurve(ROC.materializeROC(dim, pos, Arrays.asList(combined).iterator()));
+ data[a][a] = auc;
+ // minmax.put(auc);
+ // logger.verbose(auc + " " + labels.get(ids.get(a)));
}
- Arrays.sort(combined, Collections.reverseOrder(DoubleIntPair.BYFIRST_COMPARATOR));
- double auc = XYCurve.areaUnderCurve(ROC.materializeROC(dim, pos, Arrays.asList(combined).iterator()));
- data[a][a] = auc;
- // minmax.put(auc);
- // logger.verbose(auc + " " + labels.get(ids.get(a)));
- }
- // Compare to others, exploiting symmetry
- for(int b = a + 1; b < size; b++) {
- final NumberVector<?, ?> vecb = relation.get(ids.get(b));
- for(int d = 0; d < dim; d++) {
- combined[d].first = veca.doubleValue(d + 1) + vecb.doubleValue(d + 1);
- combined[d].second = d;
+ // Compare to others, exploiting symmetry
+ DBIDArrayIter id2 = ids.iter();
+ id2.seek(a + 1);
+ for(int b = a + 1; b < size; b++, id2.advance()) {
+ final NumberVector<?> vecb = relation.get(id2);
+ for(int d = 0; d < dim; d++) {
+ combined[d].first = veca.doubleValue(d) + vecb.doubleValue(d);
+ combined[d].second = d;
+ }
+ Arrays.sort(combined, Collections.reverseOrder(DoubleIntPair.BYFIRST_COMPARATOR));
+ double auc = XYCurve.areaUnderCurve(ROC.materializeROC(dim, pos, Arrays.asList(combined).iterator()));
+ // logger.verbose(auc + " " + labels.get(ids.get(a)) + " " +
+ // labels.get(ids.get(b)));
+ data[a][b] = auc;
+ data[b][a] = auc;
+ // minmax.put(auc);
}
- Arrays.sort(combined, Collections.reverseOrder(DoubleIntPair.BYFIRST_COMPARATOR));
- double auc = XYCurve.areaUnderCurve(ROC.materializeROC(dim, pos, Arrays.asList(combined).iterator()));
- // logger.verbose(auc + " " + labels.get(ids.get(a)) + " " +
- // labels.get(ids.get(b)));
- data[a][b] = auc;
- data[b][a] = auc;
- // minmax.put(auc);
}
}
for(int a = 0; a < size; a++) {
@@ -189,7 +199,7 @@ public class VisualizePairwiseGainMatrix extends AbstractApplication {
data[a][a] = 0;
}
- logger.verbose(minmax.toString());
+ LOG.verbose(minmax.toString());
boolean hasneg = (minmax.getMin() < -1E-3);
LinearScaling scale;
@@ -233,7 +243,7 @@ public class VisualizePairwiseGainMatrix extends AbstractApplication {
VisualizerContext context = vispar.newContext(database);
// Attach visualizers to results
- SimilarityMatrixVisualizer.Factory factory = new SimilarityMatrixVisualizer.Factory();
+ SimilarityMatrixVisualizer factory = new SimilarityMatrixVisualizer();
factory.processNewResult(database, database);
List<VisualizationTask> tasks = ResultUtil.filterResults(database, VisualizationTask.class);
@@ -247,12 +257,11 @@ public class VisualizePairwiseGainMatrix extends AbstractApplication {
/**
* Show a single visualization.
*
- * @param context
- *
- * @param factory
- * @param task
+ * @param context Visualization context
+ * @param factory Visualizer factory
+ * @param task Visualization task
*/
- private void showVisualization(VisualizerContext context, SimilarityMatrixVisualizer.Factory factory, VisualizationTask task) {
+ private void showVisualization(VisualizerContext context, SimilarityMatrixVisualizer factory, VisualizationTask task) {
SVGPlot plot = new SVGPlot();
Visualization vis = factory.makeVisualization(task.clone(plot, context, null, 1.0, 1.0));
plot.getRoot().appendChild(vis.getLayer());
@@ -273,12 +282,12 @@ public class VisualizePairwiseGainMatrix extends AbstractApplication {
*/
public static class Parameterizer extends AbstractApplication.Parameterizer {
/**
- * Data source
+ * Data source.
*/
InputStep inputstep;
/**
- * Parameterizer for visualizers
+ * Parameterizer for visualizers.
*/
private VisualizerParameterizer vispar;
@@ -293,7 +302,7 @@ public class VisualizePairwiseGainMatrix extends AbstractApplication {
@Override
protected AbstractApplication makeInstance() {
- return new VisualizePairwiseGainMatrix(verbose, inputstep, vispar);
+ return new VisualizePairwiseGainMatrix(inputstep, vispar);
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/application/internal/CheckELKIServices.java b/src/de/lmu/ifi/dbs/elki/application/internal/CheckELKIServices.java
index 21b8c698..cfa57c80 100644
--- a/src/de/lmu/ifi/dbs/elki/application/internal/CheckELKIServices.java
+++ b/src/de/lmu/ifi/dbs/elki/application/internal/CheckELKIServices.java
@@ -25,9 +25,11 @@ package de.lmu.ifi.dbs.elki.application.internal;
import java.io.BufferedReader;
import java.io.File;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
+import java.io.PrintStream;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
@@ -55,7 +57,7 @@ public class CheckELKIServices {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(CheckELKIServices.class);
+ private static final Logging LOG = Logging.getLogger(CheckELKIServices.class);
/**
* Pattern to strip comments, while keeping commented class names.
@@ -73,23 +75,36 @@ public class CheckELKIServices {
* @param argv Command line arguments
*/
public static void main(String[] argv) {
- new CheckELKIServices().checkServices();
+ boolean update = false, noskip = false;
+ for(String arg : argv) {
+ if("-update".equals(arg)) {
+ update = true;
+ }
+ if("-all".equals(arg)) {
+ noskip = true;
+ }
+ }
+ new CheckELKIServices().checkServices(update, noskip);
}
/**
* Retrieve all properties and check them.
+ *
+ * @param update Flag to enable automatic updating
+ * @param noskip Override filters, include all (in particular,
+ * experimentalcode)
*/
- public void checkServices() {
+ public void checkServices(boolean update, boolean noskip) {
URL u = getClass().getClassLoader().getResource(ELKIServiceLoader.PREFIX);
try {
for(String prop : new File(u.toURI()).list()) {
- if (".svn".equals(prop)) {
+ if(".svn".equals(prop)) {
continue;
}
- if (logger.isVerbose()) {
- logger.verbose("Checking property: "+prop);
+ if(LOG.isVerbose()) {
+ LOG.verbose("Checking property: " + prop);
}
- checkService(prop);
+ checkService(prop, update, noskip);
}
}
catch(URISyntaxException e) {
@@ -101,14 +116,17 @@ public class CheckELKIServices {
* Check a single service class
*
* @param prop Class name.
+ * @param update Flag to enable automatic updating
+ * @param noskip Override filters, include all (in particular,
+ * experimentalcode)
*/
- private void checkService(String prop) {
+ private void checkService(String prop, boolean update, boolean noskip) {
Class<?> cls;
try {
cls = Class.forName(prop);
}
catch(ClassNotFoundException e) {
- logger.warning("Property is not a class name: " + prop);
+ LOG.warning("Property is not a class name: " + prop);
return;
}
List<Class<?>> impls = InspectionUtil.findAllImplementations(cls, false);
@@ -121,7 +139,7 @@ public class CheckELKIServices {
break;
}
}
- if(skip) {
+ if(!noskip && skip) {
continue;
}
names.add(c2.getName());
@@ -144,29 +162,58 @@ public class CheckELKIServices {
names.remove(stripped);
}
else {
- logger.warning("Name " + stripped + " found for property " + prop + " but no class discovered (or referenced twice?).");
+ LOG.warning("Name " + stripped + " found for property " + prop + " but no class discovered (or referenced twice?).");
}
}
}
else {
- logger.warning("Line: " + line + " didn't match regexp.");
+ LOG.warning("Line: " + line + " didn't match regexp.");
}
}
}
catch(IOException e) {
- logger.exception(e);
+ LOG.exception(e);
}
if(names.size() > 0) {
- StringBuffer message = new StringBuffer();
- message.append("Class " + prop + " lacks suggestions:" + FormatUtil.NEWLINE);
// format for copy & paste to properties file:
ArrayList<String> sorted = new ArrayList<String>(names);
// TODO: sort by package, then classname
Collections.sort(sorted);
- for(String remaining : sorted) {
- message.append("# " + remaining + FormatUtil.NEWLINE);
+ if(!update) {
+ StringBuilder message = new StringBuilder();
+ message.append("Class ").append(prop).append(" lacks suggestions:").append(FormatUtil.NEWLINE);
+ for(String remaining : sorted) {
+ message.append("# ").append(remaining).append(FormatUtil.NEWLINE);
+ }
+ LOG.warning(message.toString());
+ }
+ else {
+ // Try to automatically update:
+ URL f = getClass().getClassLoader().getResource(ELKIServiceLoader.PREFIX + cls.getName());
+ String fnam = f.getFile();
+ if(fnam == null) {
+ LOG.warning("Cannot update: " + f + " seems to be in a jar file.");
+ }
+ else {
+ try {
+ FileOutputStream out = new FileOutputStream(fnam, true);
+ PrintStream pr = new PrintStream(out);
+ pr.println();
+ pr.println("### Automatically appended entries:");
+ for(String remaining : sorted) {
+ pr.println(remaining);
+ }
+ pr.flush();
+ pr.close();
+ out.flush();
+ out.close();
+ LOG.warning("Updated: " + fnam);
+ }
+ catch(IOException e) {
+ LOG.exception(e);
+ }
+ }
}
- logger.warning(message.toString());
}
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/application/internal/CheckParameterizables.java b/src/de/lmu/ifi/dbs/elki/application/internal/CheckParameterizables.java
index b94ee47a..2e990bc8 100644
--- a/src/de/lmu/ifi/dbs/elki/application/internal/CheckParameterizables.java
+++ b/src/de/lmu/ifi/dbs/elki/application/internal/CheckParameterizables.java
@@ -48,7 +48,7 @@ public class CheckParameterizables {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(CheckParameterizables.class);
+ private static final Logging LOG = Logging.getLogger(CheckParameterizables.class);
/**
* Validate all "Parameterizable" objects for parts of the API contract that
@@ -63,7 +63,7 @@ public class CheckParameterizables {
constructor = cls.getDeclaredConstructor(Parameterization.class);
}
catch(NoClassDefFoundError e) {
- logger.verbose("Class discovered but not found?!? " + cls.getName());
+ LOG.verbose("Class discovered but not found?!? " + cls.getName());
// Not found ?!?
continue;
}
@@ -87,7 +87,7 @@ public class CheckParameterizables {
}
}
catch(Exception e) {
- logger.verbose("Could not run Parameterizer: " + inner.getName() + ": " + e);
+ LOG.verbose("Could not run Parameterizer: " + inner.getName() + ": " + e);
// continue. Probably non-public
}
}
@@ -100,7 +100,7 @@ public class CheckParameterizables {
// logger.debugFine("Found factory method for class: "+ cls.getName());
}
catch(NoClassDefFoundError e) {
- logger.verbose("Class discovered but not found?!? " + cls.getName());
+ LOG.verbose("Class discovered but not found?!? " + cls.getName());
// Not found ?!?
continue;
}
@@ -112,7 +112,7 @@ public class CheckParameterizables {
hasConstructor = true;
}
catch(NoClassDefFoundError e) {
- logger.verbose("Class discovered but not found?!? " + cls.getName());
+ LOG.verbose("Class discovered but not found?!? " + cls.getName());
// Not found ?!?
continue;
}
@@ -124,7 +124,7 @@ public class CheckParameterizables {
hasConstructor = true;
}
catch(NoClassDefFoundError e) {
- logger.verbose("Class discovered but not found?!? " + cls.getName());
+ LOG.verbose("Class discovered but not found?!? " + cls.getName());
// Not found ?!?
continue;
}
@@ -132,7 +132,7 @@ public class CheckParameterizables {
// do nothing.
}
if(!hasConstructor) {
- logger.verbose("Class " + cls.getName() + " is Parameterizable but doesn't have a constructor with the appropriate signature!");
+ LOG.verbose("Class " + cls.getName() + " is Parameterizable but doesn't have a constructor with the appropriate signature!");
}
}
}
@@ -159,10 +159,10 @@ public class CheckParameterizables {
}
}
catch(Exception e) {
- logger.warning("No proper Parameterizer.makeInstance for " + cls.getName() + ": " + e);
+ LOG.warning("No proper Parameterizer.makeInstance for " + cls.getName() + ": " + e);
return false;
}
- logger.warning("No proper Parameterizer.makeInstance for " + cls.getName() + " found!");
+ LOG.warning("No proper Parameterizer.makeInstance for " + cls.getName() + " found!");
return false;
}
@@ -170,10 +170,10 @@ public class CheckParameterizables {
// Classes in the same package are special and don't cause warnings.
if(!cls.getName().startsWith(Parameterizable.class.getPackage().getName())) {
if(!Modifier.isPublic(constructor.getModifiers())) {
- logger.verbose("Constructor for class " + cls.getName() + " is not public!");
+ LOG.verbose("Constructor for class " + cls.getName() + " is not public!");
}
if(!Parameterizable.class.isAssignableFrom(cls)) {
- logger.verbose("Class " + cls.getName() + " should implement Parameterizable!");
+ LOG.verbose("Class " + cls.getName() + " should implement Parameterizable!");
}
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/application/internal/DocumentParameters.java b/src/de/lmu/ifi/dbs/elki/application/internal/DocumentParameters.java
index c77e9dbb..f4e06c02 100644
--- a/src/de/lmu/ifi/dbs/elki/application/internal/DocumentParameters.java
+++ b/src/de/lmu/ifi/dbs/elki/application/internal/DocumentParameters.java
@@ -50,7 +50,9 @@ import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
+import de.lmu.ifi.dbs.elki.application.AbstractApplication;
import de.lmu.ifi.dbs.elki.logging.Logging;
+import de.lmu.ifi.dbs.elki.logging.LoggingConfiguration;
import de.lmu.ifi.dbs.elki.utilities.ClassGenericsUtil;
import de.lmu.ifi.dbs.elki.utilities.ELKIServiceLoader;
import de.lmu.ifi.dbs.elki.utilities.InspectionUtil;
@@ -77,7 +79,7 @@ import de.lmu.ifi.dbs.elki.utilities.xml.HTMLUtil;
* @apiviz.uses Parameter
*/
public class DocumentParameters {
- static final Logging logger = Logging.getLogger(DocumentParameters.class);
+ private static final Logging LOG = Logging.getLogger(DocumentParameters.class);
private static final String HEADER_PARAMETER_FOR = "Parameter for: ";
@@ -103,28 +105,29 @@ public class DocumentParameters {
* @param args Command line arguments
*/
public static void main(String[] args) {
+ LoggingConfiguration.setVerbose(true);
if(args.length != 2) {
- logger.warning("I need exactly two file names to operate!");
+ LOG.warning("I need exactly two file names to operate!");
System.exit(1);
}
if(!args[0].endsWith(".html")) {
- logger.warning("First file name doesn't end with .html!");
+ LOG.warning("First file name doesn't end with .html!");
System.exit(1);
}
if(!args[1].endsWith(".html")) {
- logger.warning("Second file name doesn't end with .html!");
+ LOG.warning("Second file name doesn't end with .html!");
System.exit(1);
}
File byclsname = new File(args[0]);
File byoptname = new File(args[1]);
- HashMapList<Class<?>, Parameter<?, ?>> byclass = new HashMapList<Class<?>, Parameter<?, ?>>();
- HashMapList<OptionID, Pair<Parameter<?, ?>, Class<?>>> byopt = new HashMapList<OptionID, Pair<Parameter<?, ?>, Class<?>>>();
+ HashMapList<Class<?>, Parameter<?>> byclass = new HashMapList<Class<?>, Parameter<?>>();
+ HashMapList<OptionID, Pair<Parameter<?>, Class<?>>> byopt = new HashMapList<OptionID, Pair<Parameter<?>, Class<?>>>();
try {
buildParameterIndex(byclass, byopt);
}
catch(Exception e) {
- logger.exception(e);
+ LOG.exception(e);
System.exit(1);
}
@@ -134,7 +137,7 @@ public class DocumentParameters {
byclassfo = new FileOutputStream(byclsname);
}
catch(FileNotFoundException e) {
- logger.exception("Can't create output stream!", e);
+ LOG.exception("Can't create output stream!", e);
throw new RuntimeException(e);
}
OutputStream byclassstream = new BufferedOutputStream(byclassfo);
@@ -146,7 +149,7 @@ public class DocumentParameters {
byclassfo.close();
}
catch(IOException e) {
- logger.exception("IO Exception writing output.", e);
+ LOG.exception("IO Exception writing output.", e);
throw new RuntimeException(e);
}
}
@@ -157,7 +160,7 @@ public class DocumentParameters {
byoptfo = new FileOutputStream(byoptname);
}
catch(FileNotFoundException e) {
- logger.exception("Can't create output stream!", e);
+ LOG.exception("Can't create output stream!", e);
throw new RuntimeException(e);
}
OutputStream byoptstream = new BufferedOutputStream(byoptfo);
@@ -169,22 +172,25 @@ public class DocumentParameters {
byoptfo.close();
}
catch(IOException e) {
- logger.exception("IO Exception writing output.", e);
+ LOG.exception("IO Exception writing output.", e);
throw new RuntimeException(e);
}
}
+ // Forcibly terminate, as some class may have screwed up.
+ System.exit(0);
}
- private static void buildParameterIndex(HashMapList<Class<?>, Parameter<?, ?>> byclass, HashMapList<OptionID, Pair<Parameter<?, ?>, Class<?>>> byopt) {
- final ArrayList<Pair<Object, Parameter<?, ?>>> options = new ArrayList<Pair<Object, Parameter<?, ?>>>();
+ private static void buildParameterIndex(HashMapList<Class<?>, Parameter<?>> byclass, HashMapList<OptionID, Pair<Parameter<?>, Class<?>>> byopt) {
+ final ArrayList<Pair<Object, Parameter<?>>> options = new ArrayList<Pair<Object, Parameter<?>>>();
ExecutorService es = Executors.newSingleThreadExecutor();
for(final Class<?> cls : InspectionUtil.findAllImplementations(Parameterizable.class, false)) {
// Doesn't have a proper name?
if(cls.getCanonicalName() == null) {
continue;
}
- // Special cases we need to skip...
- if(cls.getCanonicalName() == "experimentalcode.elke.AlgorithmTest") {
+ // Some of the "applications" do currently not have appropriate
+ // constructors / parameterizers and may start AWT threads - skip them.
+ if(AbstractApplication.class.isAssignableFrom(cls)) {
continue;
}
@@ -204,7 +210,7 @@ public class DocumentParameters {
ClassGenericsUtil.tryInstantiate(Object.class, cls, track);
}
catch(java.lang.NoSuchMethodException e) {
- logger.warning("Could not instantiate class " + cls.getName() + " - no appropriate constructor or parameterizer found.");
+ LOG.warning("Could not instantiate class " + cls.getName() + " - no appropriate constructor or parameterizer found.");
}
catch(java.lang.reflect.InvocationTargetException e) {
if(e.getCause() instanceof RuntimeException) {
@@ -225,7 +231,7 @@ public class DocumentParameters {
throw new RuntimeException(e);
}
}
- for(Pair<Object, Parameter<?, ?>> pair : track.getAllParameters()) {
+ for(Pair<Object, Parameter<?>> pair : track.getAllParameters()) {
if(pair.first == null) {
pair.first = cls;
}
@@ -236,35 +242,45 @@ public class DocumentParameters {
es.submit(instantiator);
try {
// Wait up to one second.
- instantiator.get(100000L, TimeUnit.MILLISECONDS);
+ instantiator.get(1L, TimeUnit.SECONDS);
}
catch(TimeoutException e) {
- de.lmu.ifi.dbs.elki.logging.LoggingUtil.warning("Timeout on instantiating " + cls.getName());
+ LOG.warning("Timeout on instantiating " + cls.getName());
es.shutdownNow();
throw new RuntimeException(e);
}
catch(java.util.concurrent.ExecutionException e) {
- de.lmu.ifi.dbs.elki.logging.LoggingUtil.warning("Error instantiating " + cls.getName(), e.getCause());
- /*
- * es.shutdownNow(); if(e.getCause() instanceof RuntimeException) {
- * throw (RuntimeException) e.getCause(); } throw new
- * RuntimeException(e.getCause());
- */
+ // Do full reporting only on release branch.
+ if(cls.getName().startsWith("de.lmu.ifi.dbs.elki")) {
+ LOG.warning("Error instantiating " + cls.getName(), e.getCause());
+ }
+ else {
+ LOG.warning("Error instantiating " + cls.getName());
+ }
+ // es.shutdownNow();
+ // if(e.getCause() instanceof RuntimeException) {
+ // throw (RuntimeException) e.getCause();
+ // }
+ // throw new RuntimeException(e.getCause());
continue;
}
catch(Exception e) {
- /*
- * de.lmu.ifi.dbs.elki.logging.LoggingUtil.warning("Error instantiating "
- * + cls.getName()); es.shutdownNow(); throw new RuntimeException(e);
- */
+ // Do full reporting only on release branch.
+ if(cls.getName().startsWith("de.lmu.ifi.dbs.elki")) {
+ LOG.warning("Error instantiating " + cls.getName(), e.getCause());
+ }
+ else {
+ LOG.warning("Error instantiating " + cls.getName());
+ }
+ // es.shutdownNow();
+ // throw new RuntimeException(e);
continue;
}
}
-
- logger.debug("Documenting " + options.size() + " parameter instances.");
- for(Pair<Object, Parameter<?, ?>> pp : options) {
+ LOG.debug("Documenting " + options.size() + " parameter instances.");
+ for(Pair<Object, Parameter<?>> pp : options) {
if(pp.first == null || pp.second == null) {
- logger.debugFiner("Null: " + pp.first + " " + pp.second);
+ LOG.debugFiner("Null: " + pp.first + " " + pp.second);
continue;
}
Class<?> c;
@@ -274,14 +290,14 @@ public class DocumentParameters {
else {
c = pp.first.getClass();
}
- Parameter<?, ?> o = pp.second;
+ Parameter<?> o = pp.second;
// just collect unique occurrences
{
- List<Parameter<?, ?>> byc = byclass.get(c);
+ List<Parameter<?>> byc = byclass.get(c);
boolean inlist = false;
if(byc != null) {
- for(Parameter<?, ?> par : byc) {
+ for(Parameter<?> par : byc) {
if(par.getOptionID() == o.getOptionID()) {
inlist = true;
break;
@@ -293,10 +309,10 @@ public class DocumentParameters {
}
}
{
- List<Pair<Parameter<?, ?>, Class<?>>> byo = byopt.get(o.getOptionID());
+ List<Pair<Parameter<?>, Class<?>>> byo = byopt.get(o.getOptionID());
boolean inlist = false;
if(byo != null) {
- for(Pair<Parameter<?, ?>, Class<?>> pair : byo) {
+ for(Pair<Parameter<?>, Class<?>> pair : byo) {
if(pair.second.equals(c)) {
inlist = true;
break;
@@ -304,12 +320,11 @@ public class DocumentParameters {
}
}
if(!inlist) {
- byopt.add(o.getOptionID(), new Pair<Parameter<?, ?>, Class<?>>(o, c));
+ byopt.add(o.getOptionID(), new Pair<Parameter<?>, Class<?>>(o, c));
}
}
}
- es.shutdownNow();
- logger.debug("byClass: " + byclass.size() + " byOpt: " + byopt.size());
+ LOG.debug("byClass: " + byclass.size() + " byOpt: " + byopt.size());
}
protected static Constructor<?> getConstructor(final Class<?> cls) {
@@ -321,19 +336,19 @@ public class DocumentParameters {
}
catch(RuntimeException e) {
// Not parameterizable, usually not even found ...
- logger.warning("RuntimeException: ", e);
+ LOG.warning("RuntimeException: ", e);
}
catch(Exception e) {
// Not parameterizable.
}
catch(java.lang.Error e) {
// Not parameterizable.
- logger.warning("Error: ", e);
+ LOG.warning("Error: ", e);
}
return null;
}
- private static Document makeByClassOverview(HashMapList<Class<?>, Parameter<?, ?>> byclass) {
+ private static Document makeByClassOverview(HashMapList<Class<?>, Parameter<?>> byclass) {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder;
try {
@@ -415,7 +430,7 @@ public class DocumentParameters {
// nested definition list for options
Element classdl = htmldoc.createElement(HTMLUtil.HTML_DL_TAG);
classdd.appendChild(classdl);
- for(Parameter<?, ?> opt : byclass.get(cls)) {
+ for(Parameter<?> opt : byclass.get(cls)) {
// DT definition term: option name, in TT for typewriter optics
Element elemdt = htmldoc.createElement(HTMLUtil.HTML_DT_TAG);
{
@@ -447,7 +462,7 @@ public class DocumentParameters {
return htmldoc;
}
- private static Document makeByOptOverview(HashMapList<OptionID, Pair<Parameter<?, ?>, Class<?>>> byopt) {
+ private static Document makeByOptOverview(HashMapList<OptionID, Pair<Parameter<?>, Class<?>>> byopt) {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder;
try {
@@ -507,7 +522,7 @@ public class DocumentParameters {
Collections.sort(opts, new SortByOption());
for(OptionID oid : opts) {
- final Parameter<?, ?> firstopt = byopt.get(oid).get(0).getFirst();
+ final Parameter<?> firstopt = byopt.get(oid).get(0).getFirst();
// DT = definition term
Element optdt = htmldoc.createElement(HTMLUtil.HTML_DT_TAG);
// Anchor for references
@@ -535,9 +550,9 @@ public class DocumentParameters {
if(firstopt instanceof ClassParameter<?>) {
// Find superclass heuristically
superclass = ((ClassParameter<?>) firstopt).getRestrictionClass();
- for (Pair<Parameter<?, ?>, Class<?>> clinst : byopt.get(oid)) {
+ for(Pair<Parameter<?>, Class<?>> clinst : byopt.get(oid)) {
ClassParameter<?> cls = (ClassParameter<?>) clinst.getFirst();
- if (!cls.getRestrictionClass().equals(superclass) && cls.getRestrictionClass().isAssignableFrom(superclass)) {
+ if(!cls.getRestrictionClass().equals(superclass) && cls.getRestrictionClass().isAssignableFrom(superclass)) {
superclass = cls.getRestrictionClass();
}
}
@@ -558,7 +573,7 @@ public class DocumentParameters {
optdd.appendChild(p);
}
optdd.appendChild(classesul);
- for(Pair<Parameter<?, ?>, Class<?>> clinst : byopt.get(oid)) {
+ for(Pair<Parameter<?>, Class<?>> clinst : byopt.get(oid)) {
// DT definition term: option name, in TT for typewriter optics
Element classli = htmldoc.createElement(HTMLUtil.HTML_LI_TAG);
@@ -581,7 +596,7 @@ public class DocumentParameters {
appendNoClassRestriction(htmldoc, classli);
}
}
- Parameter<?, ?> param = clinst.getFirst();
+ Parameter<?> param = clinst.getFirst();
if(param.getDefaultValue() != null) {
if(!param.getDefaultValue().equals(firstopt.getDefaultValue())) {
appendDefaultValueIfSet(htmldoc, param, classli);
@@ -599,7 +614,7 @@ public class DocumentParameters {
return htmldoc;
}
- private static void appendDefaultClassLink(Document htmldoc, Parameter<?, ?> opt, Element p) {
+ private static void appendDefaultClassLink(Document htmldoc, Parameter<?> opt, Element p) {
Element defa = htmldoc.createElement(HTMLUtil.HTML_A_TAG);
defa.setAttribute(HTMLUtil.HTML_HREF_ATTRIBUTE, linkForClassName(((ClassParameter<?>) opt).getDefaultValue().getCanonicalName()));
defa.setTextContent(((ClassParameter<?>) opt).getDefaultValue().getCanonicalName());
@@ -608,7 +623,7 @@ public class DocumentParameters {
private static void appendClassRestriction(Document htmldoc, Class<?> restriction, Element elemdd) {
if(restriction == null) {
- logger.warning("No restriction class!");
+ LOG.warning("No restriction class!");
return;
}
Element p = htmldoc.createElement(HTMLUtil.HTML_P_TAG);
@@ -653,8 +668,8 @@ public class DocumentParameters {
}
// Report when not in properties file.
Iterator<Class<?>> clss = new ELKIServiceLoader(opt.getRestrictionClass());
- if (!clss.hasNext()) {
- logger.warning(opt.getRestrictionClass().getName() + " not in properties. No autocompletion available in release GUI.");
+ if(!clss.hasNext()) {
+ LOG.warning(opt.getRestrictionClass().getName() + " not in properties. No autocompletion available in release GUI.");
}
}
}
@@ -666,7 +681,7 @@ public class DocumentParameters {
* @param par Parameter
* @param optdd HTML Element
*/
- private static void appendDefaultValueIfSet(Document htmldoc, Parameter<?, ?> par, Element optdd) {
+ private static void appendDefaultValueIfSet(Document htmldoc, Parameter<?> par, Element optdd) {
if(par.hasDefaultValue()) {
Element p = htmldoc.createElement(HTMLUtil.HTML_P_TAG);
p.appendChild(htmldoc.createTextNode(HEADER_DEFAULT_VALUE));
diff --git a/src/de/lmu/ifi/dbs/elki/application/internal/DocumentReferences.java b/src/de/lmu/ifi/dbs/elki/application/internal/DocumentReferences.java
index 2e002243..eaf05425 100644
--- a/src/de/lmu/ifi/dbs/elki/application/internal/DocumentReferences.java
+++ b/src/de/lmu/ifi/dbs/elki/application/internal/DocumentReferences.java
@@ -66,7 +66,7 @@ public class DocumentReferences {
/**
* Logger
*/
- private static final Logging logger = Logging.getLogger(DocumentReferences.class);
+ private static final Logging LOG = Logging.getLogger(DocumentReferences.class);
/**
* @param args Command line arguments
@@ -316,11 +316,11 @@ public class DocumentReferences {
}
catch(NoClassDefFoundError e) {
if(!cls.getCanonicalName().startsWith("experimentalcode.")) {
- logger.warning("Exception in finding references for class " + cls.getCanonicalName() + " - missing referenced class?");
+ LOG.warning("Exception in finding references for class " + cls.getCanonicalName() + " - missing referenced class?");
}
}
catch(Error e) {
- logger.warning("Exception in finding references for class " + cls.getCanonicalName() + ": " + e, e);
+ LOG.warning("Exception in finding references for class " + cls.getCanonicalName() + ": " + e, e);
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/application/jsmap/JSONBuffer.java b/src/de/lmu/ifi/dbs/elki/application/jsmap/JSONBuffer.java
index c077e9d3..2458d39a 100644
--- a/src/de/lmu/ifi/dbs/elki/application/jsmap/JSONBuffer.java
+++ b/src/de/lmu/ifi/dbs/elki/application/jsmap/JSONBuffer.java
@@ -30,14 +30,14 @@ import java.util.Stack;
*
* @author Erich Schubert
*
- * @apiviz.composedOf StringBuffer
+ * @apiviz.composedOf StringBuilder
* @apiviz.has JSONBuffer.JSONException
*/
public class JSONBuffer {
/**
* The actual buffer we serialize to
*/
- final StringBuffer buffer;
+ final StringBuilder buffer;
/**
* Operations on the stack.
@@ -58,7 +58,7 @@ public class JSONBuffer {
*
* @param buffer Buffer to serialize to
*/
- public JSONBuffer(StringBuffer buffer) {
+ public JSONBuffer(StringBuilder buffer) {
this.buffer = buffer;
}
diff --git a/src/de/lmu/ifi/dbs/elki/application/jsmap/JSONResultHandler.java b/src/de/lmu/ifi/dbs/elki/application/jsmap/JSONResultHandler.java
index 7fc26496..217010da 100644
--- a/src/de/lmu/ifi/dbs/elki/application/jsmap/JSONResultHandler.java
+++ b/src/de/lmu/ifi/dbs/elki/application/jsmap/JSONResultHandler.java
@@ -28,7 +28,8 @@ import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.result.ResultHandler;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.IntervalConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterEqualConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.LessEqualConstraint;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
@@ -80,7 +81,7 @@ public class JSONResultHandler implements ResultHandler {
/**
* Port to use for listening
*/
- public static final OptionID PORT_ID = OptionID.getOrCreateOptionID("json.port", "Port for the JSON web server to listen on.");
+ public static final OptionID PORT_ID = new OptionID("json.port", "Port for the JSON web server to listen on.");
/**
* Our port
@@ -90,7 +91,9 @@ public class JSONResultHandler implements ResultHandler {
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- IntParameter portP = new IntParameter(PORT_ID, new IntervalConstraint(1, IntervalConstraint.IntervalBoundary.CLOSE, 65535, IntervalConstraint.IntervalBoundary.CLOSE), port);
+ IntParameter portP = new IntParameter(PORT_ID, port);
+ portP.addConstraint(new GreaterEqualConstraint(1));
+ portP.addConstraint(new LessEqualConstraint(65535));
if(config.grab(portP)) {
this.port = portP.getValue();
}
diff --git a/src/de/lmu/ifi/dbs/elki/application/jsmap/JSONWebServer.java b/src/de/lmu/ifi/dbs/elki/application/jsmap/JSONWebServer.java
index 2f51c743..bcf628d0 100644
--- a/src/de/lmu/ifi/dbs/elki/application/jsmap/JSONWebServer.java
+++ b/src/de/lmu/ifi/dbs/elki/application/jsmap/JSONWebServer.java
@@ -42,6 +42,7 @@ import de.lmu.ifi.dbs.elki.data.spatial.PolygonsObject;
import de.lmu.ifi.dbs.elki.database.Database;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
@@ -65,27 +66,27 @@ import de.lmu.ifi.dbs.elki.utilities.exceptions.AbortException;
*/
public class JSONWebServer implements HttpHandler {
/**
- * Our logger
+ * Our logger.
*/
- protected final static Logging logger = Logging.getLogger(JSONWebServer.class);
+ private static final Logging LOG = Logging.getLogger(JSONWebServer.class);
/**
- * The base path we serve data from
+ * The base path we serve data from.
*/
- public final static String PATH_JSON = "/json/";
+ public static final String PATH_JSON = "/json/";
/**
- * Server instance
+ * Server instance.
*/
private HttpServer server;
/**
- * The result tree we serve
+ * The result tree we serve.
*/
private HierarchicalResult result;
/**
- * The database we use for obtaining object bundles
+ * The database we use for obtaining object bundles.
*/
private Database db;
@@ -109,7 +110,7 @@ public class JSONWebServer implements HttpHandler {
server.setExecutor(Executors.newCachedThreadPool());
server.start();
- logger.verbose("Webserver started on port " + port + ".");
+ LOG.verbose("Webserver started on port " + port + ".");
}
catch(IOException e) {
throw new AbortException("Could not start mini web server.", e);
@@ -139,7 +140,7 @@ public class JSONWebServer implements HttpHandler {
* @param re Buffer to serialize to
* @param id Object ID
*/
- protected void bundleToJSON(JSONBuffer re, DBID id) {
+ protected void bundleToJSON(JSONBuffer re, DBIDRef id) {
SingleObjectBundle bundle = db.getBundle(id);
if(bundle != null) {
for(int j = 0; j < bundle.metaLength(); j++) {
@@ -147,10 +148,10 @@ public class JSONWebServer implements HttpHandler {
// TODO: refactor to JSONFormatters!
// Format a NumberVector
if(data instanceof NumberVector) {
- NumberVector<?, ?> v = (NumberVector<?, ?>) data;
+ NumberVector<?> v = (NumberVector<?>) data;
re.appendKeyArray(bundle.meta(j));
for(int i = 0; i < v.getDimensionality(); i++) {
- re.append(v.doubleValue(i + 1));
+ re.append(v.doubleValue(i));
}
re.closeArray();
}
@@ -171,7 +172,7 @@ public class JSONWebServer implements HttpHandler {
else {
re.appendKeyValue(bundle.meta(j), data);
}
- if(logger.isDebuggingFiner()) {
+ if(LOG.isDebuggingFiner()) {
re.appendNewline();
}
}
@@ -307,7 +308,7 @@ public class JSONWebServer implements HttpHandler {
re.appendKeyValue("offset", offset);
re.appendKeyValue("pagesize", pagesize);
re.closeHash();
- if(logger.isDebuggingFiner()) {
+ if(LOG.isDebuggingFiner()) {
re.appendNewline();
}
@@ -322,10 +323,9 @@ public class JSONWebServer implements HttpHandler {
iter.advance();
}
for(int i = 0; i < pagesize && iter.valid(); i++, iter.advance()) {
- DBID id = iter.getDBID();
re.startHash();
- bundleToJSON(re, id);
- final Double val = scores.get(id);
+ bundleToJSON(re, iter);
+ final Double val = scores.get(iter);
if(val != null) {
re.appendKeyValue("score", val);
}
@@ -354,7 +354,7 @@ public class JSONWebServer implements HttpHandler {
re.appendKeyValue("base", meta.getTheoreticalBaseline());
re.appendKeyValue("type", meta.getClass().getSimpleName());
re.closeHash();
- if(logger.isDebuggingFiner()) {
+ if(LOG.isDebuggingFiner()) {
re.appendNewline();
}
}
@@ -371,7 +371,7 @@ public class JSONWebServer implements HttpHandler {
path = path.substring(PATH_JSON.length());
}
else {
- logger.warning("Unexpected path in request handler: " + path);
+ LOG.warning("Unexpected path in request handler: " + path);
throw new AbortException("Unexpected path: " + path);
}
@@ -396,7 +396,7 @@ public class JSONWebServer implements HttpHandler {
}
// Prepare JSON response.
- StringBuffer response = new StringBuffer();
+ StringBuilder response = new StringBuilder();
{
if(callback != null) {
response.append(callback);
@@ -411,7 +411,7 @@ public class JSONWebServer implements HttpHandler {
jsonbuf.closeHash();
}
catch(Throwable e) {
- logger.exception("Exception occurred in embedded web server:", e);
+ LOG.exception("Exception occurred in embedded web server:", e);
throw (new IOException(e));
}
// wrap up
diff --git a/src/de/lmu/ifi/dbs/elki/application/visualization/KNNExplorer.java b/src/de/lmu/ifi/dbs/elki/application/visualization/KNNExplorer.java
index c7f6d266..34daefa2 100644
--- a/src/de/lmu/ifi/dbs/elki/application/visualization/KNNExplorer.java
+++ b/src/de/lmu/ifi/dbs/elki/application/visualization/KNNExplorer.java
@@ -58,13 +58,16 @@ import de.lmu.ifi.dbs.elki.database.Database;
import de.lmu.ifi.dbs.elki.database.StaticArrayDatabase;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
+import de.lmu.ifi.dbs.elki.database.ids.DistanceDBIDPair;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.database.query.knn.KNNQuery;
-import de.lmu.ifi.dbs.elki.database.query.knn.KNNResult;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
import de.lmu.ifi.dbs.elki.distance.distancefunction.EuclideanDistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance;
import de.lmu.ifi.dbs.elki.logging.LoggingUtil;
import de.lmu.ifi.dbs.elki.math.DoubleMinMax;
@@ -127,7 +130,7 @@ import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
* @param <D> Distance type
*/
@Reference(authors = "E. Achtert, T. Bernecker, H.-P. Kriegel, E. Schubert, A. Zimek", title = "ELKI in Time: ELKI 0.2 for the Performance Evaluation of Distance Measures for Time Series", booktitle = "Proceedings of the 11th International Symposium on Spatial and Temporal Databases (SSTD), Aalborg, Denmark, 2009", url = "http://dx.doi.org/10.1007/978-3-642-02982-0_35")
-public class KNNExplorer<O extends NumberVector<?, ?>, D extends NumberDistance<D, ?>> extends AbstractApplication {
+public class KNNExplorer<O extends NumberVector<?>, D extends NumberDistance<D, ?>> extends AbstractApplication {
// TODO: replace by a wrapper creating appropriate visualization modules.
/**
@@ -142,7 +145,7 @@ public class KNNExplorer<O extends NumberVector<?, ?>, D extends NumberDistance<
* {@link de.lmu.ifi.dbs.elki.distance.distancefunction.EuclideanDistanceFunction}
* </p>
*/
- public static final OptionID DISTANCE_FUNCTION_ID = OptionID.getOrCreateOptionID("explorer.distancefunction", "Distance function to determine the distance between database objects.");
+ public static final OptionID DISTANCE_FUNCTION_ID = new OptionID("explorer.distancefunction", "Distance function to determine the distance between database objects.");
/**
* Holds the database connection to have the algorithm run with.
@@ -158,12 +161,11 @@ public class KNNExplorer<O extends NumberVector<?, ?>, D extends NumberDistance<
/**
* Constructor.
*
- * @param verbose Verbose flag
* @param database Database
* @param distanceFunction Distance function
*/
- public KNNExplorer(boolean verbose, Database database, DistanceFunction<O, D> distanceFunction) {
- super(verbose);
+ public KNNExplorer(Database database, DistanceFunction<O, D> distanceFunction) {
+ super();
this.database = database;
this.distanceFunction = distanceFunction;
}
@@ -370,15 +372,14 @@ public class KNNExplorer<O extends NumberVector<?, ?>, D extends NumberDistance<
this.db = db;
this.data = distanceQuery.getRelation();
this.labelRep = DatabaseUtil.guessObjectLabelRepresentation(db);
- this.dim = DatabaseUtil.dimensionality(distanceQuery.getRelation());
+ this.dim = RelationUtil.dimensionality(distanceQuery.getRelation());
this.distanceQuery = distanceQuery;
this.updateK(k);
double min = Double.MAX_VALUE;
double max = Double.MIN_VALUE;
for(DBIDIter iditer = data.iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID objID = iditer.getDBID();
- O vec = data.get(objID);
+ O vec = data.get(iditer);
DoubleMinMax mm = VectorUtil.getRangeDouble(vec);
min = Math.min(min, mm.getMin());
max = Math.max(max, mm.getMax());
@@ -411,8 +412,7 @@ public class KNNExplorer<O extends NumberVector<?, ?>, D extends NumberDistance<
DefaultListModel m = new DefaultListModel();
for(DBIDIter iditer = data.iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID dbid = iditer.getDBID();
- m.addElement(dbid);
+ m.addElement(DBIDUtil.deref(iditer));
}
seriesList.setModel(m);
@@ -447,18 +447,18 @@ public class KNNExplorer<O extends NumberVector<?, ?>, D extends NumberDistance<
}
for (int i = knn.size() - 1; i >= 0; i--) {
- DistanceResultPair<D> pair = knn.get(i);
- Element line = plotSeries(pair.getDBID(), MAXRESOLUTION);
+ DistanceDBIDPair<D> pair = knn.get(i);
+ Element line = plotSeries(pair, MAXRESOLUTION);
double dist = pair.getDistance().doubleValue() / maxdist;
Color color = getColor(dist);
String colstr = "#" + Integer.toHexString(color.getRGB()).substring(2);
- String width = (pair.getDBID().sameDBID(idx)) ? "0.5%" : "0.2%";
+ String width = (DBIDUtil.equal(pair, idx)) ? "0.5%" : "0.2%";
SVGUtil.setStyle(line, "stroke: " + colstr + "; stroke-width: " + width + "; fill: none");
newe.appendChild(line);
// put into cache
- Double known = distancecache.get(pair.getDBID());
+ Double known = distancecache.get(DBIDUtil.deref(pair));
if(known == null || dist < known) {
- distancecache.put(pair.getDBID(), dist);
+ distancecache.put(DBIDUtil.deref(pair), dist);
}
}
}
@@ -484,7 +484,7 @@ public class KNNExplorer<O extends NumberVector<?, ?>, D extends NumberDistance<
* @param resolution Maximum number of steps to plot
* @return SVG element
*/
- private Element plotSeries(DBID idx, int resolution) {
+ private Element plotSeries(DBIDRef idx, int resolution) {
O series = data.get(idx);
double step = 1.0;
@@ -495,7 +495,7 @@ public class KNNExplorer<O extends NumberVector<?, ?>, D extends NumberDistance<
SVGPath path = new SVGPath();
for(double id = 0; id < dim; id += step) {
int i = (int) Math.floor(id);
- path.drawTo(StyleLibrary.SCALE * ratio * (((double) i) / (dim - 1)), StyleLibrary.SCALE * (1.0 - s.getScaled(series.doubleValue(i + 1))));
+ path.drawTo(StyleLibrary.SCALE * ratio * (((double) i) / (dim - 1)), StyleLibrary.SCALE * (1.0 - s.getScaled(series.doubleValue(i))));
}
Element p = path.makeElement(plot);
return p;
@@ -548,7 +548,7 @@ public class KNNExplorer<O extends NumberVector<?, ?>, D extends NumberDistance<
*
* @apiviz.exclude
*/
- public static class Parameterizer<O extends NumberVector<?, ?>, D extends NumberDistance<D, ?>> extends AbstractApplication.Parameterizer {
+ public static class Parameterizer<O extends NumberVector<?>, D extends NumberDistance<D, ?>> extends AbstractApplication.Parameterizer {
protected Database database = null;
protected DistanceFunction<O, D> distanceFunction = null;
@@ -571,7 +571,7 @@ public class KNNExplorer<O extends NumberVector<?, ?>, D extends NumberDistance<
@Override
protected KNNExplorer<O, D> makeInstance() {
- return new KNNExplorer<O, D>(verbose, database, distanceFunction);
+ return new KNNExplorer<O, D>(database, distanceFunction);
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/data/AbstractNumberVector.java b/src/de/lmu/ifi/dbs/elki/data/AbstractNumberVector.java
index eb783ab5..95e81891 100644
--- a/src/de/lmu/ifi/dbs/elki/data/AbstractNumberVector.java
+++ b/src/de/lmu/ifi/dbs/elki/data/AbstractNumberVector.java
@@ -1,7 +1,5 @@
package de.lmu.ifi.dbs.elki.data;
-import de.lmu.ifi.dbs.elki.utilities.datastructures.arraylike.ArrayLikeUtil;
-
/*
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
@@ -25,54 +23,21 @@ import de.lmu.ifi.dbs.elki.utilities.datastructures.arraylike.ArrayLikeUtil;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+import de.lmu.ifi.dbs.elki.utilities.datastructures.arraylike.ArrayLikeUtil;
+
/**
* AbstractNumberVector is an abstract implementation of FeatureVector.
*
* @author Arthur Zimek
- * @param <V> the concrete type of this AbstractNumberVector
- * @param <N> the type of number, this AbstractNumberVector consists of (i.e., a
- * AbstractNumberVector {@code v} of type {@code V} and dimensionality
- * {@code d} is an element of {@code N}<sup>{@code d}</sup>)
+ *
+ * @param <N> the type of number stored in this vector
*/
-public abstract class AbstractNumberVector<V extends AbstractNumberVector<? extends V, N>, N extends Number> implements NumberVector<V, N> {
+public abstract class AbstractNumberVector<N extends Number> implements NumberVector<N> {
/**
* The String to separate attribute values in a String that represents the
* values.
*/
- public final static String ATTRIBUTE_SEPARATOR = " ";
-
- /**
- * An Object obj is equal to this AbstractNumberVector if it is an instance of
- * the same runtime class and is of the identical dimensionality and the
- * values of this AbstractNumberVector are equal to the values of obj in all
- * dimensions, respectively.
- *
- * @param obj another Object
- * @return true if the specified Object is an instance of the same runtime
- * class and is of the identical dimensionality and the values of this
- * AbstractNumberVector are equal to the values of obj in all
- * dimensions, respectively
- */
- @SuppressWarnings("unchecked")
- @Override
- public boolean equals(Object obj) {
- if(this.getClass().isInstance(obj)) {
- V rv = (V) obj;
- boolean equal = (this.getDimensionality() == rv.getDimensionality());
- for(int i = 1; i <= getDimensionality() && equal; i++) {
- equal &= this.getValue(i).equals(rv.getValue(i));
- }
- return equal;
- }
- else {
- return false;
- }
- }
-
- @Override
- public int hashCode() {
- return super.hashCode();
- }
+ public static final String ATTRIBUTE_SEPARATOR = " ";
@Override
public double getMin(int dimension) {
@@ -104,8 +69,20 @@ public abstract class AbstractNumberVector<V extends AbstractNumberVector<? exte
return (short) longValue(dimension);
}
- @Override
- public V newNumberVector(double[] values) {
- return newNumberVector(values, ArrayLikeUtil.doubleArrayAdapter());
+ /**
+ * Factory class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.has AbstractNumberVector
+ *
+ * @param <V> Vector type
+ * @param <N> Number type
+ */
+ public abstract static class Factory<V extends AbstractNumberVector<N>, N extends Number> implements NumberVector.Factory<V, N> {
+ @Override
+ public V newNumberVector(double[] values) {
+ return newNumberVector(values, ArrayLikeUtil.doubleArrayAdapter());
+ }
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/data/Arithmetic.java b/src/de/lmu/ifi/dbs/elki/data/Arithmetic.java
index 75c3cad8..534f7717 100644
--- a/src/de/lmu/ifi/dbs/elki/data/Arithmetic.java
+++ b/src/de/lmu/ifi/dbs/elki/data/Arithmetic.java
@@ -39,7 +39,7 @@ public interface Arithmetic<N extends Number> extends Comparable<N> {
* @return the result of arithmetic addition of this Number with the given
* number
*/
- public N plus(N number);
+ N plus(N number);
/**
* Multiplies this number with the given number.
@@ -48,7 +48,7 @@ public interface Arithmetic<N extends Number> extends Comparable<N> {
* @return the result of arithmetic multiplication of this Number with the
* given number
*/
- public N times(N number);
+ N times(N number);
/**
* Subtracts the given number from this number.
@@ -57,7 +57,7 @@ public interface Arithmetic<N extends Number> extends Comparable<N> {
* @return the result of arithmetic subtraction of the given number from this
* Number
*/
- public N minus(N number);
+ N minus(N number);
/**
* Divides this number by the given number.
@@ -66,5 +66,5 @@ public interface Arithmetic<N extends Number> extends Comparable<N> {
* @return the result of arithmetic division of this Number by the given
* number
*/
- public N divided(N number);
-}
+ N divided(N number);
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/data/BitVector.java b/src/de/lmu/ifi/dbs/elki/data/BitVector.java
index 4756fef4..d662af66 100644
--- a/src/de/lmu/ifi/dbs/elki/data/BitVector.java
+++ b/src/de/lmu/ifi/dbs/elki/data/BitVector.java
@@ -39,21 +39,26 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
*
* @author Arthur Zimek
*/
-public class BitVector extends AbstractNumberVector<BitVector, Bit> implements ByteBufferSerializer<BitVector> {
+public class BitVector extends AbstractNumberVector<Bit> {
/**
* Static instance.
*/
- public static final BitVector STATIC = new BitVector(new BitSet(0), 0);
+ public static final BitVector.Factory FACTORY = new BitVector.Factory();
+
+ /**
+ * Serializer for up to 2^15-1 dimensions.
+ */
+ public static final ByteBufferSerializer<BitVector> SHORT_SERIALIZER = new ShortSerializer();
/**
* Storing the bits.
*/
- private BitSet bits;
+ private final BitSet bits;
/**
* Dimensionality of this bit vector.
*/
- private int dimensionality;
+ private final int dimensionality;
/**
* Provides a new BitVector corresponding to the specified bits and of the
@@ -65,7 +70,7 @@ public class BitVector extends AbstractNumberVector<BitVector, Bit> implements B
* small to match the given BitSet
*/
public BitVector(BitSet bits, int dimensionality) throws IllegalArgumentException {
- if(dimensionality < bits.length()) {
+ if (dimensionality < bits.length()) {
throw new IllegalArgumentException("Specified dimensionality " + dimensionality + " is to low for specified BitSet of length " + bits.length());
}
this.bits = bits;
@@ -79,72 +84,40 @@ public class BitVector extends AbstractNumberVector<BitVector, Bit> implements B
*/
public BitVector(Bit[] bits) {
this.bits = new BitSet(bits.length);
- for(int i = 0; i < bits.length; i++) {
+ for (int i = 0; i < bits.length; i++) {
this.bits.set(i, bits[i].bitValue());
}
this.dimensionality = bits.length;
}
- /**
- * The dimensionality of the binary vector space of which this BitVector is an
- * element.
- *
- * @see de.lmu.ifi.dbs.elki.data.NumberVector#getDimensionality()
- */
@Override
public int getDimensionality() {
return dimensionality;
}
- /**
- * Returns the value in the specified dimension.
- *
- * @param dimension the desired dimension, where 1 &le; dimension &le;
- * <code>this.getDimensionality()</code>
- * @return the value in the specified dimension
- *
- * @see de.lmu.ifi.dbs.elki.data.NumberVector#getValue(int)
- */
@Override
+ @Deprecated
public Bit getValue(int dimension) {
- if(dimension < 1 || dimension > dimensionality) {
+ if (dimension < 1 || dimension > dimensionality) {
throw new IllegalArgumentException("illegal dimension: " + dimension);
}
return new Bit(bits.get(dimension - 1));
}
- /**
- * Returns the value in the specified dimension as double.
- *
- * @param dimension the desired dimension, where 1 &le; dimension &le;
- * <code>this.getDimensionality()</code>
- * @return the value in the specified dimension
- *
- * @see de.lmu.ifi.dbs.elki.data.NumberVector#doubleValue(int)
- */
@Override
public double doubleValue(int dimension) {
- if(dimension < 1 || dimension > dimensionality) {
+ if (dimension < 0 || dimension >= dimensionality) {
throw new IllegalArgumentException("illegal dimension: " + dimension);
}
- return bits.get(dimension - 1) ? 1.0 : 0.0;
+ return bits.get(dimension) ? 1.0 : 0.0;
}
- /**
- * Returns the value in the specified dimension as long.
- *
- * @param dimension the desired dimension, where 1 &le; dimension &le;
- * <code>this.getDimensionality()</code>
- * @return the value in the specified dimension
- *
- * @see de.lmu.ifi.dbs.elki.data.NumberVector#longValue(int)
- */
@Override
public long longValue(int dimension) {
- if(dimension < 1 || dimension > dimensionality) {
+ if (dimension < 0 || dimension >= dimensionality) {
throw new IllegalArgumentException("illegal dimension: " + dimension);
}
- return bits.get(dimension - 1) ? 1 : 0;
+ return bits.get(dimension) ? 1 : 0;
}
/**
@@ -161,7 +134,7 @@ public class BitVector extends AbstractNumberVector<BitVector, Bit> implements B
@Override
public Vector getColumnVector() {
double[] values = new double[dimensionality];
- for(int i = 0; i < dimensionality; i++) {
+ for (int i = 0; i < dimensionality; i++) {
values[i] = bits.get(i) ? 1 : 0;
}
return new Vector(values);
@@ -177,7 +150,7 @@ public class BitVector extends AbstractNumberVector<BitVector, Bit> implements B
*/
public boolean contains(BitSet bitset) {
boolean contains = true;
- for(int i = bitset.nextSetBit(0); i >= 0 && contains; i = bitset.nextSetBit(i + 1)) {
+ for (int i = bitset.nextSetBit(0); i >= 0 && contains; i = bitset.nextSetBit(i + 1)) {
// noinspection ConstantConditions
contains &= bits.get(i);
}
@@ -199,17 +172,17 @@ public class BitVector extends AbstractNumberVector<BitVector, Bit> implements B
* {@link de.lmu.ifi.dbs.elki.datasource.parser.BitVectorLabelParser
* BitVectorLabelParser}.
*
- * @see Object#toString()
+ * {@inheritDoc}
*/
@Override
public String toString() {
Bit[] bitArray = new Bit[dimensionality];
- for(int i = 0; i < dimensionality; i++) {
+ for (int i = 0; i < dimensionality; i++) {
bitArray[i] = new Bit(bits.get(i));
}
- StringBuffer representation = new StringBuffer();
- for(Bit bit : bitArray) {
- if(representation.length() > 0) {
+ StringBuilder representation = new StringBuilder();
+ for (Bit bit : bitArray) {
+ if (representation.length() > 0) {
representation.append(ATTRIBUTE_SEPARATOR);
}
representation.append(bit.toString());
@@ -221,110 +194,140 @@ public class BitVector extends AbstractNumberVector<BitVector, Bit> implements B
* Indicates whether some other object is "equal to" this BitVector. This
* BitVector is equal to the given object, if the object is a BitVector of
* same dimensionality and with identical bits set.
+ *
+ * {@inheritDoc}
*/
@Override
public boolean equals(Object obj) {
- if(obj instanceof BitVector) {
+ if (obj instanceof BitVector) {
BitVector bv = (BitVector) obj;
return this.getDimensionality() == bv.getDimensionality() && this.bits.equals(bv.bits);
- }
- else {
+ } else {
return false;
}
}
- @Override
- public <A> BitVector newFeatureVector(A array, ArrayAdapter<Bit, A> adapter) {
- int dim = adapter.size(array);
- BitSet bits = new BitSet(dim);
- for(int i = 0; i < dim; i++) {
- bits.set(i, adapter.get(array, i).bitValue());
- i++;
+ /**
+ * Factory for bit vectors.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.has BitVector
+ */
+ public static class Factory extends AbstractNumberVector.Factory<BitVector, Bit> {
+ @Override
+ public <A> BitVector newFeatureVector(A array, ArrayAdapter<Bit, A> adapter) {
+ int dim = adapter.size(array);
+ BitSet bits = new BitSet(dim);
+ for (int i = 0; i < dim; i++) {
+ bits.set(i, adapter.get(array, i).bitValue());
+ i++;
+ }
+ return new BitVector(bits, dim);
}
- return new BitVector(bits, dim);
- }
- @Override
- public <A> BitVector newNumberVector(A array, NumberArrayAdapter<?, A> adapter) {
- int dim = adapter.size(array);
- BitSet bits = new BitSet(dim);
- for(int i = 0; i < dim; i++) {
- if(adapter.getDouble(array, i) >= 0.5) {
- bits.set(i);
+ @Override
+ public <A> BitVector newNumberVector(A array, NumberArrayAdapter<?, ? super A> adapter) {
+ int dim = adapter.size(array);
+ BitSet bits = new BitSet(dim);
+ for (int i = 0; i < dim; i++) {
+ if (adapter.getDouble(array, i) >= 0.5) {
+ bits.set(i);
+ }
}
+ return new BitVector(bits, dim);
}
- return new BitVector(bits, dim);
- }
- @Override
- public BitVector fromByteBuffer(ByteBuffer buffer) throws IOException {
- short dimensionality = buffer.getShort();
- final int len = ByteArrayUtil.SIZE_SHORT + (dimensionality + 7) / 8;
- if(buffer.remaining() < len) {
- throw new IOException("Not enough data for a bit vector!");
+ @Override
+ public ByteBufferSerializer<BitVector> getDefaultSerializer() {
+ return SHORT_SERIALIZER;
}
- // read values
- BitSet values = new BitSet(dimensionality);
- byte b = 0;
- for(int i = 0; i < dimensionality; i++) {
- // read the next byte when needed.
- if((i & 7) == 0) {
- b = buffer.get();
- }
- final byte bit = (byte) (1 << (i & 7));
- if((b & bit) != 0) {
- values.set(i + 1);
- }
+
+ @Override
+ public Class<? super BitVector> getRestrictionClass() {
+ return BitVector.class;
}
- return new BitVector(values, dimensionality);
- }
- @Override
- public void toByteBuffer(ByteBuffer buffer, BitVector vec) throws IOException {
- final int len = getByteSize(vec);
- assert (vec.getDimensionality() <= Short.MAX_VALUE);
- final short dim = (short) vec.getDimensionality();
- if(buffer.remaining() < len) {
- throw new IOException("Not enough space for the bit vector!");
- }
- // write size
- buffer.putShort(dim);
- // write values
- // Next byte to write:
- byte b = 0;
- for(int i = 0; i < dim; i++) {
- final byte mask = (byte) (1 << (i & 7));
- if(vec.bits.get(i)) {
- b |= mask;
- }
- else {
- b &= ~mask;
- }
- // Write when appropriate
- if((i & 7) == 7 || i == dim - 1) {
- buffer.put(b);
- b = 0;
+ /**
+ * Parameterization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public static class Parameterizer extends AbstractParameterizer {
+ @Override
+ protected BitVector.Factory makeInstance() {
+ return FACTORY;
}
}
}
- @Override
- public int getByteSize(BitVector vec) {
- return ByteArrayUtil.SIZE_SHORT + (vec.getDimensionality() + 7) / 8;
- }
-
/**
- * Parameterization class
+ * Serialization class for dense integer vectors with up to
+ * {@link Short#MAX_VALUE} dimensions, by using a short for storing the
+ * dimensionality.
*
* @author Erich Schubert
*
- * @apiviz.exclude
+ * @apiviz.uses BitVector - - «serializes»
*/
- public static class Parameterizer extends AbstractParameterizer {
+ public static class ShortSerializer implements ByteBufferSerializer<BitVector> {
+ @Override
+ public BitVector fromByteBuffer(ByteBuffer buffer) throws IOException {
+ short dimensionality = buffer.getShort();
+ final int len = ByteArrayUtil.SIZE_SHORT + (dimensionality + 7) / 8;
+ if (buffer.remaining() < len) {
+ throw new IOException("Not enough data for a bit vector!");
+ }
+ // read values
+ BitSet values = new BitSet(dimensionality);
+ byte b = 0;
+ for (int i = 0; i < dimensionality; i++) {
+ // read the next byte when needed.
+ if ((i & 7) == 0) {
+ b = buffer.get();
+ }
+ final byte bit = (byte) (1 << (i & 7));
+ if ((b & bit) != 0) {
+ values.set(i + 1);
+ }
+ }
+ return new BitVector(values, dimensionality);
+ }
+
+ @Override
+ public void toByteBuffer(ByteBuffer buffer, BitVector vec) throws IOException {
+ final int len = getByteSize(vec);
+ assert (vec.getDimensionality() <= Short.MAX_VALUE);
+ final short dim = (short) vec.getDimensionality();
+ if (buffer.remaining() < len) {
+ throw new IOException("Not enough space for the bit vector!");
+ }
+ // write size
+ buffer.putShort(dim);
+ // write values
+ // Next byte to write:
+ byte b = 0;
+ for (int i = 0; i < dim; i++) {
+ final byte mask = (byte) (1 << (i & 7));
+ if (vec.bits.get(i)) {
+ b |= mask;
+ } else {
+ b &= ~mask;
+ }
+ // Write when appropriate
+ if ((i & 7) == 7 || i == dim - 1) {
+ buffer.put(b);
+ b = 0;
+ }
+ }
+ }
+
@Override
- protected BitVector makeInstance() {
- return STATIC;
+ public int getByteSize(BitVector vec) {
+ return ByteArrayUtil.SIZE_SHORT + (vec.getDimensionality() + 7) / 8;
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/data/ClassLabel.java b/src/de/lmu/ifi/dbs/elki/data/ClassLabel.java
index 8a3b0b30..ce3b76a9 100644
--- a/src/de/lmu/ifi/dbs/elki/data/ClassLabel.java
+++ b/src/de/lmu/ifi/dbs/elki/data/ClassLabel.java
@@ -25,6 +25,7 @@ package de.lmu.ifi.dbs.elki.data;
import java.util.HashMap;
+import de.lmu.ifi.dbs.elki.data.type.SimpleTypeInformation;
import de.lmu.ifi.dbs.elki.utilities.InspectionUtilFrequentlyScanned;
/**
@@ -53,7 +54,7 @@ public abstract class ClassLabel implements Comparable<ClassLabel> {
*/
@Override
public boolean equals(Object obj) {
- if(!(obj instanceof ClassLabel)) {
+ if (!(obj instanceof ClassLabel)) {
return false;
}
return this == obj || this.compareTo((ClassLabel) obj) == 0;
@@ -64,7 +65,7 @@ public abstract class ClassLabel implements Comparable<ClassLabel> {
* <code>ClassLabel a.equals((ClassLabel) b)</code>, then also
* <code>a.toString().equals(b.toString())</code> should hold.
*
- * @see java.lang.Object#toString()
+ * {@inheritDoc}
*/
@Override
public abstract String toString();
@@ -72,7 +73,7 @@ public abstract class ClassLabel implements Comparable<ClassLabel> {
/**
* Returns the hashCode of the String-representation of this ClassLabel.
*
- * @see java.lang.Object#hashCode()
+ * {@inheritDoc}
*/
@Override
public int hashCode() {
@@ -80,25 +81,32 @@ public abstract class ClassLabel implements Comparable<ClassLabel> {
}
/**
- * Class label factory
+ * Class label factory.
*
* @author Erich Schubert
*
* @apiviz.has ClassLabel - - «creates»
* @apiviz.stereotype factory
*/
- public static abstract class Factory<L extends ClassLabel> implements InspectionUtilFrequentlyScanned {
+ public abstract static class Factory<L extends ClassLabel> implements InspectionUtilFrequentlyScanned {
/**
* Set for reusing the same objects.
*/
protected HashMap<String, L> existing = new HashMap<String, L>();
/**
- * Convert a string into a class label
+ * Convert a string into a class label.
*
* @param lbl String to convert
* @return Class label instance.
*/
public abstract L makeFromString(String lbl);
+
+ /**
+ * Get type information for the labels.
+ *
+ * @return Type information
+ */
+ public abstract SimpleTypeInformation<? super L> getTypeInformation();
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/data/Cluster.java b/src/de/lmu/ifi/dbs/elki/data/Cluster.java
index 88f610ea..95e9fb6a 100644
--- a/src/de/lmu/ifi/dbs/elki/data/Cluster.java
+++ b/src/de/lmu/ifi/dbs/elki/data/Cluster.java
@@ -53,6 +53,8 @@ import de.lmu.ifi.dbs.elki.utilities.iterator.EmptyIterator;
* @param <M> Model object type
*
* @author Erich Schubert
+ *
+ * @apiviz.landmark
*
* @apiviz.composedOf DBIDs
* @apiviz.composedOf Model
@@ -522,4 +524,4 @@ public class Cluster<M extends Model> implements Hierarchical<Cluster<M>>, TextW
String nstr = noise ? ",noise" : "";
return "Cluster(size=" + size() + ",model=" + mstr + nstr + ")";
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/data/DoubleVector.java b/src/de/lmu/ifi/dbs/elki/data/DoubleVector.java
index a8f8db6d..dfc0a316 100644
--- a/src/de/lmu/ifi/dbs/elki/data/DoubleVector.java
+++ b/src/de/lmu/ifi/dbs/elki/data/DoubleVector.java
@@ -45,25 +45,42 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
*
* @apiviz.landmark
*/
-public class DoubleVector extends AbstractNumberVector<DoubleVector, Double> implements ByteBufferSerializer<DoubleVector> {
+public class DoubleVector extends AbstractNumberVector<Double> {
/**
- * Static factory instance
+ * Static factory instance.
*/
- public static final DoubleVector STATIC = new DoubleVector(new double[0], true);
+ public static final DoubleVector.Factory FACTORY = new DoubleVector.Factory();
/**
- * Keeps the values of the real vector
+ * Serializer for up to 127 dimensions.
*/
- private double[] values;
+ public static final ByteBufferSerializer<DoubleVector> BYTE_SERIALIZER = new SmallSerializer();
+
+ /**
+ * Serializer for up to 2^15-1 dimensions.
+ */
+ public static final ByteBufferSerializer<DoubleVector> SHORT_SERIALIZER = new ShortSerializer();
+
+ /**
+ * Serializer using varint encoding.
+ */
+ public static final ByteBufferSerializer<DoubleVector> VARIABLE_SERIALIZER = new VariableSerializer();
+
+ /**
+ * Stores the values of the real vector.
+ */
+ private final double[] values;
/**
* Private constructor. NOT for public use.
+ *
+ * @param values Values to use
+ * @param nocopy Flag to not copy the array
*/
private DoubleVector(double[] values, boolean nocopy) {
- if(nocopy) {
+ if (nocopy) {
this.values = values;
- }
- else {
+ } else {
this.values = new double[values.length];
System.arraycopy(values, 0, this.values, 0, values.length);
}
@@ -78,7 +95,7 @@ public class DoubleVector extends AbstractNumberVector<DoubleVector, Double> imp
public DoubleVector(List<Double> values) {
int i = 0;
this.values = new double[values.size()];
- for(Iterator<Double> iter = values.iterator(); iter.hasNext(); i++) {
+ for (Iterator<Double> iter = values.iterator(); iter.hasNext(); i++) {
this.values[i] = (iter.next());
}
}
@@ -100,7 +117,7 @@ public class DoubleVector extends AbstractNumberVector<DoubleVector, Double> imp
*/
public DoubleVector(Double[] values) {
this.values = new double[values.length];
- for(int i = 0; i < values.length; i++) {
+ for (int i = 0; i < values.length; i++) {
this.values[i] = values[i];
}
}
@@ -112,7 +129,7 @@ public class DoubleVector extends AbstractNumberVector<DoubleVector, Double> imp
*/
public DoubleVector(Vector columnMatrix) {
values = new double[columnMatrix.getDimensionality()];
- for(int i = 0; i < values.length; i++) {
+ for (int i = 0; i < values.length; i++) {
values[i] = columnMatrix.get(i);
}
}
@@ -122,59 +139,30 @@ public class DoubleVector extends AbstractNumberVector<DoubleVector, Double> imp
return values.length;
}
- /**
- * Returns the value of the specified attribute.
- *
- * @param dimension the selected attribute. Attributes are counted starting
- * with 1.
- *
- * @throws IllegalArgumentException if the specified dimension is out of range
- * of the possible attributes
- */
@Override
+ @Deprecated
public Double getValue(int dimension) {
try {
- return values[dimension - 1];
- }
- catch(IndexOutOfBoundsException e) {
+ return values[dimension];
+ } catch (IndexOutOfBoundsException e) {
throw new IllegalArgumentException("Dimension " + dimension + " out of range.");
}
}
- /**
- * Returns the value of the specified attribute.
- *
- * @param dimension the selected attribute. Attributes are counted starting
- * with 1.
- *
- * @throws IllegalArgumentException if the specified dimension is out of range
- * of the possible attributes
- */
@Override
public double doubleValue(int dimension) {
try {
- return values[dimension - 1];
- }
- catch(IndexOutOfBoundsException e) {
+ return values[dimension];
+ } catch (IndexOutOfBoundsException e) {
throw new IllegalArgumentException("Dimension " + dimension + " out of range.");
}
}
- /**
- * Returns the value of the specified attribute as long.
- *
- * @param dimension the selected attribute. Attributes are counted starting
- * with 1.
- *
- * @throws IllegalArgumentException if the specified dimension is out of range
- * of the possible attributes
- */
@Override
public long longValue(int dimension) {
try {
- return (long) values[dimension - 1];
- }
- catch(IndexOutOfBoundsException e) {
+ return (long) values[dimension];
+ } catch (IndexOutOfBoundsException e) {
throw new IllegalArgumentException("Dimension " + dimension + " out of range.");
}
}
@@ -199,83 +187,185 @@ public class DoubleVector extends AbstractNumberVector<DoubleVector, Double> imp
@Override
public String toString() {
- StringBuffer featureLine = new StringBuffer();
- for(int i = 0; i < values.length; i++) {
+ StringBuilder featureLine = new StringBuilder();
+ for (int i = 0; i < values.length; i++) {
featureLine.append(values[i]);
- if(i + 1 < values.length) {
+ if (i + 1 < values.length) {
featureLine.append(ATTRIBUTE_SEPARATOR);
}
}
return featureLine.toString();
}
- @Override
- public DoubleVector newNumberVector(double[] values) {
- return new DoubleVector(values);
- }
+ /**
+ * Factory for Double vectors.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.has DoubleVector
+ */
+ public static class Factory extends AbstractNumberVector.Factory<DoubleVector, Double> {
+ @Override
+ public DoubleVector newNumberVector(double[] values) {
+ return new DoubleVector(values);
+ }
- @Override
- public <A> DoubleVector newFeatureVector(A array, ArrayAdapter<Double, A> adapter) {
- int dim = adapter.size(array);
- double[] values = new double[dim];
- for(int i = 0; i < dim; i++) {
- values[i] = adapter.get(array, i);
+ @Override
+ public <A> DoubleVector newFeatureVector(A array, ArrayAdapter<Double, A> adapter) {
+ int dim = adapter.size(array);
+ double[] values = new double[dim];
+ for (int i = 0; i < dim; i++) {
+ values[i] = adapter.get(array, i);
+ }
+ return new DoubleVector(values, true);
}
- return new DoubleVector(values, true);
- }
- @Override
- public <A> DoubleVector newNumberVector(A array, NumberArrayAdapter<?, A> adapter) {
- if(adapter == ArrayLikeUtil.TDOUBLELISTADAPTER) {
- return new DoubleVector(((TDoubleList) array).toArray(), true);
+ @Override
+ public <A> DoubleVector newNumberVector(A array, NumberArrayAdapter<?, ? super A> adapter) {
+ if (adapter == ArrayLikeUtil.TDOUBLELISTADAPTER) {
+ return new DoubleVector(((TDoubleList) array).toArray(), true);
+ }
+ final int dim = adapter.size(array);
+ double[] values = new double[dim];
+ for (int i = 0; i < dim; i++) {
+ values[i] = adapter.getDouble(array, i);
+ }
+ return new DoubleVector(values, true);
+ }
+
+ @Override
+ public ByteBufferSerializer<DoubleVector> getDefaultSerializer() {
+ return VARIABLE_SERIALIZER;
}
- final int dim = adapter.size(array);
- double[] values = new double[dim];
- for(int i = 0; i < dim; i++) {
- values[i] = adapter.getDouble(array, i);
+
+ @Override
+ public Class<? super DoubleVector> getRestrictionClass() {
+ return DoubleVector.class;
}
- return new DoubleVector(values, true);
- }
- @Override
- public DoubleVector fromByteBuffer(ByteBuffer buffer) throws IOException {
- final short dimensionality = buffer.getShort();
- final int len = ByteArrayUtil.SIZE_SHORT + ByteArrayUtil.SIZE_DOUBLE * dimensionality;
- if(buffer.remaining() < len) {
- throw new IOException("Not enough data for a double vector!");
+ /**
+ * Parameterization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public static class Parameterizer extends AbstractParameterizer {
+ @Override
+ protected DoubleVector.Factory makeInstance() {
+ return FACTORY;
+ }
}
- final double[] values = new double[dimensionality];
- buffer.asDoubleBuffer().get(values);
- return new DoubleVector(values, true);
}
- @Override
- public void toByteBuffer(ByteBuffer buffer, DoubleVector vec) throws IOException {
- final short dimensionality = buffer.getShort();
- final int len = ByteArrayUtil.SIZE_SHORT + ByteArrayUtil.SIZE_DOUBLE * dimensionality;
- if(buffer.remaining() < len) {
- throw new IOException("Not enough space for the double vector!");
+ /**
+ * Serialization class for dense double vectors with up to 127 dimensions, by
+ * using a byte for storing the dimensionality.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.uses DoubleVector - - «serializes»
+ */
+ public static class SmallSerializer implements ByteBufferSerializer<DoubleVector> {
+ @Override
+ public DoubleVector fromByteBuffer(ByteBuffer buffer) throws IOException {
+ final byte dimensionality = buffer.get();
+ assert (buffer.remaining() >= ByteArrayUtil.SIZE_DOUBLE * dimensionality);
+ final double[] values = new double[dimensionality];
+ for (int i = 0; i < dimensionality; i++) {
+ values[i] = buffer.getDouble();
+ }
+ return new DoubleVector(values, true);
+ }
+
+ @Override
+ public void toByteBuffer(ByteBuffer buffer, DoubleVector vec) throws IOException {
+ assert (vec.values.length < Byte.MAX_VALUE) : "This serializer only supports a maximum dimensionality of " + Byte.MAX_VALUE + "!";
+ assert (buffer.remaining() >= ByteArrayUtil.SIZE_DOUBLE * vec.values.length);
+ buffer.put((byte) vec.values.length);
+ for (int i = 0; i < vec.values.length; i++) {
+ buffer.putDouble(vec.values[i]);
+ }
+ }
+
+ @Override
+ public int getByteSize(DoubleVector vec) {
+ assert (vec.values.length < Byte.MAX_VALUE) : "This serializer only supports a maximum dimensionality of " + Byte.MAX_VALUE + "!";
+ return ByteArrayUtil.SIZE_BYTE + ByteArrayUtil.SIZE_DOUBLE * vec.getDimensionality();
}
- buffer.putShort(dimensionality);
- buffer.asDoubleBuffer().put(vec.values);
}
- @Override
- public int getByteSize(DoubleVector vec) {
- return ByteArrayUtil.SIZE_SHORT + ByteArrayUtil.SIZE_DOUBLE * vec.getDimensionality();
+ /**
+ * Serialization class for dense double vectors with up to
+ * {@link Short#MAX_VALUE} dimensions, by using a short for storing the
+ * dimensionality.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.uses DoubleVector - - «serializes»
+ */
+ public static class ShortSerializer implements ByteBufferSerializer<DoubleVector> {
+ @Override
+ public DoubleVector fromByteBuffer(ByteBuffer buffer) throws IOException {
+ final short dimensionality = buffer.getShort();
+ assert (buffer.remaining() >= ByteArrayUtil.SIZE_DOUBLE * dimensionality);
+ final double[] values = new double[dimensionality];
+ for (int i = 0; i < dimensionality; i++) {
+ values[i] = buffer.getDouble();
+ }
+ return new DoubleVector(values, true);
+ }
+
+ @Override
+ public void toByteBuffer(ByteBuffer buffer, DoubleVector vec) throws IOException {
+ assert (vec.values.length < Short.MAX_VALUE) : "This serializer only supports a maximum dimensionality of " + Short.MAX_VALUE + "!";
+ assert (buffer.remaining() >= ByteArrayUtil.SIZE_DOUBLE * vec.values.length);
+ buffer.putShort((short) vec.values.length);
+ for (int i = 0; i < vec.values.length; i++) {
+ buffer.putDouble(vec.values[i]);
+ }
+ }
+
+ @Override
+ public int getByteSize(DoubleVector vec) {
+ assert (vec.values.length < Short.MAX_VALUE) : "This serializer only supports a maximum dimensionality of " + Short.MAX_VALUE + "!";
+ return ByteArrayUtil.SIZE_SHORT + ByteArrayUtil.SIZE_DOUBLE * vec.getDimensionality();
+ }
}
/**
- * Parameterization class
+ * Serialization class for variable dimensionality by using VarInt encoding.
*
* @author Erich Schubert
*
- * @apiviz.exclude
+ * @apiviz.uses DoubleVector - - «serializes»
*/
- public static class Parameterizer extends AbstractParameterizer {
+ public static class VariableSerializer implements ByteBufferSerializer<DoubleVector> {
+ @Override
+ public DoubleVector fromByteBuffer(ByteBuffer buffer) throws IOException {
+ final int dimensionality = ByteArrayUtil.readUnsignedVarint(buffer);
+ assert (buffer.remaining() >= ByteArrayUtil.SIZE_DOUBLE * dimensionality);
+ final double[] values = new double[dimensionality];
+ for (int i = 0; i < dimensionality; i++) {
+ values[i] = buffer.getDouble();
+ }
+ return new DoubleVector(values, true);
+ }
+
+ @Override
+ public void toByteBuffer(ByteBuffer buffer, DoubleVector vec) throws IOException {
+ assert (vec.values.length < Short.MAX_VALUE) : "This serializer only supports a maximum dimensionality of " + Short.MAX_VALUE + "!";
+ assert (buffer.remaining() >= ByteArrayUtil.SIZE_DOUBLE * vec.values.length);
+ ByteArrayUtil.writeUnsignedVarint(buffer, vec.values.length);
+ for (int i = 0; i < vec.values.length; i++) {
+ buffer.putDouble(vec.values[i]);
+ }
+ }
+
@Override
- protected DoubleVector makeInstance() {
- return STATIC;
+ public int getByteSize(DoubleVector vec) {
+ assert (vec.values.length < Short.MAX_VALUE) : "This serializer only supports a maximum dimensionality of " + Short.MAX_VALUE + "!";
+ return ByteArrayUtil.getUnsignedVarintSize(vec.values.length) + ByteArrayUtil.SIZE_DOUBLE * vec.values.length;
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/data/FeatureVector.java b/src/de/lmu/ifi/dbs/elki/data/FeatureVector.java
index 12999159..c3b255c6 100644
--- a/src/de/lmu/ifi/dbs/elki/data/FeatureVector.java
+++ b/src/de/lmu/ifi/dbs/elki/data/FeatureVector.java
@@ -23,7 +23,9 @@ package de.lmu.ifi.dbs.elki.data;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+import de.lmu.ifi.dbs.elki.persistent.ByteBufferSerializer;
import de.lmu.ifi.dbs.elki.utilities.datastructures.arraylike.ArrayAdapter;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable;
/**
* Generic FeatureVector class that can contain any type of data (i.e. numerical
@@ -32,10 +34,9 @@ import de.lmu.ifi.dbs.elki.utilities.datastructures.arraylike.ArrayAdapter;
*
* @author Erich Schubert
*
- * @param <V> Vector class
* @param <D> Data type
*/
-public interface FeatureVector<V extends FeatureVector<? extends V, D>, D> {
+public interface FeatureVector<D> {
/**
* The dimensionality of the vector space where of this FeatureVector of V is
* an element.
@@ -63,12 +64,38 @@ public interface FeatureVector<V extends FeatureVector<? extends V, D>, D> {
String toString();
/**
- * Returns a new FeatureVector of V for the given values.
+ * Factory API for this feature vector.
*
- * @param array the values of the featureVector
- * @param adapter adapter class
- * @param <A> Array type
- * @return a new FeatureVector of V for the given values
+ * @author Erich Schubert
+ *
+ * @param <V> Vector type
+ * @param <D> Data type of vector
*/
- <A> V newFeatureVector(A array, ArrayAdapter<D, A> adapter);
-} \ No newline at end of file
+ interface Factory<V extends FeatureVector<? extends D>, D> extends Parameterizable {
+ /**
+ * Returns a new FeatureVector of V for the given values.
+ *
+ * @param array the values of the featureVector
+ * @param adapter adapter class
+ * @param <A> Array type
+ * @return a new FeatureVector of V for the given values
+ */
+ <A> V newFeatureVector(A array, ArrayAdapter<D, A> adapter);
+
+ /**
+ * Get the default serializer for this type.
+ *
+ * Note, this may be {@code null} when no serializer is available.
+ *
+ * @return Serializer
+ */
+ ByteBufferSerializer<V> getDefaultSerializer();
+
+ /**
+ * Get the objects type restriction.
+ *
+ * @return Restriction class
+ */
+ Class<? super V> getRestrictionClass();
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/data/FloatVector.java b/src/de/lmu/ifi/dbs/elki/data/FloatVector.java
index e34e97d9..821c16c3 100644
--- a/src/de/lmu/ifi/dbs/elki/data/FloatVector.java
+++ b/src/de/lmu/ifi/dbs/elki/data/FloatVector.java
@@ -37,29 +37,46 @@ import de.lmu.ifi.dbs.elki.utilities.datastructures.arraylike.NumberArrayAdapter
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
/**
- * A FloatVector is to store real values approximately as float values.
+ * A FloatVector is to store real values with lower memory requirements by using float values.
*
* @author Elke Achtert
*/
-public class FloatVector extends AbstractNumberVector<FloatVector, Float> implements ByteBufferSerializer<FloatVector> {
+public class FloatVector extends AbstractNumberVector<Float> {
/**
- * Static factory instance
+ * Static factory instance.
*/
- public static final FloatVector STATIC = new FloatVector(new float[0], true);
+ public static final FloatVector.Factory FACTORY = new FloatVector.Factory();
/**
- * Keeps the values of the float vector
+ * Serializer for up to 127 dimensions.
*/
- private float[] values;
+ public static final ByteBufferSerializer<FloatVector> BYTE_SERIALIZER = new SmallSerializer();
+
+ /**
+ * Serializer for up to 2^15-1 dimensions.
+ */
+ public static final ByteBufferSerializer<FloatVector> SHORT_SERIALIZER = new ShortSerializer();
+
+ /**
+ * Serializer using varint encoding.
+ */
+ public static final ByteBufferSerializer<FloatVector> VARIABLE_SERIALIZER = new VariableSerializer();
+
+ /**
+ * Keeps the values of the float vector.
+ */
+ private final float[] values;
/**
* Private constructor. NOT for public use.
+ *
+ * @param values Data values
+ * @param nocopy Flag to re-use the values array
*/
private FloatVector(float[] values, boolean nocopy) {
- if(nocopy) {
+ if (nocopy) {
this.values = values;
- }
- else {
+ } else {
this.values = new float[values.length];
System.arraycopy(values, 0, this.values, 0, values.length);
}
@@ -74,7 +91,7 @@ public class FloatVector extends AbstractNumberVector<FloatVector, Float> implem
public FloatVector(List<Float> values) {
int i = 0;
this.values = new float[values.size()];
- for(Iterator<Float> iter = values.iterator(); iter.hasNext(); i++) {
+ for (Iterator<Float> iter = values.iterator(); iter.hasNext(); i++) {
this.values[i] = (iter.next());
}
}
@@ -96,7 +113,7 @@ public class FloatVector extends AbstractNumberVector<FloatVector, Float> implem
*/
public FloatVector(Float[] values) {
this.values = new float[values.length];
- for(int i = 0; i < values.length; i++) {
+ for (int i = 0; i < values.length; i++) {
this.values[i] = values[i];
}
}
@@ -108,7 +125,7 @@ public class FloatVector extends AbstractNumberVector<FloatVector, Float> implem
*/
public FloatVector(Vector columnMatrix) {
values = new float[columnMatrix.getDimensionality()];
- for(int i = 0; i < values.length; i++) {
+ for (int i = 0; i < values.length; i++) {
values[i] = (float) columnMatrix.get(i);
}
}
@@ -118,12 +135,12 @@ public class FloatVector extends AbstractNumberVector<FloatVector, Float> implem
return values.length;
}
+ @Deprecated
@Override
public Float getValue(int dimension) {
try {
return values[dimension - 1];
- }
- catch(ArrayIndexOutOfBoundsException e) {
+ } catch (ArrayIndexOutOfBoundsException e) {
throw new IllegalArgumentException("Dimension " + dimension + " out of range.");
}
}
@@ -132,8 +149,7 @@ public class FloatVector extends AbstractNumberVector<FloatVector, Float> implem
public double doubleValue(int dimension) {
try {
return values[dimension - 1];
- }
- catch(ArrayIndexOutOfBoundsException e) {
+ } catch (ArrayIndexOutOfBoundsException e) {
throw new IllegalArgumentException("Dimension " + dimension + " out of range.");
}
}
@@ -142,90 +158,189 @@ public class FloatVector extends AbstractNumberVector<FloatVector, Float> implem
public long longValue(int dimension) {
try {
return (long) values[dimension - 1];
- }
- catch(ArrayIndexOutOfBoundsException e) {
+ } catch (ArrayIndexOutOfBoundsException e) {
throw new IllegalArgumentException("Dimension " + dimension + " out of range.");
}
}
@Override
public Vector getColumnVector() {
- return new Vector(ArrayLikeUtil.toPrimitiveDoubleArray(values, ArrayLikeUtil.FLOATARRAYADAPTER));
+ return new Vector(ArrayLikeUtil.toPrimitiveDoubleArray(values, ArrayLikeUtil.FLOATARRAYADAPTER));
}
@Override
public String toString() {
- StringBuffer featureLine = new StringBuffer();
- for(int i = 0; i < values.length; i++) {
+ StringBuilder featureLine = new StringBuilder();
+ for (int i = 0; i < values.length; i++) {
featureLine.append(values[i]);
- if(i + 1 < values.length) {
+ if (i + 1 < values.length) {
featureLine.append(ATTRIBUTE_SEPARATOR);
}
}
return featureLine.toString();
}
- @Override
- public <A> FloatVector newFeatureVector(A array, ArrayAdapter<Float, A> adapter) {
- int dim = adapter.size(array);
- float[] values = new float[dim];
- for(int i = 0; i < dim; i++) {
- values[i] = adapter.get(array, i);
+ /**
+ * Factory for float vectors.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.has FloatVector
+ */
+ public static class Factory extends AbstractNumberVector.Factory<FloatVector, Float> {
+ @Override
+ public <A> FloatVector newFeatureVector(A array, ArrayAdapter<Float, A> adapter) {
+ int dim = adapter.size(array);
+ float[] values = new float[dim];
+ for (int i = 0; i < dim; i++) {
+ values[i] = adapter.get(array, i);
+ }
+ return new FloatVector(values, true);
}
- return new FloatVector(values, true);
- }
- @Override
- public <A> FloatVector newNumberVector(A array, NumberArrayAdapter<?, A> adapter) {
- int dim = adapter.size(array);
- float[] values = new float[dim];
- for(int i = 0; i < dim; i++) {
- values[i] = adapter.getFloat(array, i);
+ @Override
+ public <A> FloatVector newNumberVector(A array, NumberArrayAdapter<?, ? super A> adapter) {
+ int dim = adapter.size(array);
+ float[] values = new float[dim];
+ for (int i = 0; i < dim; i++) {
+ values[i] = adapter.getFloat(array, i);
+ }
+ return new FloatVector(values, true);
}
- return new FloatVector(values, true);
- }
- @Override
- public FloatVector fromByteBuffer(ByteBuffer buffer) throws IOException {
- final short dimensionality = buffer.getShort();
- final int len = ByteArrayUtil.SIZE_SHORT + ByteArrayUtil.SIZE_FLOAT * dimensionality;
- if(buffer.remaining() < len) {
- throw new IOException("Not enough data for a float vector!");
- }
- // read the values
- float[] values = new float[dimensionality];
- buffer.asFloatBuffer().get(values);
- return new FloatVector(values, false);
+ @Override
+ public ByteBufferSerializer<FloatVector> getDefaultSerializer() {
+ return VARIABLE_SERIALIZER;
+ }
+
+ @Override
+ public Class<? super FloatVector> getRestrictionClass() {
+ return FloatVector.class;
+ }
+
+ /**
+ * Parameterization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public static class Parameterizer extends AbstractParameterizer {
+ @Override
+ protected FloatVector.Factory makeInstance() {
+ return FACTORY;
+ }
+ }
}
- @Override
- public void toByteBuffer(ByteBuffer buffer, FloatVector vec) throws IOException {
- final short dimensionality = buffer.getShort();
- final int len = getByteSize(vec);
- if(buffer.remaining() < len) {
- throw new IOException("Not enough space for the float vector!");
- }
- // write dimensionality
- buffer.putShort(dimensionality);
- buffer.asFloatBuffer().put(vec.values);
+ /**
+ * Serialization class for dense float vectors with up to 127 dimensions, by
+ * using a byte for storing the dimensionality.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.uses FloatVector - - «serializes»
+ */
+ public static class SmallSerializer implements ByteBufferSerializer<FloatVector> {
+ @Override
+ public FloatVector fromByteBuffer(ByteBuffer buffer) throws IOException {
+ final byte dimensionality = buffer.get();
+ assert (buffer.remaining() >= ByteArrayUtil.SIZE_FLOAT * dimensionality);
+ final float[] values = new float[dimensionality];
+ for (int i = 0; i < dimensionality; i++) {
+ values[i] = buffer.getFloat();
+ }
+ return new FloatVector(values, true);
+ }
+
+ @Override
+ public void toByteBuffer(ByteBuffer buffer, FloatVector vec) throws IOException {
+ assert (vec.values.length < Byte.MAX_VALUE) : "This serializer only supports a maximum dimensionality of " + Byte.MAX_VALUE + "!";
+ assert (buffer.remaining() >= ByteArrayUtil.SIZE_FLOAT * vec.values.length);
+ buffer.put((byte) vec.values.length);
+ for (int i = 0; i < vec.values.length; i++) {
+ buffer.putFloat(vec.values[i]);
+ }
+ }
+
+ @Override
+ public int getByteSize(FloatVector vec) {
+ assert (vec.values.length < Byte.MAX_VALUE) : "This serializer only supports a maximum dimensionality of " + Byte.MAX_VALUE + "!";
+ return ByteArrayUtil.SIZE_BYTE + ByteArrayUtil.SIZE_FLOAT * vec.getDimensionality();
+ }
}
- @Override
- public int getByteSize(FloatVector vec) {
- return ByteArrayUtil.SIZE_SHORT + ByteArrayUtil.SIZE_FLOAT * vec.getDimensionality();
+ /**
+ * Serialization class for dense float vectors with up to
+ * {@link Short#MAX_VALUE} dimensions, by using a short for storing the
+ * dimensionality.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.uses FloatVector - - «serializes»
+ */
+ public static class ShortSerializer implements ByteBufferSerializer<FloatVector> {
+ @Override
+ public FloatVector fromByteBuffer(ByteBuffer buffer) throws IOException {
+ final short dimensionality = buffer.getShort();
+ assert (buffer.remaining() >= ByteArrayUtil.SIZE_FLOAT * dimensionality);
+ final float[] values = new float[dimensionality];
+ for (int i = 0; i < dimensionality; i++) {
+ values[i] = buffer.getFloat();
+ }
+ return new FloatVector(values, true);
+ }
+
+ @Override
+ public void toByteBuffer(ByteBuffer buffer, FloatVector vec) throws IOException {
+ assert (vec.values.length < Short.MAX_VALUE) : "This serializer only supports a maximum dimensionality of " + Short.MAX_VALUE + "!";
+ assert (buffer.remaining() >= ByteArrayUtil.SIZE_FLOAT * vec.values.length);
+ buffer.putShort((short) vec.values.length);
+ for (int i = 0; i < vec.values.length; i++) {
+ buffer.putFloat(vec.values[i]);
+ }
+ }
+
+ @Override
+ public int getByteSize(FloatVector vec) {
+ assert (vec.values.length < Short.MAX_VALUE) : "This serializer only supports a maximum dimensionality of " + Short.MAX_VALUE + "!";
+ return ByteArrayUtil.SIZE_SHORT + ByteArrayUtil.SIZE_FLOAT * vec.getDimensionality();
+ }
}
/**
- * Parameterization class
+ * Serialization class for variable dimensionality by using VarInt encoding.
*
* @author Erich Schubert
*
- * @apiviz.exclude
+ * @apiviz.uses FloatVector - - «serializes»
*/
- public static class Parameterizer extends AbstractParameterizer {
+ public static class VariableSerializer implements ByteBufferSerializer<FloatVector> {
+ @Override
+ public FloatVector fromByteBuffer(ByteBuffer buffer) throws IOException {
+ final int dimensionality = ByteArrayUtil.readUnsignedVarint(buffer);
+ assert (buffer.remaining() >= ByteArrayUtil.SIZE_FLOAT * dimensionality);
+ final float[] values = new float[dimensionality];
+ for (int i = 0; i < dimensionality; i++) {
+ values[i] = buffer.getFloat();
+ }
+ return new FloatVector(values, true);
+ }
+
+ @Override
+ public void toByteBuffer(ByteBuffer buffer, FloatVector vec) throws IOException {
+ assert (vec.values.length < Short.MAX_VALUE) : "This serializer only supports a maximum dimensionality of " + Short.MAX_VALUE + "!";
+ assert (buffer.remaining() >= ByteArrayUtil.SIZE_FLOAT * vec.values.length);
+ ByteArrayUtil.writeUnsignedVarint(buffer, vec.values.length);
+ for (int i = 0; i < vec.values.length; i++) {
+ buffer.putFloat(vec.values[i]);
+ }
+ }
+
@Override
- protected FloatVector makeInstance() {
- return STATIC;
+ public int getByteSize(FloatVector vec) {
+ assert (vec.values.length < Short.MAX_VALUE) : "This serializer only supports a maximum dimensionality of " + Short.MAX_VALUE + "!";
+ return ByteArrayUtil.getUnsignedVarintSize(vec.values.length) + ByteArrayUtil.SIZE_FLOAT * vec.values.length;
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/data/HierarchicalClassLabel.java b/src/de/lmu/ifi/dbs/elki/data/HierarchicalClassLabel.java
index 5e958fd7..3db21bd8 100644
--- a/src/de/lmu/ifi/dbs/elki/data/HierarchicalClassLabel.java
+++ b/src/de/lmu/ifi/dbs/elki/data/HierarchicalClassLabel.java
@@ -25,6 +25,8 @@ package de.lmu.ifi.dbs.elki.data;
import java.util.regex.Pattern;
+import de.lmu.ifi.dbs.elki.data.type.SimpleTypeInformation;
+
/**
* A HierarchicalClassLabel is a ClassLabel to reflect a hierarchical structure
* of classes.
@@ -45,6 +47,11 @@ public class HierarchicalClassLabel extends ClassLabel {
public static final String DEFAULT_SEPARATOR_STRING = ".";
/**
+ * Type information.
+ */
+ public static final SimpleTypeInformation<HierarchicalClassLabel> TYPE = new SimpleTypeInformation<HierarchicalClassLabel>(HierarchicalClassLabel.class);
+
+ /**
* Holds the Pattern to separate different levels parsing input.
*/
private Pattern separatorPattern;
@@ -78,11 +85,10 @@ public class HierarchicalClassLabel extends ClassLabel {
this.separatorString = separator;
String[] levelwiseStrings = separatorPattern.split(name);
this.levelwiseNames = new Comparable<?>[levelwiseStrings.length];
- for(int i = 0; i < levelwiseStrings.length; i++) {
+ for (int i = 0; i < levelwiseStrings.length; i++) {
try {
- levelwiseNames[i] = new Integer(levelwiseStrings[i]);
- }
- catch(NumberFormatException e) {
+ levelwiseNames[i] = Integer.valueOf(levelwiseStrings[i]);
+ } catch (NumberFormatException e) {
levelwiseNames[i] = levelwiseStrings[i];
}
}
@@ -107,28 +113,28 @@ public class HierarchicalClassLabel extends ClassLabel {
* equal. Names at a level are tried to be compared as integer values. If this
* does not succeed, both names are compared as Strings.
*
+ * {@inheritDoc}
*/
- @Override
@SuppressWarnings({ "unchecked", "rawtypes" })
+ @Override
public int compareTo(ClassLabel o) {
HierarchicalClassLabel h = (HierarchicalClassLabel) o;
- for(int i = 0; i < this.levelwiseNames.length && i < h.levelwiseNames.length; i++) {
+ for (int i = 0; i < this.levelwiseNames.length && i < h.levelwiseNames.length; i++) {
int comp = 0;
try {
Comparable first = this.levelwiseNames[i];
Comparable second = h.levelwiseNames[i];
comp = first.compareTo(second);
- }
- catch(RuntimeException e) {
+ } catch (RuntimeException e) {
String h1 = (String) (this.levelwiseNames[i] instanceof Integer ? this.levelwiseNames[i].toString() : this.levelwiseNames[i]);
String h2 = (String) (h.levelwiseNames[i] instanceof Integer ? h.levelwiseNames[i].toString() : h.levelwiseNames[i]);
comp = h1.compareTo(h2);
}
- if(comp != 0) {
+ if (comp != 0) {
return comp;
}
}
- return new Integer(this.levelwiseNames.length).compareTo(new Integer(h.levelwiseNames.length));
+ return (this.levelwiseNames.length < h.levelwiseNames.length) ? -1 : ((this.levelwiseNames.length == h.levelwiseNames.length) ? 0 : 1);
}
/**
@@ -154,7 +160,7 @@ public class HierarchicalClassLabel extends ClassLabel {
* Returns a String representation of this HierarchicalClassLabel using
* {@link #separatorString separatorString} to separate levels.
*
- * @see #toString(int)
+ * {@inheritDoc}
*/
@Override
public String toString() {
@@ -170,14 +176,14 @@ public class HierarchicalClassLabel extends ClassLabel {
* first <code>level</code> levels
*/
public String toString(int level) {
- if(level > levelwiseNames.length) {
+ if (level > levelwiseNames.length) {
throw new IllegalArgumentException("Specified level exceeds depth of hierarchy.");
}
- StringBuffer name = new StringBuffer();
- for(int i = 0; i < level; i++) {
+ StringBuilder name = new StringBuilder();
+ for (int i = 0; i < level; i++) {
name.append(this.getNameAt(i));
- if(i < level - 1) {
+ if (i < level - 1) {
name.append(this.separatorString);
}
}
@@ -185,7 +191,7 @@ public class HierarchicalClassLabel extends ClassLabel {
}
/**
- * Factory class
+ * Factory class.
*
* @author Erich Schubert
*
@@ -195,12 +201,18 @@ public class HierarchicalClassLabel extends ClassLabel {
public static class Factory extends ClassLabel.Factory<HierarchicalClassLabel> {
@Override
public HierarchicalClassLabel makeFromString(String lbl) {
+ lbl = lbl.intern();
HierarchicalClassLabel l = existing.get(lbl);
- if(l == null) {
+ if (l == null) {
l = new HierarchicalClassLabel(lbl);
existing.put(lbl, l);
}
return l;
}
+
+ @Override
+ public SimpleTypeInformation<? super HierarchicalClassLabel> getTypeInformation() {
+ return TYPE;
+ }
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/data/HyperBoundingBox.java b/src/de/lmu/ifi/dbs/elki/data/HyperBoundingBox.java
index feb5cace..299f329b 100644
--- a/src/de/lmu/ifi/dbs/elki/data/HyperBoundingBox.java
+++ b/src/de/lmu/ifi/dbs/elki/data/HyperBoundingBox.java
@@ -40,19 +40,19 @@ import de.lmu.ifi.dbs.elki.utilities.FormatUtil;
*/
public class HyperBoundingBox implements SpatialComparable, Externalizable {
/**
- * Serial version
+ * Serial version.
*/
private static final long serialVersionUID = 1;
/**
* The coordinates of the 'lower left' (= minimum) hyper point.
*/
- protected double[] min;
+ double[] min;
/**
* The coordinates of the 'upper right' (= maximum) hyper point.
*/
- protected double[] max;
+ double[] max;
/**
* Empty constructor for Externalizable interface.
@@ -86,37 +86,37 @@ public class HyperBoundingBox implements SpatialComparable, Externalizable {
this.min = new double[dim];
this.max = new double[dim];
for(int i = 0; i < dim; i++) {
- this.min[i] = other.getMin(i + 1);
- this.max[i] = other.getMax(i + 1);
+ this.min[i] = other.getMin(i);
+ this.max[i] = other.getMax(i);
}
}
/**
* Returns the coordinate at the specified dimension of the minimum hyper
- * point
+ * point.
*
* @param dimension the dimension for which the coordinate should be returned,
- * where 1 &le; dimension &le; <code>this.getDimensionality()</code>
+ * where 0 &le; dimension &lt; <code>this.getDimensionality()</code>
* @return the coordinate at the specified dimension of the minimum hyper
* point
*/
@Override
public double getMin(int dimension) {
- return min[dimension - 1];
+ return min[dimension];
}
/**
* Returns the coordinate at the specified dimension of the maximum hyper
- * point
+ * point.
*
* @param dimension the dimension for which the coordinate should be returned,
- * where 1 &le; dimension &le; <code>this.getDimensionality()</code>
+ * where 0 &le; dimension &lt; <code>this.getDimensionality()</code>
* @return the coordinate at the specified dimension of the maximum hyper
* point
*/
@Override
public double getMax(int dimension) {
- return max[dimension - 1];
+ return max[dimension];
}
/**
@@ -136,7 +136,7 @@ public class HyperBoundingBox implements SpatialComparable, Externalizable {
*/
@Override
public String toString() {
- return "[Min(" + FormatUtil.format(min, ",", 10) + "), Max(" + FormatUtil.format(max, ",", 10) + ")]";
+ return "[Min(" + FormatUtil.format(min, ",", FormatUtil.NF8) + "), Max(" + FormatUtil.format(max, ",", FormatUtil.NF8) + ")]";
}
/**
@@ -150,18 +150,12 @@ public class HyperBoundingBox implements SpatialComparable, Externalizable {
return pre + "[Min(" + FormatUtil.format(min, ",", nf) + "), Max(" + FormatUtil.format(max, ",", nf) + ")]";
}
- /**
- * @see Object#equals(Object)
- */
@Override
public boolean equals(Object obj) {
HyperBoundingBox box = (HyperBoundingBox) obj;
return Arrays.equals(min, box.min) && Arrays.equals(max, box.max);
}
- /**
- * @see Object#hashCode()
- */
@Override
public int hashCode() {
return 29 * Arrays.hashCode(min) + Arrays.hashCode(max);
diff --git a/src/de/lmu/ifi/dbs/elki/data/IntegerVector.java b/src/de/lmu/ifi/dbs/elki/data/IntegerVector.java
index ae0d8cce..595fcd94 100644
--- a/src/de/lmu/ifi/dbs/elki/data/IntegerVector.java
+++ b/src/de/lmu/ifi/dbs/elki/data/IntegerVector.java
@@ -38,25 +38,42 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
*
* @author Erich Schubert
*/
-public class IntegerVector extends AbstractNumberVector<IntegerVector, Integer> implements ByteBufferSerializer<IntegerVector> {
+public class IntegerVector extends AbstractNumberVector<Integer> {
/**
- * Static instance (object factory)
+ * Static instance (object factory).
*/
- public static final IntegerVector STATIC = new IntegerVector(new int[0], true);
+ public static final IntegerVector.Factory STATIC = new IntegerVector.Factory();
/**
- * Keeps the values of the real vector
+ * Serializer for up to 127 dimensions.
*/
- private int[] values;
+ public static final ByteBufferSerializer<IntegerVector> BYTE_SERIALIZER = new SmallSerializer();
+
+ /**
+ * Serializer for up to 2^15-1 dimensions.
+ */
+ public static final ByteBufferSerializer<IntegerVector> SHORT_SERIALIZER = new ShortSerializer();
+
+ /**
+ * Serializer using varint encoding.
+ */
+ public static final ByteBufferSerializer<IntegerVector> VARIABLE_SERIALIZER = new VariableSerializer();
+
+ /**
+ * Keeps the values of the real vector.
+ */
+ private final int[] values;
/**
* Private constructor. NOT for public use.
+ *
+ * @param values Value data
+ * @param nocopy Flag to use without copying.
*/
private IntegerVector(int[] values, boolean nocopy) {
- if(nocopy) {
+ if (nocopy) {
this.values = values;
- }
- else {
+ } else {
this.values = new int[values.length];
System.arraycopy(values, 0, this.values, 0, values.length);
}
@@ -85,13 +102,15 @@ public class IntegerVector extends AbstractNumberVector<IntegerVector, Integer>
*
* @throws IllegalArgumentException if the specified dimension is out of range
* of the possible attributes
+ *
+ * {@inheritDoc}
*/
@Override
+ @Deprecated
public Integer getValue(int dimension) {
try {
- return values[dimension - 1];
- }
- catch(IndexOutOfBoundsException e) {
+ return Integer.valueOf(values[dimension - 1]);
+ } catch (IndexOutOfBoundsException e) {
throw new IllegalArgumentException("Dimension " + dimension + " out of range.");
}
}
@@ -104,13 +123,14 @@ public class IntegerVector extends AbstractNumberVector<IntegerVector, Integer>
*
* @throws IllegalArgumentException if the specified dimension is out of range
* of the possible attributes
+ *
+ * {@inheritDoc}
*/
@Override
public double doubleValue(int dimension) {
try {
return values[dimension - 1];
- }
- catch(IndexOutOfBoundsException e) {
+ } catch (IndexOutOfBoundsException e) {
throw new IllegalArgumentException("Dimension " + dimension + " out of range.");
}
}
@@ -123,13 +143,14 @@ public class IntegerVector extends AbstractNumberVector<IntegerVector, Integer>
*
* @throws IllegalArgumentException if the specified dimension is out of range
* of the possible attributes
+ *
+ * {@inheritDoc}
*/
@Override
public long longValue(int dimension) {
try {
return values[dimension - 1];
- }
- catch(IndexOutOfBoundsException e) {
+ } catch (IndexOutOfBoundsException e) {
throw new IllegalArgumentException("Dimension " + dimension + " out of range.");
}
}
@@ -148,7 +169,7 @@ public class IntegerVector extends AbstractNumberVector<IntegerVector, Integer>
@Override
public Vector getColumnVector() {
double[] data = new double[values.length];
- for(int i = 0; i < values.length; i++) {
+ for (int i = 0; i < values.length; i++) {
data[i] = values[i];
}
return new Vector(data);
@@ -156,76 +177,180 @@ public class IntegerVector extends AbstractNumberVector<IntegerVector, Integer>
@Override
public String toString() {
- StringBuffer featureLine = new StringBuffer();
- for(int i = 0; i < values.length; i++) {
+ StringBuilder featureLine = new StringBuilder();
+ for (int i = 0; i < values.length; i++) {
featureLine.append(values[i]);
- if(i + 1 < values.length) {
+ if (i + 1 < values.length) {
featureLine.append(ATTRIBUTE_SEPARATOR);
}
}
return featureLine.toString();
}
- @Override
- public <A> IntegerVector newFeatureVector(A array, ArrayAdapter<Integer, A> adapter) {
- int dim = adapter.size(array);
- int[] values = new int[dim];
- for(int i = 0; i < dim; i++) {
- values[i] = adapter.get(array, i);
+ /**
+ * Factory for integer vectors.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.has IntegerVector
+ */
+ public static class Factory extends AbstractNumberVector.Factory<IntegerVector, Integer> {
+ @Override
+ public <A> IntegerVector newFeatureVector(A array, ArrayAdapter<Integer, A> adapter) {
+ int dim = adapter.size(array);
+ int[] values = new int[dim];
+ for (int i = 0; i < dim; i++) {
+ values[i] = adapter.get(array, i);
+ }
+ return new IntegerVector(values, true);
}
- return new IntegerVector(values, true);
- }
- @Override
- public <A> IntegerVector newNumberVector(A array, NumberArrayAdapter<?, A> adapter) {
- int dim = adapter.size(array);
- int[] values = new int[dim];
- for(int i = 0; i < dim; i++) {
- values[i] = adapter.getInteger(array, i);
+ @Override
+ public <A> IntegerVector newNumberVector(A array, NumberArrayAdapter<?, ? super A> adapter) {
+ int dim = adapter.size(array);
+ int[] values = new int[dim];
+ for (int i = 0; i < dim; i++) {
+ values[i] = adapter.getInteger(array, i);
+ }
+ return new IntegerVector(values, true);
}
- return new IntegerVector(values, true);
- }
- @Override
- public IntegerVector fromByteBuffer(ByteBuffer buffer) throws IOException {
- final short dimensionality = buffer.getShort();
- final int len = ByteArrayUtil.SIZE_SHORT + ByteArrayUtil.SIZE_DOUBLE * dimensionality;
- if(buffer.remaining() < len) {
- throw new IOException("Not enough data for a double vector!");
- }
- int[] values = new int[dimensionality];
- buffer.asIntBuffer().get(values);
- return new IntegerVector(values, false);
- }
+ @Override
+ public ByteBufferSerializer<IntegerVector> getDefaultSerializer() {
+ return VARIABLE_SERIALIZER;
+ }
+
+ @Override
+ public Class<? super IntegerVector> getRestrictionClass() {
+ return IntegerVector.class;
+ }
- @Override
- public void toByteBuffer(ByteBuffer buffer, IntegerVector vec) throws IOException {
- final short dimensionality = buffer.getShort();
- final int len = ByteArrayUtil.SIZE_SHORT + ByteArrayUtil.SIZE_DOUBLE * dimensionality;
- if(buffer.remaining() < len) {
- throw new IOException("Not enough space for the double vector!");
- }
- buffer.putShort(dimensionality);
- buffer.asIntBuffer().put(vec.values);
+ /**
+ * Parameterization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public static class Parameterizer extends AbstractParameterizer {
+ @Override
+ protected IntegerVector.Factory makeInstance() {
+ return STATIC;
+ }
+ }
}
- @Override
- public int getByteSize(IntegerVector vec) {
- return ByteArrayUtil.SIZE_SHORT + ByteArrayUtil.SIZE_DOUBLE * vec.getDimensionality();
+ /**
+ * Serialization class for dense integer vectors with up to 127 dimensions, by
+ * using a byte for storing the dimensionality.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.uses IntegerVector - - «serializes»
+ */
+ public static class SmallSerializer implements ByteBufferSerializer<IntegerVector> {
+ @Override
+ public IntegerVector fromByteBuffer(ByteBuffer buffer) throws IOException {
+ final byte dimensionality = buffer.get();
+ assert (buffer.remaining() >= ByteArrayUtil.SIZE_INT * dimensionality);
+ final int[] values = new int[dimensionality];
+ for (int i = 0; i < dimensionality; i++) {
+ values[i] = buffer.getInt();
+ }
+ return new IntegerVector(values, true);
+ }
+
+ @Override
+ public void toByteBuffer(ByteBuffer buffer, IntegerVector vec) throws IOException {
+ assert (vec.values.length < Byte.MAX_VALUE) : "This serializer only supports a maximum dimensionality of " + Byte.MAX_VALUE + "!";
+ assert (buffer.remaining() >= ByteArrayUtil.SIZE_INT * vec.values.length);
+ buffer.put((byte) vec.values.length);
+ for (int i = 0; i < vec.values.length; i++) {
+ buffer.putInt(vec.values[i]);
+ }
+ }
+
+ @Override
+ public int getByteSize(IntegerVector vec) {
+ assert (vec.values.length < Byte.MAX_VALUE) : "This serializer only supports a maximum dimensionality of " + Byte.MAX_VALUE + "!";
+ return ByteArrayUtil.SIZE_BYTE + ByteArrayUtil.SIZE_INT * vec.getDimensionality();
+ }
}
+ /**
+ * Serialization class for dense integer vectors with up to
+ * {@link Short#MAX_VALUE} dimensions, by using a short for storing the
+ * dimensionality.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.uses IntegerVector - - «serializes»
+ */
+ public static class ShortSerializer implements ByteBufferSerializer<IntegerVector> {
+ @Override
+ public IntegerVector fromByteBuffer(ByteBuffer buffer) throws IOException {
+ final short dimensionality = buffer.getShort();
+ assert (buffer.remaining() >= ByteArrayUtil.SIZE_INT * dimensionality);
+ final int[] values = new int[dimensionality];
+ for (int i = 0; i < dimensionality; i++) {
+ values[i] = buffer.getInt();
+ }
+ return new IntegerVector(values, true);
+ }
+
+ @Override
+ public void toByteBuffer(ByteBuffer buffer, IntegerVector vec) throws IOException {
+ assert (vec.values.length < Short.MAX_VALUE) : "This serializer only supports a maximum dimensionality of " + Short.MAX_VALUE + "!";
+ assert (buffer.remaining() >= ByteArrayUtil.SIZE_INT * vec.values.length);
+ buffer.putShort((short) vec.values.length);
+ for (int i = 0; i < vec.values.length; i++) {
+ buffer.putInt(vec.values[i]);
+ }
+ }
+
+ @Override
+ public int getByteSize(IntegerVector vec) {
+ assert (vec.values.length < Short.MAX_VALUE) : "This serializer only supports a maximum dimensionality of " + Short.MAX_VALUE + "!";
+ return ByteArrayUtil.SIZE_SHORT + ByteArrayUtil.SIZE_INT * vec.getDimensionality();
+ }
+ }
/**
- * Parameterization class
+ * Serialization class for variable dimensionality by using VarInt encoding.
*
* @author Erich Schubert
*
- * @apiviz.exclude
+ * @apiviz.uses IntegerVector - - «serializes»
*/
- public static class Parameterizer extends AbstractParameterizer {
+ public static class VariableSerializer implements ByteBufferSerializer<IntegerVector> {
+ @Override
+ public IntegerVector fromByteBuffer(ByteBuffer buffer) throws IOException {
+ final int dimensionality = ByteArrayUtil.readUnsignedVarint(buffer);
+ assert (buffer.remaining() >= ByteArrayUtil.SIZE_INT * dimensionality);
+ final int[] values = new int[dimensionality];
+ for (int i = 0; i < dimensionality; i++) {
+ values[i] = ByteArrayUtil.readSignedVarint(buffer);
+ }
+ return new IntegerVector(values, true);
+ }
+
@Override
- protected IntegerVector makeInstance() {
- return STATIC;
+ public void toByteBuffer(ByteBuffer buffer, IntegerVector vec) throws IOException {
+ assert (vec.values.length < Short.MAX_VALUE) : "This serializer only supports a maximum dimensionality of " + Short.MAX_VALUE + "!";
+ ByteArrayUtil.writeUnsignedVarint(buffer, vec.values.length);
+ for (int i = 0; i < vec.values.length; i++) {
+ ByteArrayUtil.writeSignedVarint(buffer, vec.values[i]);
+ }
+ }
+
+ @Override
+ public int getByteSize(IntegerVector vec) {
+ assert (vec.values.length < Short.MAX_VALUE) : "This serializer only supports a maximum dimensionality of " + Short.MAX_VALUE + "!";
+ int len = ByteArrayUtil.getUnsignedVarintSize(vec.values.length);
+ for (int i = 0; i < vec.values.length; i++) {
+ len += ByteArrayUtil.getSignedVarintSize(vec.values[i]);
+ }
+ return len;
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/data/LabelList.java b/src/de/lmu/ifi/dbs/elki/data/LabelList.java
index b1182112..0301eff3 100644
--- a/src/de/lmu/ifi/dbs/elki/data/LabelList.java
+++ b/src/de/lmu/ifi/dbs/elki/data/LabelList.java
@@ -23,13 +23,17 @@ package de.lmu.ifi.dbs.elki.data;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+import java.io.IOException;
+import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collection;
+import de.lmu.ifi.dbs.elki.persistent.ByteArrayUtil;
+import de.lmu.ifi.dbs.elki.persistent.ByteBufferSerializer;
import de.lmu.ifi.dbs.elki.utilities.FormatUtil;
/**
- * A list of string labels
+ * A list of string labels.
*
* @author Erich Schubert
*
@@ -37,7 +41,12 @@ import de.lmu.ifi.dbs.elki.utilities.FormatUtil;
*/
public class LabelList extends ArrayList<String> {
/**
- * Serial number
+ * Serializer.
+ */
+ public static final ByteBufferSerializer<LabelList> SERIALIZER = new Serializer();
+
+ /**
+ * Serial number.
*/
private static final long serialVersionUID = 1L;
@@ -70,4 +79,42 @@ public class LabelList extends ArrayList<String> {
public String toString() {
return FormatUtil.format(this, " ");
}
-} \ No newline at end of file
+
+ /**
+ * Serialization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.uses SimpleClassLabel - - «serializes»
+ */
+ public static class Serializer implements ByteBufferSerializer<LabelList> {
+ @Override
+ public LabelList fromByteBuffer(ByteBuffer buffer) throws IOException {
+ final int cnt = ByteArrayUtil.readUnsignedVarint(buffer);
+ LabelList ret = new LabelList(cnt);
+ for (int i = 0; i < cnt; i++) {
+ ret.add(ByteArrayUtil.STRING_SERIALIZER.fromByteBuffer(buffer));
+ }
+ return ret;
+ }
+
+ @Override
+ public void toByteBuffer(ByteBuffer buffer, LabelList object) throws IOException {
+ final int cnt = object.size();
+ ByteArrayUtil.writeUnsignedVarint(buffer, cnt);
+ for (int i = 0; i < cnt; i++) {
+ ByteArrayUtil.STRING_SERIALIZER.toByteBuffer(buffer, object.get(i));
+ }
+ }
+
+ @Override
+ public int getByteSize(LabelList object) throws IOException {
+ final int cnt = object.size();
+ int size = ByteArrayUtil.getUnsignedVarintSize(cnt);
+ for (int i = 0; i < cnt; i++) {
+ size += ByteArrayUtil.STRING_SERIALIZER.getByteSize(object.get(i));
+ }
+ return size;
+ }
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/data/ModifiableHyperBoundingBox.java b/src/de/lmu/ifi/dbs/elki/data/ModifiableHyperBoundingBox.java
index b11411bd..360fe5e9 100644
--- a/src/de/lmu/ifi/dbs/elki/data/ModifiableHyperBoundingBox.java
+++ b/src/de/lmu/ifi/dbs/elki/data/ModifiableHyperBoundingBox.java
@@ -34,12 +34,12 @@ import de.lmu.ifi.dbs.elki.logging.LoggingConfiguration;
*/
public class ModifiableHyperBoundingBox extends HyperBoundingBox {
/**
- * Serial version
+ * Serial version.
*/
private static final long serialVersionUID = 1;
/**
- * Constructor
+ * Constructor.
*/
public ModifiableHyperBoundingBox() {
super();
@@ -117,7 +117,7 @@ public class ModifiableHyperBoundingBox extends HyperBoundingBox {
}
/**
- * Extend the bounding box by some other spatial object
+ * Extend the bounding box by some other spatial object.
*
* @param obj Spatial object to extend with
* @return true when the MBR changed.
@@ -127,8 +127,8 @@ public class ModifiableHyperBoundingBox extends HyperBoundingBox {
assert (!LoggingConfiguration.DEBUG || (obj.getDimensionality() == dim));
boolean extended = false;
for(int i = 0; i < dim; i++) {
- final double omin = obj.getMin(i + 1);
- final double omax = obj.getMax(i + 1);
+ final double omin = obj.getMin(i);
+ final double omax = obj.getMax(i);
if(omin < min[i]) {
min[i] = omin;
extended = true;
diff --git a/src/de/lmu/ifi/dbs/elki/data/NumberVector.java b/src/de/lmu/ifi/dbs/elki/data/NumberVector.java
index dcb869f8..ac5e302a 100644
--- a/src/de/lmu/ifi/dbs/elki/data/NumberVector.java
+++ b/src/de/lmu/ifi/dbs/elki/data/NumberVector.java
@@ -26,7 +26,6 @@ package de.lmu.ifi.dbs.elki.data;
import de.lmu.ifi.dbs.elki.data.spatial.SpatialComparable;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector;
import de.lmu.ifi.dbs.elki.utilities.datastructures.arraylike.NumberArrayAdapter;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable;
/**
* Interface NumberVector defines the methods that should be implemented by any
@@ -34,13 +33,16 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable;
*
* @author Arthur Zimek
*
- * @param <V> the type of NumberVector implemented by a subclass
* @param <N> the type of the attribute values
*
* @apiviz.landmark
* @apiviz.has Vector
*/
-public interface NumberVector<V extends NumberVector<? extends V, N>, N extends Number> extends FeatureVector<V, N>, SpatialComparable, Parameterizable {
+public interface NumberVector<N extends Number> extends FeatureVector<N>, SpatialComparable {
+ @Deprecated
+ @Override
+ N getValue(int dimension);
+
/**
* Returns the value in the specified dimension as double.
*
@@ -48,7 +50,7 @@ public interface NumberVector<V extends NumberVector<? extends V, N>, N extends
* {@code getValue(dim).doubleValue()}, but usually this is much more
* efficient due to boxing/unboxing cost.
*
- * @param dimension the desired dimension, where 1 &le; dimension &le;
+ * @param dimension the desired dimension, where 0 &le; dimension &lt;
* <code>this.getDimensionality()</code>
* @return the value in the specified dimension
*/
@@ -61,7 +63,7 @@ public interface NumberVector<V extends NumberVector<? extends V, N>, N extends
* {@code getValue(dim).floatValue()}, but usually this is much more efficient
* due to boxing/unboxing cost.
*
- * @param dimension the desired dimension, where 1 &le; dimension &le;
+ * @param dimension the desired dimension, where 0 &le; dimension &lt;
* <code>this.getDimensionality()</code>
* @return the value in the specified dimension
*/
@@ -74,7 +76,7 @@ public interface NumberVector<V extends NumberVector<? extends V, N>, N extends
* {@code getValue(dim).intValue()}, but usually this is much more efficient
* due to boxing/unboxing cost.
*
- * @param dimension the desired dimension, where 1 &le; dimension &le;
+ * @param dimension the desired dimension, where 0 &le; dimension &lt;
* <code>this.getDimensionality()</code>
* @return the value in the specified dimension
*/
@@ -87,7 +89,7 @@ public interface NumberVector<V extends NumberVector<? extends V, N>, N extends
* {@code getValue(dim).longValue()}, but usually this is much more efficient
* due to boxing/unboxing cost.
*
- * @param dimension the desired dimension, where 1 &le; dimension &le;
+ * @param dimension the desired dimension, where 0 &le; dimension &lt;
* <code>this.getDimensionality()</code>
* @return the value in the specified dimension
*/
@@ -100,7 +102,7 @@ public interface NumberVector<V extends NumberVector<? extends V, N>, N extends
* {@code getValue(dim).shortValue()}, but usually this is much more efficient
* due to boxing/unboxing cost.
*
- * @param dimension the desired dimension, where 1 &le; dimension &le;
+ * @param dimension the desired dimension, where 0 &le; dimension &lt;
* <code>this.getDimensionality()</code>
* @return the value in the specified dimension
*/
@@ -113,7 +115,7 @@ public interface NumberVector<V extends NumberVector<? extends V, N>, N extends
* {@code getValue(dim).byteValue()}, but usually this is much more efficient
* due to boxing/unboxing cost.
*
- * @param dimension the desired dimension, where 1 &le; dimension &le;
+ * @param dimension the desired dimension, where 0 &le; dimension &lt;
* <code>this.getDimensionality()</code>
* @return the value in the specified dimension
*/
@@ -130,20 +132,32 @@ public interface NumberVector<V extends NumberVector<? extends V, N>, N extends
Vector getColumnVector();
/**
- * Returns a new NumberVector of N for the given values.
+ * Factory API for this feature vector.
*
- * @param values the values of the NumberVector
- * @return a new NumberVector of N for the given values
- */
- V newNumberVector(double[] values);
-
- /**
- * Instantiate from any number-array like object.
+ * @author Erich Schubert
*
- * @param <A> Array type
- * @param array Array
- * @param adapter Adapter
- * @return a new NumberVector of N for the given values.
+ * @apiviz.has NumberVector
+ *
+ * @param <V> Vector type
+ * @param <N> Data type of vector
*/
- <A> V newNumberVector(A array, NumberArrayAdapter<?, A> adapter);
-} \ No newline at end of file
+ interface Factory<V extends NumberVector<? extends N>, N extends Number> extends FeatureVector.Factory<V, N> {
+ /**
+ * Returns a new NumberVector of N for the given values.
+ *
+ * @param values the values of the NumberVector
+ * @return a new NumberVector of N for the given values
+ */
+ V newNumberVector(double[] values);
+
+ /**
+ * Instantiate from any number-array like object.
+ *
+ * @param <A> Array type
+ * @param array Array
+ * @param adapter Adapter
+ * @return a new NumberVector of N for the given values.
+ */
+ <A> V newNumberVector(A array, NumberArrayAdapter<?, ? super A> adapter);
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/data/OneDimensionalDoubleVector.java b/src/de/lmu/ifi/dbs/elki/data/OneDimensionalDoubleVector.java
index 382ff9ae..7198d820 100644
--- a/src/de/lmu/ifi/dbs/elki/data/OneDimensionalDoubleVector.java
+++ b/src/de/lmu/ifi/dbs/elki/data/OneDimensionalDoubleVector.java
@@ -24,6 +24,7 @@ package de.lmu.ifi.dbs.elki.data;
*/
import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector;
+import de.lmu.ifi.dbs.elki.persistent.ByteBufferSerializer;
import de.lmu.ifi.dbs.elki.utilities.datastructures.arraylike.ArrayAdapter;
import de.lmu.ifi.dbs.elki.utilities.datastructures.arraylike.NumberArrayAdapter;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
@@ -35,14 +36,14 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
*
* @author Erich Schubert
*/
-public class OneDimensionalDoubleVector extends AbstractNumberVector<OneDimensionalDoubleVector, Double> {
+public class OneDimensionalDoubleVector extends AbstractNumberVector<Double> {
/**
- * Static factory instance
+ * Static factory instance.
*/
- public static final OneDimensionalDoubleVector STATIC = new OneDimensionalDoubleVector(Double.NaN);
+ public static final OneDimensionalDoubleVector.Factory STATIC = new OneDimensionalDoubleVector.Factory();
/**
- * The actual data value
+ * The actual data value.
*/
double val;
@@ -62,19 +63,20 @@ public class OneDimensionalDoubleVector extends AbstractNumberVector<OneDimensio
@Override
public double doubleValue(int dimension) {
- assert (dimension == 1) : "Non-existant dimension accessed.";
+ assert (dimension == 0) : "Non-existant dimension accessed.";
return val;
}
@Override
public long longValue(int dimension) {
- assert (dimension == 1) : "Non-existant dimension accessed.";
+ assert (dimension == 0) : "Non-existant dimension accessed.";
return (long) val;
}
+ @Deprecated
@Override
public Double getValue(int dimension) {
- assert (dimension == 1) : "Incorrect dimension requested for 1-dimensional vector.";
+ assert (dimension == 0) : "Incorrect dimension requested for 1-dimensional vector.";
return this.val;
}
@@ -83,29 +85,49 @@ public class OneDimensionalDoubleVector extends AbstractNumberVector<OneDimensio
return new Vector(new double[] { val });
}
- @Override
- public <A> OneDimensionalDoubleVector newFeatureVector(A array, ArrayAdapter<Double, A> adapter) {
- assert (adapter.size(array) == 1) : "Incorrect dimensionality for 1-dimensional vector.";
- return new OneDimensionalDoubleVector(adapter.get(array, 0));
- }
-
- @Override
- public <A> OneDimensionalDoubleVector newNumberVector(A array, NumberArrayAdapter<?, A> adapter) {
- assert (adapter.size(array) == 1) : "Incorrect dimensionality for 1-dimensional vector.";
- return new OneDimensionalDoubleVector(adapter.getDouble(array, 0));
- }
-
/**
- * Parameterization class
+ * Factory class.
*
* @author Erich Schubert
*
- * @apiviz.exclude
+ * @apiviz.has OneDimensionalDoubleVector
*/
- public static class Parameterizer extends AbstractParameterizer {
+ public static class Factory extends AbstractNumberVector.Factory<OneDimensionalDoubleVector, Double> {
+ @Override
+ public <A> OneDimensionalDoubleVector newFeatureVector(A array, ArrayAdapter<Double, A> adapter) {
+ assert (adapter.size(array) == 1) : "Incorrect dimensionality for 1-dimensional vector.";
+ return new OneDimensionalDoubleVector(adapter.get(array, 0));
+ }
+
+ @Override
+ public <A> OneDimensionalDoubleVector newNumberVector(A array, NumberArrayAdapter<?, ? super A> adapter) {
+ assert (adapter.size(array) == 1) : "Incorrect dimensionality for 1-dimensional vector.";
+ return new OneDimensionalDoubleVector(adapter.getDouble(array, 0));
+ }
+
@Override
- protected OneDimensionalDoubleVector makeInstance() {
- return STATIC;
+ public ByteBufferSerializer<OneDimensionalDoubleVector> getDefaultSerializer() {
+ // FIXME: add a serializer
+ return null;
+ }
+
+ @Override
+ public Class<? super OneDimensionalDoubleVector> getRestrictionClass() {
+ return OneDimensionalDoubleVector.class;
+ }
+
+ /**
+ * Parameterization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public static class Parameterizer extends AbstractParameterizer {
+ @Override
+ protected OneDimensionalDoubleVector.Factory makeInstance() {
+ return STATIC;
+ }
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/data/RationalNumber.java b/src/de/lmu/ifi/dbs/elki/data/RationalNumber.java
index 5b2e08fa..cf388481 100644
--- a/src/de/lmu/ifi/dbs/elki/data/RationalNumber.java
+++ b/src/de/lmu/ifi/dbs/elki/data/RationalNumber.java
@@ -23,11 +23,7 @@ package de.lmu.ifi.dbs.elki.data;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.PrintStream;
import java.math.BigInteger;
-import java.util.Random;
/**
* RationalNumber represents rational numbers in arbitrary precision. Note that
@@ -74,7 +70,7 @@ public class RationalNumber extends Number implements Arithmetic<RationalNumber>
* @throws IllegalArgumentException if {@link BigInteger#equals(Object)
* denominator.equals(}{@link BigInteger#ZERO BigInteger.ZERO)}
*/
- public RationalNumber(final BigInteger numerator, final BigInteger denominator) throws IllegalArgumentException {
+ public RationalNumber(final BigInteger numerator, final BigInteger denominator) {
if(denominator.equals(BigInteger.ZERO)) {
throw new IllegalArgumentException("denominator is 0");
}
@@ -113,6 +109,17 @@ public class RationalNumber extends Number implements Arithmetic<RationalNumber>
}
/**
+ * Constructs a RationalNumber out of the given double number.
+ *
+ * @param number a double number to be represented as a RationalNumber
+ * @throws IllegalArgumentException if the given Double is infinit or not a
+ * number
+ */
+ public RationalNumber(final double number) throws IllegalArgumentException {
+ this(Double.toString(number));
+ }
+
+ /**
* Constructs a RationalNumber for a given String representing a double.
*
* @param doubleString a String representing a double number
@@ -147,7 +154,7 @@ public class RationalNumber extends Number implements Arithmetic<RationalNumber>
power -= fractionalPart.length();
denominator = BigInteger.ONE;
// translate power notation
- StringBuffer multiplicandString = new StringBuffer("1");
+ StringBuilder multiplicandString = new StringBuilder("1");
for(int i = 0; i < Math.abs(power); i++) {
multiplicandString.append('0');
}
@@ -389,107 +396,4 @@ public class RationalNumber extends Number implements Arithmetic<RationalNumber>
public RationalNumber copy() {
return new RationalNumber(numerator, denominator);
}
-
- /**
- * Compares doubles and RationalNumbers wrt efficiency and accuracy.
- *
- * @param n the number of random numbers
- * @param out target to print results to
- */
- public static void test(final int n, final PrintStream out) {
- Random rnd = new Random();
- int[] testNumbers1 = new int[n];
- int[] testNumbers2 = new int[n];
- RationalNumber[] rationalNumbers = new RationalNumber[n];
- double[] doubles = new double[n];
-
- for(int i = 0; i < n; i++) {
- testNumbers1[i] = rnd.nextInt();
- int second = rnd.nextInt();
- while(second == 0) {
- second = rnd.nextInt();
- }
- testNumbers2[i] = second;
- rationalNumbers[i] = new RationalNumber(testNumbers1[i], testNumbers2[i]);
- doubles[i] = (double) testNumbers1[i] / (double) testNumbers2[i];
- }
-
- long doubleStart = System.currentTimeMillis();
- for(int i = 0; i < n; i++) {
- doubles[i] = doubles[i] * 7.0 / 5.0 * 5.0 / 7.0 * testNumbers2[i];
- }
- long doubleTime = System.currentTimeMillis() - doubleStart;
- long rnStart = System.currentTimeMillis();
- for(int i = 0; i < n; i++) {
- rationalNumbers[i] = rationalNumbers[i].times(new RationalNumber(7, 1)).divided(new RationalNumber(5, 1)).times(new RationalNumber(5, 1)).divided(new RationalNumber(7, 1)).times(new RationalNumber(testNumbers2[i], 1));
- }
- long rnTime = System.currentTimeMillis() - rnStart;
- out.println("Efficiency: ");
- out.println(" time required for a predefined sequence of operations on " + n + " random numbers:");
- out.println(" double: " + doubleTime);
- out.println(" RationalNumber: " + rnTime);
- int accuracyDouble = n;
- double deviationDouble = 0.0;
- int accuracyRN = n;
- double deviationRN = 0.0;
- for(int i = 0; i < n; i++) {
- if((int) doubles[i] != testNumbers1[i]) {
- accuracyDouble--;
- deviationDouble += testNumbers1[i] - doubles[i];
- }
- if(rationalNumbers[i].intValue() != testNumbers1[i]) {
- accuracyRN--;
- deviationRN += testNumbers1[i] - rationalNumbers[i].doubleValue();
- }
- }
- out.println("\nAccuracy: ");
- out.println(" percentage of correctly recomputed " + n + " random numbers for a sequence of predefined operations:");
- out.println(" double: " + (double) accuracyDouble / (double) n);
- out.println(" RationalNumber: " + (double) accuracyRN / (double) n);
- out.println(" average deviation:");
- out.println(" double: " + deviationDouble / n);
- out.println(" RationalNumber: " + deviationRN / n);
-
- out.print("Overview:\n--------------------------------------------\n");
- for(int i = 0; i < n; i++) {
- out.print("target: ");
- out.print((double) testNumbers1[i]);
- out.print("\ndouble: ");
- out.print(doubles[i]);
- out.print("\nrationalnr: ");
- out.print(rationalNumbers[i].doubleValue());
- out.print("\n--------------------------------------------\n");
- }
- }
-
- /**
- * Calls test for a given number of numbers and a optionally given target
- * file.
- *
- * @param args &lt;int&gt; [&lt;filename&gt;]
- */
- public static void main(String[] args) {
- try {
- int numbers = Integer.parseInt(args[0]);
- PrintStream out;
- try {
- out = new PrintStream(new File(args[1]));
- }
- catch(FileNotFoundException e) {
- System.err.println(e.getMessage());
- System.err.println("printing output to STDOUT");
- out = System.out;
- }
- catch(ArrayIndexOutOfBoundsException e) {
- out = System.out;
- }
- test(numbers, out);
- }
- catch(Exception e) {
- System.err.print("Usage:\njava ");
- System.err.print(RationalNumber.class.getName());
- System.err.println(" <numberOfTestnumbers> [<filename>]");
- System.err.println("If <filename> is ommitted, output will be printed to STDOUT.");
- }
- }
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/data/SimpleClassLabel.java b/src/de/lmu/ifi/dbs/elki/data/SimpleClassLabel.java
index ad88295e..b0ee779c 100644
--- a/src/de/lmu/ifi/dbs/elki/data/SimpleClassLabel.java
+++ b/src/de/lmu/ifi/dbs/elki/data/SimpleClassLabel.java
@@ -23,6 +23,14 @@ package de.lmu.ifi.dbs.elki.data;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+import de.lmu.ifi.dbs.elki.data.type.SimpleTypeInformation;
+import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
+import de.lmu.ifi.dbs.elki.persistent.ByteArrayUtil;
+import de.lmu.ifi.dbs.elki.persistent.ByteBufferSerializer;
+
/**
* A simple class label casting a String as it is as label.
*
@@ -32,6 +40,11 @@ package de.lmu.ifi.dbs.elki.data;
*/
public class SimpleClassLabel extends ClassLabel {
/**
+ * Serializer.
+ */
+ public static final ByteBufferSerializer<SimpleClassLabel> SERIALIZER = new Serializer();
+
+ /**
* Holds the String designating the label.
*/
private String label;
@@ -51,6 +64,8 @@ public class SimpleClassLabel extends ClassLabel {
* Strings they represent.
* <p/>
* That is, the result equals <code>this.label.compareTo(o.label)</code>.
+ *
+ * {@inheritDoc}
*/
@Override
public int compareTo(ClassLabel o) {
@@ -61,6 +76,8 @@ public class SimpleClassLabel extends ClassLabel {
/**
* The hash code of a simple class label is the hash code of the String
* represented by the ClassLabel.
+ *
+ * {@inheritDoc}
*/
@Override
public int hashCode() {
@@ -78,13 +95,13 @@ public class SimpleClassLabel extends ClassLabel {
*/
@Override
public boolean equals(Object o) {
- if(this == o) {
+ if (this == o) {
return true;
}
- if(o == null || getClass() != o.getClass()) {
+ if (o == null || getClass() != o.getClass()) {
return false;
}
- if(!super.equals(o)) {
+ if (!super.equals(o)) {
return false;
}
final SimpleClassLabel that = (SimpleClassLabel) o;
@@ -103,7 +120,31 @@ public class SimpleClassLabel extends ClassLabel {
}
/**
- * Factory class
+ * Serialization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.uses SimpleClassLabel - - «serializes»
+ */
+ public static class Serializer implements ByteBufferSerializer<SimpleClassLabel> {
+ @Override
+ public SimpleClassLabel fromByteBuffer(ByteBuffer buffer) throws IOException {
+ return new SimpleClassLabel(ByteArrayUtil.STRING_SERIALIZER.fromByteBuffer(buffer));
+ }
+
+ @Override
+ public void toByteBuffer(ByteBuffer buffer, SimpleClassLabel object) throws IOException {
+ ByteArrayUtil.STRING_SERIALIZER.toByteBuffer(buffer, object.label);
+ }
+
+ @Override
+ public int getByteSize(SimpleClassLabel object) throws IOException {
+ return ByteArrayUtil.STRING_SERIALIZER.getByteSize(object.label);
+ }
+ }
+
+ /**
+ * Factory class.
*
* @author Erich Schubert
*
@@ -113,12 +154,18 @@ public class SimpleClassLabel extends ClassLabel {
public static class Factory extends ClassLabel.Factory<SimpleClassLabel> {
@Override
public SimpleClassLabel makeFromString(String lbl) {
+ lbl = lbl.intern();
SimpleClassLabel l = existing.get(lbl);
- if(l == null) {
+ if (l == null) {
l = new SimpleClassLabel(lbl);
existing.put(lbl, l);
}
return l;
}
+
+ @Override
+ public SimpleTypeInformation<? super SimpleClassLabel> getTypeInformation() {
+ return TypeUtil.SIMPLE_CLASSLABEL;
+ }
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/data/SparseDoubleVector.java b/src/de/lmu/ifi/dbs/elki/data/SparseDoubleVector.java
index 10536058..1b07d5e9 100644
--- a/src/de/lmu/ifi/dbs/elki/data/SparseDoubleVector.java
+++ b/src/de/lmu/ifi/dbs/elki/data/SparseDoubleVector.java
@@ -31,8 +31,8 @@ import gnu.trove.map.hash.TIntDoubleHashMap;
import java.util.Arrays;
import java.util.BitSet;
-import de.lmu.ifi.dbs.elki.datasource.parser.SparseNumberVectorLabelParser;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector;
+import de.lmu.ifi.dbs.elki.persistent.ByteBufferSerializer;
import de.lmu.ifi.dbs.elki.utilities.datastructures.arraylike.ArrayAdapter;
import de.lmu.ifi.dbs.elki.utilities.datastructures.arraylike.NumberArrayAdapter;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
@@ -42,27 +42,27 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
* A SparseDoubleVector is to store real values as double values.
* </p>
*
- * A SparseDoubleVector only requires storage for those attribute values that are
- * non-zero.
+ * A SparseDoubleVector only requires storage for those attribute values that
+ * are non-zero.
*
* @author Arthur Zimek
*/
// TODO: implement ByteArraySerializer<SparseDoubleVector>
-public class SparseDoubleVector extends AbstractNumberVector<SparseDoubleVector, Double> implements SparseNumberVector<SparseDoubleVector, Double> {
+public class SparseDoubleVector extends AbstractNumberVector<Double> implements SparseNumberVector<Double> {
/**
- * Static instance
+ * Static instance.
*/
- public static final SparseDoubleVector STATIC = new SparseDoubleVector(new int[0], new double[0], -1);
+ public static final SparseDoubleVector.Factory FACTORY = new SparseDoubleVector.Factory();
/**
- * Indexes of values
+ * Indexes of values.
*/
- private int[] indexes;
+ private final int[] indexes;
/**
- * Stored values
+ * Stored values.
*/
- private double[] values;
+ private final double[] values;
/**
* The dimensionality of this feature vector.
@@ -94,7 +94,7 @@ public class SparseDoubleVector extends AbstractNumberVector<SparseDoubleVector,
* zero is bigger than the given dimensionality)
*/
public SparseDoubleVector(TIntDoubleMap values, int dimensionality) throws IllegalArgumentException {
- if(values.size() > dimensionality) {
+ if (values.size() > dimensionality) {
throw new IllegalArgumentException("values.size() > dimensionality!");
}
@@ -111,13 +111,13 @@ public class SparseDoubleVector extends AbstractNumberVector<SparseDoubleVector,
}
// Import the values accordingly
{
- for(int i = 0; i < values.size(); i++) {
+ for (int i = 0; i < values.size(); i++) {
this.values[i] = values.get(this.indexes[i]);
}
}
this.dimensionality = dimensionality;
final int maxdim = getMaxDim();
- if(maxdim > dimensionality) {
+ if (maxdim > dimensionality) {
throw new IllegalArgumentException("Given dimensionality " + dimensionality + " is too small w.r.t. the given values (occurring maximum: " + maxdim + ").");
}
}
@@ -128,10 +128,9 @@ public class SparseDoubleVector extends AbstractNumberVector<SparseDoubleVector,
* @return the maximum dimensionality seen
*/
private int getMaxDim() {
- if(this.indexes.length == 0) {
+ if (this.indexes.length == 0) {
return 0;
- }
- else {
+ } else {
return this.indexes[this.indexes.length - 1];
}
}
@@ -151,8 +150,8 @@ public class SparseDoubleVector extends AbstractNumberVector<SparseDoubleVector,
// Count the number of non-zero entries
int size = 0;
{
- for(int i = 0; i < values.length; i++) {
- if(values[i] != 0.0f) {
+ for (int i = 0; i < values.length; i++) {
+ if (values[i] != 0.0f) {
size++;
}
}
@@ -163,9 +162,9 @@ public class SparseDoubleVector extends AbstractNumberVector<SparseDoubleVector,
// Copy the values
{
int pos = 0;
- for(int i = 0; i < values.length; i++) {
+ for (int i = 0; i < values.length; i++) {
double value = values[i];
- if(value != 0.0f) {
+ if (value != 0.0f) {
this.indexes[pos] = i + 1;
this.values[pos] = value;
pos++;
@@ -191,19 +190,19 @@ public class SparseDoubleVector extends AbstractNumberVector<SparseDoubleVector,
@Override
public void setDimensionality(int dimensionality) throws IllegalArgumentException {
final int maxdim = getMaxDim();
- if(maxdim > dimensionality) {
+ if (maxdim > dimensionality) {
throw new IllegalArgumentException("Given dimensionality " + dimensionality + " is too small w.r.t. the given values (occurring maximum: " + maxdim + ").");
}
this.dimensionality = dimensionality;
}
@Override
+ @Deprecated
public Double getValue(int dimension) {
int pos = Arrays.binarySearch(this.indexes, dimension);
- if(pos >= 0) {
+ if (pos >= 0) {
return values[pos];
- }
- else {
+ } else {
return 0.0;
}
}
@@ -211,10 +210,9 @@ public class SparseDoubleVector extends AbstractNumberVector<SparseDoubleVector,
@Override
public double doubleValue(int dimension) {
int pos = Arrays.binarySearch(this.indexes, dimension);
- if(pos >= 0) {
+ if (pos >= 0) {
return values[pos];
- }
- else {
+ } else {
return 0.0;
}
}
@@ -222,24 +220,23 @@ public class SparseDoubleVector extends AbstractNumberVector<SparseDoubleVector,
@Override
public long longValue(int dimension) {
int pos = Arrays.binarySearch(this.indexes, dimension);
- if(pos >= 0) {
+ if (pos >= 0) {
return (long) values[pos];
- }
- else {
+ } else {
return 0;
}
}
@Override
public Vector getColumnVector() {
- double[] values = getValues();
- return new Vector(values);
+ return new Vector(getValues());
}
/**
* <p>
* Provides a String representation of this SparseDoubleVector as suitable for
- * {@link SparseNumberVectorLabelParser}.
+ * {@link de.lmu.ifi.dbs.elki.datasource.parser.SparseNumberVectorLabelParser}
+ * .
* </p>
*
* <p>
@@ -261,7 +258,7 @@ public class SparseDoubleVector extends AbstractNumberVector<SparseDoubleVector,
public String toString() {
StringBuilder featureLine = new StringBuilder();
featureLine.append(this.indexes.length);
- for(int i = 0; i < this.indexes.length; i++) {
+ for (int i = 0; i < this.indexes.length; i++) {
featureLine.append(ATTRIBUTE_SEPARATOR);
featureLine.append(this.indexes[i]);
featureLine.append(ATTRIBUTE_SEPARATOR);
@@ -277,65 +274,85 @@ public class SparseDoubleVector extends AbstractNumberVector<SparseDoubleVector,
* @return an array consisting of the values of this feature vector
*/
private double[] getValues() {
- double[] values = new double[dimensionality];
- for(int i = 0; i < indexes.length; i++) {
- values[this.indexes[i]] = this.values[i];
+ double[] vals = new double[dimensionality];
+ for (int i = 0; i < indexes.length; i++) {
+ vals[this.indexes[i]] = this.values[i];
}
- return values;
+ return vals;
}
- @Override
- public <A> SparseDoubleVector newFeatureVector(A array, ArrayAdapter<Double, A> adapter) {
- int dim = adapter.size(array);
- double[] values = new double[dim];
- for(int i = 0; i < dim; i++) {
- values[i] = adapter.get(array, i);
+ /**
+ * Factory class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.has SparseDoubleVector
+ */
+ public static class Factory extends AbstractNumberVector.Factory<SparseDoubleVector, Double> implements SparseNumberVector.Factory<SparseDoubleVector, Double> {
+ @Override
+ public <A> SparseDoubleVector newFeatureVector(A array, ArrayAdapter<Double, A> adapter) {
+ int dim = adapter.size(array);
+ double[] values = new double[dim];
+ for (int i = 0; i < dim; i++) {
+ values[i] = adapter.get(array, i);
+ }
+ // TODO: inefficient
+ return new SparseDoubleVector(values);
}
- // TODO: inefficient
- return new SparseDoubleVector(values);
- }
- @Override
- public <A> SparseDoubleVector newNumberVector(A array, NumberArrayAdapter<?, A> adapter) {
- int dim = adapter.size(array);
- double[] values = new double[dim];
- for(int i = 0; i < dim; i++) {
- values[i] = adapter.getDouble(array, i);
+ @Override
+ public <A> SparseDoubleVector newNumberVector(A array, NumberArrayAdapter<?, ? super A> adapter) {
+ int dim = adapter.size(array);
+ double[] values = new double[dim];
+ for (int i = 0; i < dim; i++) {
+ values[i] = adapter.getDouble(array, i);
+ }
+ // TODO: inefficient
+ return new SparseDoubleVector(values);
}
- // TODO: inefficient
- return new SparseDoubleVector(values);
- }
- @Override
- public SparseDoubleVector newNumberVector(TIntDoubleMap values, int maxdim) {
- return new SparseDoubleVector(values, maxdim);
+ @Override
+ public SparseDoubleVector newNumberVector(TIntDoubleMap values, int maxdim) {
+ return new SparseDoubleVector(values, maxdim);
+ }
+
+ @Override
+ public ByteBufferSerializer<SparseDoubleVector> getDefaultSerializer() {
+ // FIXME: add a serializer
+ return null;
+ }
+
+ @Override
+ public Class<? super SparseDoubleVector> getRestrictionClass() {
+ return SparseDoubleVector.class;
+ }
+
+ /**
+ * Parameterization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public static class Parameterizer extends AbstractParameterizer {
+ @Override
+ protected SparseDoubleVector.Factory makeInstance() {
+ return FACTORY;
+ }
+ }
}
@Override
public BitSet getNotNullMask() {
BitSet b = new BitSet();
- for(int key : indexes) {
+ for (int key : indexes) {
b.set(key);
}
return b;
}
/**
- * Parameterization class
- *
- * @author Erich Schubert
- *
- * @apiviz.exclude
- */
- public static class Parameterizer extends AbstractParameterizer {
- @Override
- protected SparseDoubleVector makeInstance() {
- return STATIC;
- }
- }
-
- /**
* Empty map.
*/
- public static final TIntDoubleMap EMPTYMAP = new TUnmodifiableIntDoubleMap(new TIntDoubleHashMap());
+ public static final TIntDoubleMap EMPTYMAP = new TUnmodifiableIntDoubleMap(new TIntDoubleHashMap());
}
diff --git a/src/de/lmu/ifi/dbs/elki/data/SparseFeatureVector.java b/src/de/lmu/ifi/dbs/elki/data/SparseFeatureVector.java
index 4359d841..97ec8857 100644
--- a/src/de/lmu/ifi/dbs/elki/data/SparseFeatureVector.java
+++ b/src/de/lmu/ifi/dbs/elki/data/SparseFeatureVector.java
@@ -26,16 +26,15 @@ package de.lmu.ifi.dbs.elki.data;
import java.util.BitSet;
/**
- * Extended interface for sparse feature vector types
+ * Extended interface for sparse feature vector types.
*
* @author Erich Schubert
*
- * @param <V> Vector type (self-reference
* @param <D> Data type
*/
-public interface SparseFeatureVector<V extends SparseFeatureVector<V, D>, D> extends FeatureVector<V, D> {
+public interface SparseFeatureVector<D> extends FeatureVector<D> {
/**
- * Bit set of non-null features
+ * Bit set of non-null features.
*
* @return Bit set
*/
diff --git a/src/de/lmu/ifi/dbs/elki/data/SparseFloatVector.java b/src/de/lmu/ifi/dbs/elki/data/SparseFloatVector.java
index 36a4e171..de3e7293 100644
--- a/src/de/lmu/ifi/dbs/elki/data/SparseFloatVector.java
+++ b/src/de/lmu/ifi/dbs/elki/data/SparseFloatVector.java
@@ -33,8 +33,8 @@ import gnu.trove.map.hash.TIntFloatHashMap;
import java.util.Arrays;
import java.util.BitSet;
-import de.lmu.ifi.dbs.elki.datasource.parser.SparseNumberVectorLabelParser;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector;
+import de.lmu.ifi.dbs.elki.persistent.ByteBufferSerializer;
import de.lmu.ifi.dbs.elki.utilities.datastructures.arraylike.ArrayAdapter;
import de.lmu.ifi.dbs.elki.utilities.datastructures.arraylike.NumberArrayAdapter;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
@@ -50,21 +50,21 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
* @author Arthur Zimek
*/
// TODO: implement ByteArraySerializer<SparseFloatVector>
-public class SparseFloatVector extends AbstractNumberVector<SparseFloatVector, Float> implements SparseNumberVector<SparseFloatVector, Float> {
+public class SparseFloatVector extends AbstractNumberVector<Float> implements SparseNumberVector<Float> {
/**
- * Static instance
+ * Static instance.
*/
- public static final SparseFloatVector STATIC = new SparseFloatVector(new int[0], new float[0], -1);
+ public static final SparseFloatVector.Factory FACTORY = new SparseFloatVector.Factory();
/**
- * Indexes of values
+ * Indexes of values.
*/
- private int[] indexes;
+ private final int[] indexes;
/**
- * Stored values
+ * Stored values.
*/
- private float[] values;
+ private final float[] values;
/**
* The dimensionality of this feature vector.
@@ -96,7 +96,7 @@ public class SparseFloatVector extends AbstractNumberVector<SparseFloatVector, F
* zero is bigger than the given dimensionality)
*/
public SparseFloatVector(TIntFloatMap values, int dimensionality) throws IllegalArgumentException {
- if(values.size() > dimensionality) {
+ if (values.size() > dimensionality) {
throw new IllegalArgumentException("values.size() > dimensionality!");
}
@@ -105,21 +105,21 @@ public class SparseFloatVector extends AbstractNumberVector<SparseFloatVector, F
// Import and sort the indexes
{
TIntFloatIterator iter = values.iterator();
- for(int i = 0; iter.hasNext(); i++) {
- this.indexes[i] = iter.key();
+ for (int i = 0; iter.hasNext(); i++) {
iter.advance();
+ this.indexes[i] = iter.key();
}
Arrays.sort(this.indexes);
}
// Import the values accordingly
{
- for(int i = 0; i < values.size(); i++) {
+ for (int i = 0; i < values.size(); i++) {
this.values[i] = values.get(this.indexes[i]);
}
}
this.dimensionality = dimensionality;
final int maxdim = getMaxDim();
- if(maxdim > dimensionality) {
+ if (maxdim > dimensionality) {
throw new IllegalArgumentException("Given dimensionality " + dimensionality + " is too small w.r.t. the given values (occurring maximum: " + maxdim + ").");
}
}
@@ -130,10 +130,9 @@ public class SparseFloatVector extends AbstractNumberVector<SparseFloatVector, F
* @return the maximum dimensionality seen
*/
private int getMaxDim() {
- if(this.indexes.length == 0) {
+ if (this.indexes.length == 0) {
return 0;
- }
- else {
+ } else {
return this.indexes[this.indexes.length - 1];
}
}
@@ -153,8 +152,8 @@ public class SparseFloatVector extends AbstractNumberVector<SparseFloatVector, F
// Count the number of non-zero entries
int size = 0;
{
- for(int i = 0; i < values.length; i++) {
- if(values[i] != 0.0f) {
+ for (int i = 0; i < values.length; i++) {
+ if (values[i] != 0.0f) {
size++;
}
}
@@ -165,9 +164,9 @@ public class SparseFloatVector extends AbstractNumberVector<SparseFloatVector, F
// Copy the values
{
int pos = 0;
- for(int i = 0; i < values.length; i++) {
+ for (int i = 0; i < values.length; i++) {
float value = values[i];
- if(value != 0.0f) {
+ if (value != 0.0f) {
this.indexes[pos] = i + 1;
this.values[pos] = value;
pos++;
@@ -193,19 +192,19 @@ public class SparseFloatVector extends AbstractNumberVector<SparseFloatVector, F
@Override
public void setDimensionality(int dimensionality) throws IllegalArgumentException {
final int maxdim = getMaxDim();
- if(maxdim > dimensionality) {
+ if (maxdim > dimensionality) {
throw new IllegalArgumentException("Given dimensionality " + dimensionality + " is too small w.r.t. the given values (occurring maximum: " + maxdim + ").");
}
this.dimensionality = dimensionality;
}
@Override
+ @Deprecated
public Float getValue(int dimension) {
int pos = Arrays.binarySearch(this.indexes, dimension);
- if(pos >= 0) {
+ if (pos >= 0) {
return values[pos];
- }
- else {
+ } else {
return 0.0f;
}
}
@@ -213,10 +212,9 @@ public class SparseFloatVector extends AbstractNumberVector<SparseFloatVector, F
@Override
public double doubleValue(int dimension) {
int pos = Arrays.binarySearch(this.indexes, dimension);
- if(pos >= 0) {
+ if (pos >= 0) {
return values[pos];
- }
- else {
+ } else {
return 0.0;
}
}
@@ -224,24 +222,23 @@ public class SparseFloatVector extends AbstractNumberVector<SparseFloatVector, F
@Override
public long longValue(int dimension) {
int pos = Arrays.binarySearch(this.indexes, dimension);
- if(pos >= 0) {
+ if (pos >= 0) {
return (long) values[pos];
- }
- else {
+ } else {
return 0;
}
}
@Override
public Vector getColumnVector() {
- double[] values = getValues();
- return new Vector(values);
+ return new Vector(getValues());
}
/**
* <p>
* Provides a String representation of this SparseFloatVector as suitable for
- * {@link SparseNumberVectorLabelParser}.
+ * {@link de.lmu.ifi.dbs.elki.datasource.parser.SparseNumberVectorLabelParser}
+ * .
* </p>
*
* <p>
@@ -263,7 +260,7 @@ public class SparseFloatVector extends AbstractNumberVector<SparseFloatVector, F
public String toString() {
StringBuilder featureLine = new StringBuilder();
featureLine.append(this.indexes.length);
- for(int i = 0; i < this.indexes.length; i++) {
+ for (int i = 0; i < this.indexes.length; i++) {
featureLine.append(ATTRIBUTE_SEPARATOR);
featureLine.append(this.indexes[i]);
featureLine.append(ATTRIBUTE_SEPARATOR);
@@ -279,78 +276,98 @@ public class SparseFloatVector extends AbstractNumberVector<SparseFloatVector, F
* @return an array consisting of the values of this feature vector
*/
private double[] getValues() {
- double[] values = new double[dimensionality];
- for(int i = 0; i < indexes.length; i++) {
- values[this.indexes[i]] = this.values[i];
+ double[] vals = new double[dimensionality];
+ for (int i = 0; i < indexes.length; i++) {
+ vals[this.indexes[i]] = this.values[i];
}
- return values;
+ return vals;
}
- @Override
- public <A> SparseFloatVector newFeatureVector(A array, ArrayAdapter<Float, A> adapter) {
- int dim = adapter.size(array);
- float[] values = new float[dim];
- for(int i = 0; i < dim; i++) {
- values[i] = adapter.get(array, i);
+ /**
+ * Factory class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.has SparseFloatVector
+ */
+ public static class Factory extends AbstractNumberVector.Factory<SparseFloatVector, Float> implements SparseNumberVector.Factory<SparseFloatVector, Float> {
+ @Override
+ public <A> SparseFloatVector newFeatureVector(A array, ArrayAdapter<Float, A> adapter) {
+ int dim = adapter.size(array);
+ float[] values = new float[dim];
+ for (int i = 0; i < dim; i++) {
+ values[i] = adapter.get(array, i);
+ }
+ // TODO: inefficient
+ return new SparseFloatVector(values);
}
- // TODO: inefficient
- return new SparseFloatVector(values);
- }
- @Override
- public <A> SparseFloatVector newNumberVector(A array, NumberArrayAdapter<?, A> adapter) {
- int dim = adapter.size(array);
- float[] values = new float[dim];
- for(int i = 0; i < dim; i++) {
- values[i] = adapter.getFloat(array, i);
+ @Override
+ public <A> SparseFloatVector newNumberVector(A array, NumberArrayAdapter<?, ? super A> adapter) {
+ int dim = adapter.size(array);
+ float[] values = new float[dim];
+ for (int i = 0; i < dim; i++) {
+ values[i] = adapter.getFloat(array, i);
+ }
+ // TODO: inefficient
+ return new SparseFloatVector(values);
}
- // TODO: inefficient
- return new SparseFloatVector(values);
- }
- @Override
- public SparseFloatVector newNumberVector(TIntDoubleMap dvalues, int maxdim) {
- int[] indexes = new int[dvalues.size()];
- float[] values = new float[dvalues.size()];
- // Import and sort the indexes
- TIntDoubleIterator iter = dvalues.iterator();
- for(int i = 0; iter.hasNext(); i++) {
- iter.advance();
- indexes[i] = iter.key();
+ @Override
+ public SparseFloatVector newNumberVector(TIntDoubleMap dvalues, int maxdim) {
+ int[] indexes = new int[dvalues.size()];
+ float[] values = new float[dvalues.size()];
+ // Import and sort the indexes
+ TIntDoubleIterator iter = dvalues.iterator();
+ for (int i = 0; iter.hasNext(); i++) {
+ iter.advance();
+ indexes[i] = iter.key();
+ }
+ Arrays.sort(indexes);
+ // Import the values accordingly
+ for (int i = 0; i < dvalues.size(); i++) {
+ values[i] = (float) dvalues.get(indexes[i]);
+ }
+ return new SparseFloatVector(indexes, values, maxdim);
}
- Arrays.sort(indexes);
- // Import the values accordingly
- for(int i = 0; i < dvalues.size(); i++) {
- values[i] = (float) dvalues.get(indexes[i]);
+
+ @Override
+ public ByteBufferSerializer<SparseFloatVector> getDefaultSerializer() {
+ // FIXME: add a serializer
+ return null;
+ }
+
+ @Override
+ public Class<? super SparseFloatVector> getRestrictionClass() {
+ return SparseFloatVector.class;
+ }
+
+ /**
+ * Parameterization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public static class Parameterizer extends AbstractParameterizer {
+ @Override
+ protected SparseFloatVector.Factory makeInstance() {
+ return FACTORY;
+ }
}
- return new SparseFloatVector(indexes, values, maxdim);
}
@Override
public BitSet getNotNullMask() {
BitSet b = new BitSet();
- for(int key : indexes) {
+ for (int key : indexes) {
b.set(key);
}
return b;
}
/**
- * Parameterization class
- *
- * @author Erich Schubert
- *
- * @apiviz.exclude
- */
- public static class Parameterizer extends AbstractParameterizer {
- @Override
- protected SparseFloatVector makeInstance() {
- return STATIC;
- }
- }
-
- /**
* Empty map.
*/
public static final TIntFloatMap EMPTYMAP = new TUnmodifiableIntFloatMap(new TIntFloatHashMap());
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/data/SparseNumberVector.java b/src/de/lmu/ifi/dbs/elki/data/SparseNumberVector.java
index b1f1c095..6ac1cc66 100644
--- a/src/de/lmu/ifi/dbs/elki/data/SparseNumberVector.java
+++ b/src/de/lmu/ifi/dbs/elki/data/SparseNumberVector.java
@@ -26,27 +26,38 @@ package de.lmu.ifi.dbs.elki.data;
import gnu.trove.map.TIntDoubleMap;
/**
- * Combines the SparseFeatureVector and NumberVector
+ * Combines the SparseFeatureVector and NumberVector.
*
* @author Erich Schubert
*
- * @param <V> Vector type number type
* @param <N> Number type
*/
-public interface SparseNumberVector<V extends SparseNumberVector<V, N>, N extends Number> extends NumberVector<V, N>, SparseFeatureVector<V, N> {
- /**
- * Returns a new NumberVector of N for the given values.
- *
- * @param values the values of the NumberVector
- * @param maxdim Maximum dimensionality.
- * @return a new NumberVector of N for the given values
- */
- V newNumberVector(TIntDoubleMap values, int maxdim);
-
+public interface SparseNumberVector<N extends Number> extends NumberVector<N>, SparseFeatureVector<N> {
/**
* Update the vector space dimensionality.
*
* @param maxdim New dimensionality
*/
void setDimensionality(int maxdim);
+
+ /**
+ * Factory for sparse number vectors: make from a dim-value map.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.has SparseNumberVector
+ *
+ * @param <V> Vector type number type
+ * @param <N> Number type
+ */
+ interface Factory<V extends SparseNumberVector<N>, N extends Number> extends NumberVector.Factory<V, N> {
+ /**
+ * Returns a new NumberVector of N for the given values.
+ *
+ * @param values the values of the NumberVector
+ * @param maxdim Maximum dimensionality.
+ * @return a new NumberVector of N for the given values
+ */
+ V newNumberVector(TIntDoubleMap values, int maxdim);
+ }
}
diff --git a/src/de/lmu/ifi/dbs/elki/data/Subspace.java b/src/de/lmu/ifi/dbs/elki/data/Subspace.java
index cd9074a8..8a6a2d14 100644
--- a/src/de/lmu/ifi/dbs/elki/data/Subspace.java
+++ b/src/de/lmu/ifi/dbs/elki/data/Subspace.java
@@ -32,10 +32,8 @@ import java.util.Comparator;
* @author Elke Achtert
*
* @apiviz.owns de.lmu.ifi.dbs.elki.data.Subspace.DimensionComparator
- *
- * @param <V> the type of FeatureVector this subspace contains
*/
-public class Subspace<V extends FeatureVector<V, ?>> {
+public class Subspace {
/**
* The dimensions building this subspace.
*/
@@ -96,13 +94,13 @@ public class Subspace<V extends FeatureVector<V, ?>> {
* condition is fulfilled, null otherwise.
* @see Subspace#joinLastDimensions(Subspace)
*/
- public Subspace<V> join(Subspace<V> other) {
+ public Subspace join(Subspace other) {
BitSet newDimensions = joinLastDimensions(other);
if(newDimensions == null) {
return null;
}
- return new Subspace<V>(newDimensions);
+ return new Subspace(newDimensions);
}
/**
@@ -124,7 +122,7 @@ public class Subspace<V extends FeatureVector<V, ?>> {
* @return a string representation of this subspace
*/
public String toString(String pre) {
- StringBuffer result = new StringBuffer();
+ StringBuilder result = new StringBuilder();
result.append(pre).append("Dimensions: [");
int start = dimensions.nextSetBit(0);
for(int d = start; d >= 0; d = dimensions.nextSetBit(d + 1)) {
@@ -154,7 +152,7 @@ public class Subspace<V extends FeatureVector<V, ?>> {
* @return a string representation of the dimensions of this subspace
*/
public String dimensonsToString(String sep) {
- StringBuffer result = new StringBuffer();
+ StringBuilder result = new StringBuilder();
result.append("[");
for(int dim = dimensions.nextSetBit(0); dim >= 0; dim = dimensions.nextSetBit(dim + 1)) {
if(result.length() == 1) {
@@ -178,7 +176,7 @@ public class Subspace<V extends FeatureVector<V, ?>> {
* @return true if this subspace is a subspace of the specified subspace,
* false otherwise
*/
- public boolean isSubspace(Subspace<V> subspace) {
+ public boolean isSubspace(Subspace subspace) {
if(this.dimensionality > subspace.dimensionality) {
return false;
}
@@ -202,7 +200,7 @@ public class Subspace<V extends FeatureVector<V, ?>> {
* specified subspace if the join condition is fulfilled, null
* otherwise.
*/
- protected BitSet joinLastDimensions(Subspace<V> other) {
+ protected BitSet joinLastDimensions(Subspace other) {
if(this.dimensionality != other.dimensionality) {
return null;
}
@@ -244,9 +242,8 @@ public class Subspace<V extends FeatureVector<V, ?>> {
* specified object is a Subspace and is built of the same dimensions than
* this subspace.
*
- * @see java.lang.Object#equals(java.lang.Object)
+ * {@inheritDoc}
*/
- @SuppressWarnings("unchecked")
@Override
public boolean equals(Object obj) {
if(this == obj) {
@@ -258,7 +255,7 @@ public class Subspace<V extends FeatureVector<V, ?>> {
if(getClass() != obj.getClass()) {
return false;
}
- Subspace<V> other = (Subspace<V>) obj;
+ Subspace other = (Subspace) obj;
return new DimensionComparator().compare(this, other) == 0;
}
@@ -268,7 +265,7 @@ public class Subspace<V extends FeatureVector<V, ?>> {
*
* @author Elke Achtert
*/
- public static class DimensionComparator implements Comparator<Subspace<?>> {
+ public static class DimensionComparator implements Comparator<Subspace> {
/**
* Compares the two specified subspaces for order. If the two subspaces have
* different dimensionalities a negative integer or a positive integer will
@@ -280,9 +277,10 @@ public class Subspace<V extends FeatureVector<V, ?>> {
* d1} is less than or greater than {@code d2}. Otherwise the two subspaces
* have equal dimensions and zero will be returned.
*
+ * {@inheritDoc}
*/
@Override
- public int compare(Subspace<?> s1, Subspace<?> s2) {
+ public int compare(Subspace s1, Subspace s2) {
if(s1 == s2 || s1.getDimensions() == null && s2.getDimensions() == null) {
return 0;
}
diff --git a/src/de/lmu/ifi/dbs/elki/data/VectorUtil.java b/src/de/lmu/ifi/dbs/elki/data/VectorUtil.java
index c8bf2c02..ce8102fc 100644
--- a/src/de/lmu/ifi/dbs/elki/data/VectorUtil.java
+++ b/src/de/lmu/ifi/dbs/elki/data/VectorUtil.java
@@ -23,20 +23,22 @@ package de.lmu.ifi.dbs.elki.data;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+import gnu.trove.map.hash.TIntDoubleHashMap;
+
import java.util.BitSet;
import java.util.Comparator;
import java.util.Random;
import de.lmu.ifi.dbs.elki.data.spatial.SpatialComparable;
import de.lmu.ifi.dbs.elki.database.ids.ArrayModifiableDBIDs;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.math.DoubleMinMax;
import de.lmu.ifi.dbs.elki.math.MathUtil;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.utilities.datastructures.QuickSelect;
/**
@@ -50,43 +52,52 @@ import de.lmu.ifi.dbs.elki.utilities.datastructures.QuickSelect;
*/
public final class VectorUtil {
/**
+ * Fake constructor. Do not instantiate, use static methods.
+ */
+ private VectorUtil() {
+ // Do not instantiate - utility class.
+ }
+
+ /**
* Return the range across all dimensions. Useful in particular for time
* series.
*
* @param vec Vector to process.
* @return [min, max]
*/
- public static DoubleMinMax getRangeDouble(NumberVector<?, ?> vec) {
+ public static DoubleMinMax getRangeDouble(NumberVector<?> vec) {
DoubleMinMax minmax = new DoubleMinMax();
for(int i = 0; i < vec.getDimensionality(); i++) {
- minmax.put(vec.doubleValue(i + 1));
+ minmax.put(vec.doubleValue(i));
}
return minmax;
}
/**
- * Produce a new vector based on random numbers in [0:1] of the same type and
- * dimensionality as the given vector.
- *
- * @param template existing instance of wanted dimensionality.
+ * Produce a new vector based on random numbers in [0:1].
+ *
+ * @param factory Vector factory
+ * @param dim desired dimensionality
* @param r Random generator
+ * @param <V> vector type
* @return new instance
*/
- public static <V extends NumberVector<V, ?>> V randomVector(V template, Random r) {
- return template.newNumberVector(MathUtil.randomDoubleArray(template.getDimensionality(), r));
+ public static <V extends NumberVector<?>> V randomVector(NumberVector.Factory<V, ?> factory, int dim, Random r) {
+ return factory.newNumberVector(MathUtil.randomDoubleArray(dim, r));
}
/**
- * Produce a new vector based on random numbers in [0:1] of the same type and
- * dimensionality as the given vector.
+ * Produce a new vector based on random numbers in [0:1].
*
- * @param template existing instance of wanted dimensionality.
+ * @param factory Vector factory
+ * @param dim desired dimensionality
+ * @param <V> vector type
* @return new instance
*/
- public static <V extends NumberVector<V, ?>> V randomVector(V template) {
- return randomVector(template, new Random());
+ public static <V extends NumberVector<?>> V randomVector(NumberVector.Factory<V, ?> factory, int dim) {
+ return randomVector(factory, dim, new Random());
}
/**
@@ -96,7 +107,7 @@ public final class VectorUtil {
* @param v2 Second vector
* @return angle
*/
- public static double angleSparse(SparseNumberVector<?, ?> v1, SparseNumberVector<?, ?> v2) {
+ public static double angleSparse(SparseNumberVector<?> v1, SparseNumberVector<?> v2) {
BitSet b1 = v1.getNotNullMask();
BitSet b2 = v2.getNotNullMask();
BitSet both = (BitSet) b1.clone();
@@ -134,7 +145,7 @@ public final class VectorUtil {
* @param o Origin
* @return Angle
*/
- public static double angle(NumberVector<?, ?> v1, NumberVector<?, ?> v2, Vector o) {
+ public static double angle(NumberVector<?> v1, NumberVector<?> v2, Vector o) {
// Essentially, we want to compute this:
// v1' = v1 - o, v2' = v2 - o
// v1'.transposeTimes(v2') / (v1'.euclideanLength()*v2'.euclideanLength());
@@ -143,8 +154,8 @@ public final class VectorUtil {
final int dim = v1.getDimensionality();
double s = 0, e1 = 0, e2 = 0;
for(int k = 0; k < dim; k++) {
- final double r1 = v1.doubleValue(k + 1) - oe[k];
- final double r2 = v2.doubleValue(k + 1) - oe[k];
+ final double r1 = v1.doubleValue(k) - oe[k];
+ final double r2 = v2.doubleValue(k) - oe[k];
s += r1 * r2;
e1 += r1 * r1;
e2 += r2 * r2;
@@ -160,7 +171,7 @@ public final class VectorUtil {
* @param o Origin
* @return Angle
*/
- public static double angle(NumberVector<?, ?> v1, NumberVector<?, ?> v2, NumberVector<?, ?> o) {
+ public static double angle(NumberVector<?> v1, NumberVector<?> v2, NumberVector<?> o) {
// Essentially, we want to compute this:
// v1' = v1 - o, v2' = v2 - o
// v1'.transposeTimes(v2') / (v1'.euclideanLength()*v2'.euclideanLength());
@@ -168,8 +179,8 @@ public final class VectorUtil {
final int dim = v1.getDimensionality();
double s = 0, e1 = 0, e2 = 0;
for(int k = 0; k < dim; k++) {
- final double r1 = v1.doubleValue(k + 1) - o.doubleValue(k + 1);
- final double r2 = v2.doubleValue(k + 1) - o.doubleValue(k + 1);
+ final double r1 = v1.doubleValue(k) - o.doubleValue(k);
+ final double r2 = v2.doubleValue(k) - o.doubleValue(k);
s += r1 * r2;
e1 += r1 * r1;
e2 += r2 * r2;
@@ -186,9 +197,9 @@ public final class VectorUtil {
* @param v2 second vector
* @return Angle
*/
- public static double cosAngle(NumberVector<?, ?> v1, NumberVector<?, ?> v2) {
- if(v1 instanceof SparseNumberVector<?, ?> && v2 instanceof SparseNumberVector<?, ?>) {
- return angleSparse((SparseNumberVector<?, ?>) v1, (SparseNumberVector<?, ?>) v2);
+ public static double cosAngle(NumberVector<?> v1, NumberVector<?> v2) {
+ if(v1 instanceof SparseNumberVector<?> && v2 instanceof SparseNumberVector<?>) {
+ return angleSparse((SparseNumberVector<?>) v1, (SparseNumberVector<?>) v2);
}
// Essentially, we want to compute this:
// v1.transposeTimes(v2) / (v1.euclideanLength() * v2.euclideanLength());
@@ -198,18 +209,18 @@ public final class VectorUtil {
final int dim = Math.min(d1, d2);
double s = 0, e1 = 0, e2 = 0;
for(int k = 0; k < dim; k++) {
- final double r1 = v1.doubleValue(k + 1);
- final double r2 = v2.doubleValue(k + 1);
+ final double r1 = v1.doubleValue(k);
+ final double r2 = v2.doubleValue(k);
s += r1 * r2;
e1 += r1 * r1;
e2 += r2 * r2;
}
for(int k = dim; k < d1; k++) {
- final double r1 = v1.doubleValue(k + 1);
+ final double r1 = v1.doubleValue(k);
e1 += r1 * r1;
}
for(int k = dim; k < d2; k++) {
- final double r2 = v2.doubleValue(k + 1);
+ final double r2 = v2.doubleValue(k);
e2 += r2 * r2;
}
return Math.min(Math.sqrt((s / e1) * (s / e2)), 1);
@@ -227,8 +238,8 @@ public final class VectorUtil {
* @return Angle
*/
public static double minCosAngle(SpatialComparable v1, SpatialComparable v2) {
- if(v1 instanceof NumberVector<?, ?> && v2 instanceof NumberVector<?, ?>) {
- return cosAngle((NumberVector<?, ?>) v1, (NumberVector<?, ?>) v2);
+ if(v1 instanceof NumberVector<?> && v2 instanceof NumberVector<?>) {
+ return cosAngle((NumberVector<?>) v1, (NumberVector<?>) v2);
}
// Essentially, we want to compute this:
// absmax(v1.transposeTimes(v2))/(min(v1.euclideanLength())*min(v2.euclideanLength()));
@@ -236,8 +247,8 @@ public final class VectorUtil {
final int dim = v1.getDimensionality();
double s1 = 0, s2 = 0, e1 = 0, e2 = 0;
for(int k = 0; k < dim; k++) {
- final double min1 = v1.getMin(k + 1), max1 = v1.getMax(k + 1);
- final double min2 = v2.getMin(k + 1), max2 = v2.getMax(k + 1);
+ final double min1 = v1.getMin(k), max1 = v1.getMax(k);
+ final double min2 = v2.getMin(k), max2 = v2.getMax(k);
final double p1 = min1 * min2, p2 = min1 * max2;
final double p3 = max1 * min2, p4 = max1 * max2;
s1 += Math.max(Math.max(p1, p2), Math.max(p3, p4));
@@ -268,10 +279,10 @@ public final class VectorUtil {
* @return the scalar product (inner product) of this and the given
* DoubleVector
*/
- public static double scalarProduct(NumberVector<?, ?> d1, NumberVector<?, ?> d2) {
+ public static double scalarProduct(NumberVector<?> d1, NumberVector<?> d2) {
final int dim = d1.getDimensionality();
double result = 0.0;
- for(int i = 1; i <= dim; i++) {
+ for(int i = 0; i < dim; i++) {
result += d1.doubleValue(i) * d2.doubleValue(i);
}
return result;
@@ -284,46 +295,48 @@ public final class VectorUtil {
* @param sample Sample set
* @return Medoid vector
*/
- public static Vector computeMedoid(Relation<? extends NumberVector<?, ?>> relation, DBIDs sample) {
- final int dim = DatabaseUtil.dimensionality(relation);
+ public static Vector computeMedoid(Relation<? extends NumberVector<?>> relation, DBIDs sample) {
+ final int dim = RelationUtil.dimensionality(relation);
ArrayModifiableDBIDs mids = DBIDUtil.newArray(sample);
SortDBIDsBySingleDimension s = new SortDBIDsBySingleDimension(relation);
Vector medoid = new Vector(dim);
for (int d = 0; d < dim; d++) {
- s.setDimension(d + 1);
- medoid.set(d, relation.get(QuickSelect.median(mids, s)).doubleValue(d + 1));
+ s.setDimension(d);
+ medoid.set(d, relation.get(QuickSelect.median(mids, s)).doubleValue(d));
}
return medoid;
}
/**
- * Compare number vectors by a single dimension
+ * Compare number vectors by a single dimension.
*
* @author Erich Schubert
+ *
+ * @apiviz.exclude
*/
- public static class SortDBIDsBySingleDimension implements Comparator<DBID> {
+ public static class SortDBIDsBySingleDimension implements Comparator<DBIDRef> {
/**
- * Dimension to sort with
+ * Dimension to sort with.
*/
- public int d;
+ private int d;
/**
* The relation to sort.
*/
- private Relation<? extends NumberVector<?, ?>> data;
+ private Relation<? extends NumberVector<?>> data;
/**
* Constructor.
*
* @param data Vector data source
*/
- public SortDBIDsBySingleDimension(Relation<? extends NumberVector<?, ?>> data) {
+ public SortDBIDsBySingleDimension(Relation<? extends NumberVector<?>> data) {
super();
this.data = data;
};
/**
- * Get the dimension to sort by
+ * Get the dimension to sort by.
*
* @return Dimension to sort with
*/
@@ -332,30 +345,32 @@ public final class VectorUtil {
}
/**
- * Set the dimension to sort by
+ * Set the dimension to sort by.
*
- * @param d Dimension to sort with
+ * @param d2 Dimension to sort with
*/
- public void setDimension(int d) {
- this.d = d;
+ public void setDimension(int d2) {
+ this.d = d2;
}
@Override
- public int compare(DBID id1, DBID id2) {
+ public int compare(DBIDRef id1, DBIDRef id2) {
return Double.compare(data.get(id1).doubleValue(d), data.get(id2).doubleValue(d));
}
}
/**
- * Compare number vectors by a single dimension
+ * Compare number vectors by a single dimension.
*
* @author Erich Schubert
+ *
+ * @apiviz.exclude
*/
- public static class SortVectorsBySingleDimension implements Comparator<NumberVector<?, ?>> {
+ public static class SortVectorsBySingleDimension implements Comparator<NumberVector<?>> {
/**
- * Dimension to sort with
+ * Dimension to sort with.
*/
- public int d;
+ private int d;
/**
* Constructor.
@@ -365,7 +380,7 @@ public final class VectorUtil {
};
/**
- * Get the dimension to sort by
+ * Get the dimension to sort by.
*
* @return Dimension to sort with
*/
@@ -374,17 +389,50 @@ public final class VectorUtil {
}
/**
- * Set the dimension to sort by
+ * Set the dimension to sort by.
*
- * @param d Dimension to sort with
+ * @param d2 Dimension to sort with
*/
- public void setDimension(int d) {
- this.d = d;
+ public void setDimension(int d2) {
+ this.d = d2;
}
@Override
- public int compare(NumberVector<?, ?> o1, NumberVector<?, ?> o2) {
+ public int compare(NumberVector<?> o1, NumberVector<?> o2) {
return Double.compare(o1.doubleValue(d), o2.doubleValue(d));
}
}
-} \ No newline at end of file
+
+ /**
+ * Provides a new NumberVector as a projection on the specified attributes.
+ *
+ * @param v a NumberVector to project
+ * @param selectedAttributes the attributes selected for projection
+ * @param factory Vector factory
+ * @param <V> Vector type
+ * @return a new NumberVector as a projection on the specified attributes
+ */
+ public static <V extends NumberVector<?>> V project(V v, BitSet selectedAttributes, NumberVector.Factory<V, ?> factory) {
+ if (factory instanceof SparseNumberVector.Factory) {
+ final SparseNumberVector.Factory<?, ?> sfactory = (SparseNumberVector.Factory<?, ?>) factory;
+ TIntDoubleHashMap values = new TIntDoubleHashMap(selectedAttributes.cardinality(), 1);
+ for (int d = selectedAttributes.nextSetBit(0); d >= 0; d = selectedAttributes.nextSetBit(d + 1)) {
+ if (v.doubleValue(d) != 0.0) {
+ values.put(d, v.doubleValue(d));
+ }
+ }
+ // We can't avoid this cast, because Java doesn't know that V is a SparseNumberVector:
+ @SuppressWarnings("unchecked")
+ V projectedVector = (V) sfactory.newNumberVector(values, selectedAttributes.cardinality());
+ return projectedVector;
+ } else {
+ double[] newAttributes = new double[selectedAttributes.cardinality()];
+ int i = 0;
+ for (int d = selectedAttributes.nextSetBit(0); d >= 0; d = selectedAttributes.nextSetBit(d + 1)) {
+ newAttributes[i] = v.doubleValue(d);
+ i++;
+ }
+ return factory.newNumberVector(newAttributes);
+ }
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/data/images/ComputeHSBColorHistogram.java b/src/de/lmu/ifi/dbs/elki/data/images/ComputeHSBColorHistogram.java
index e5d91aa4..e9b428f1 100644
--- a/src/de/lmu/ifi/dbs/elki/data/images/ComputeHSBColorHistogram.java
+++ b/src/de/lmu/ifi/dbs/elki/data/images/ComputeHSBColorHistogram.java
@@ -29,7 +29,8 @@ import java.util.List;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.WrongParameterValueException;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.ListGreaterEqualConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterEqualConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.ListEachConstraint;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.ListSizeConstraint;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntListParameter;
@@ -47,7 +48,7 @@ public class ComputeHSBColorHistogram extends AbstractComputeColorHistogram {
* Key: {@code -rgbhist.bpp}
* </p>
*/
- public static final OptionID BINSPERPLANE_ID = OptionID.getOrCreateOptionID("hsbhist.bpp", "Bins per plane for HSV/HSB histogram. This will result in bpp ** 3 bins.");
+ public static final OptionID BINSPERPLANE_ID = new OptionID("hsbhist.bpp", "Bins per plane for HSV/HSB histogram. This will result in bpp ** 3 bins.");
/**
* Number of bins in hue to use.
@@ -124,9 +125,9 @@ public class ComputeHSBColorHistogram extends AbstractComputeColorHistogram {
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- final IntListParameter param = new IntListParameter(BINSPERPLANE_ID, false);
- param.addConstraint(new ListSizeConstraint<Integer>(3));
- param.addConstraint(new ListGreaterEqualConstraint<Integer>(1));
+ final IntListParameter param = new IntListParameter(BINSPERPLANE_ID);
+ param.addConstraint(new ListSizeConstraint(3));
+ param.addConstraint(new ListEachConstraint<Integer>(new GreaterEqualConstraint(1)));
if(config.grab(param)) {
List<Integer> quant = param.getValue();
diff --git a/src/de/lmu/ifi/dbs/elki/data/images/ComputeNaiveHSBColorHistogram.java b/src/de/lmu/ifi/dbs/elki/data/images/ComputeNaiveHSBColorHistogram.java
index 4fb887e7..30fe21be 100644
--- a/src/de/lmu/ifi/dbs/elki/data/images/ComputeNaiveHSBColorHistogram.java
+++ b/src/de/lmu/ifi/dbs/elki/data/images/ComputeNaiveHSBColorHistogram.java
@@ -41,7 +41,7 @@ public class ComputeNaiveHSBColorHistogram extends ComputeHSBColorHistogram {
* Key: {@code -hsbhist.bpp}
* </p>
*/
- public static final OptionID BINSPERPLANE_ID = OptionID.getOrCreateOptionID("hsbhist.bpp", "Bins per plane for HSV/HSB histogram. This will result in bpp ** 3 bins.");
+ public static final OptionID BINSPERPLANE_ID = new OptionID("hsbhist.bpp", "Bins per plane for HSV/HSB histogram. This will result in bpp ** 3 bins.");
/**
* Constructor.
diff --git a/src/de/lmu/ifi/dbs/elki/data/images/ComputeNaiveRGBColorHistogram.java b/src/de/lmu/ifi/dbs/elki/data/images/ComputeNaiveRGBColorHistogram.java
index 3e6e8c64..d28ed29b 100644
--- a/src/de/lmu/ifi/dbs/elki/data/images/ComputeNaiveRGBColorHistogram.java
+++ b/src/de/lmu/ifi/dbs/elki/data/images/ComputeNaiveRGBColorHistogram.java
@@ -41,7 +41,7 @@ public class ComputeNaiveRGBColorHistogram extends AbstractComputeColorHistogram
* Key: {@code -rgbhist.bpp}
* </p>
*/
- public static final OptionID BINSPERPLANE_ID = OptionID.getOrCreateOptionID("rgbhist.bpp", "Bins per plane for RGB histogram. This will result in bpp ** 3 bins.");
+ public static final OptionID BINSPERPLANE_ID = new OptionID("rgbhist.bpp", "Bins per plane for RGB histogram. This will result in bpp ** 3 bins.");
/**
* Number of bins in each dimension to use.
diff --git a/src/de/lmu/ifi/dbs/elki/data/images/package-info.java b/src/de/lmu/ifi/dbs/elki/data/images/package-info.java
index c851a21a..4a9d19ac 100644
--- a/src/de/lmu/ifi/dbs/elki/data/images/package-info.java
+++ b/src/de/lmu/ifi/dbs/elki/data/images/package-info.java
@@ -1,7 +1,7 @@
/**
* <p>Package for processing image data (e.g. compute color histograms)</p>
*
- *
+ * @apiviz.exclude java.io.*
*/
/*
This file is part of ELKI:
@@ -25,4 +25,4 @@ GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package de.lmu.ifi.dbs.elki.data.images; \ No newline at end of file
+package de.lmu.ifi.dbs.elki.data.images;
diff --git a/src/de/lmu/ifi/dbs/elki/data/model/Bicluster.java b/src/de/lmu/ifi/dbs/elki/data/model/Bicluster.java
index 371c4d02..eb7755a7 100644
--- a/src/de/lmu/ifi/dbs/elki/data/model/Bicluster.java
+++ b/src/de/lmu/ifi/dbs/elki/data/model/Bicluster.java
@@ -44,7 +44,7 @@ import de.lmu.ifi.dbs.elki.utilities.exceptions.ExceptionMessages;
* @author Arthur Zimek
* @param <V> the type of NumberVector handled by this Result
*/
-public class Bicluster<V extends FeatureVector<?, ?>> implements TextWriteable, Model {
+public class Bicluster<V extends FeatureVector<?>> implements TextWriteable, Model {
/**
* The ids of the rows included in the bicluster.
*/
diff --git a/src/de/lmu/ifi/dbs/elki/data/model/BiclusterWithInverted.java b/src/de/lmu/ifi/dbs/elki/data/model/BiclusterWithInverted.java
index 10f3bed2..1f582fdd 100644
--- a/src/de/lmu/ifi/dbs/elki/data/model/BiclusterWithInverted.java
+++ b/src/de/lmu/ifi/dbs/elki/data/model/BiclusterWithInverted.java
@@ -40,7 +40,7 @@ import de.lmu.ifi.dbs.elki.result.textwriter.TextWriterStream;
*
* @param <V> Vector type
*/
-public class BiclusterWithInverted<V extends FeatureVector<V, ?>> extends Bicluster<V> {
+public class BiclusterWithInverted<V extends FeatureVector<?>> extends Bicluster<V> {
/**
* The ids of inverted rows.
*/
diff --git a/src/de/lmu/ifi/dbs/elki/data/model/ClusterModel.java b/src/de/lmu/ifi/dbs/elki/data/model/ClusterModel.java
index 260c9a66..5bd3123c 100644
--- a/src/de/lmu/ifi/dbs/elki/data/model/ClusterModel.java
+++ b/src/de/lmu/ifi/dbs/elki/data/model/ClusterModel.java
@@ -37,7 +37,7 @@ public final class ClusterModel extends BaseModel {
* Static cluster model that can be shared for all clusters (since the object
* doesn't include meta information.
*/
- public final static ClusterModel CLUSTER = new ClusterModel();
+ public static final ClusterModel CLUSTER = new ClusterModel();
@Override
public String toString() {
diff --git a/src/de/lmu/ifi/dbs/elki/data/model/CorrelationAnalysisSolution.java b/src/de/lmu/ifi/dbs/elki/data/model/CorrelationAnalysisSolution.java
index 56fe8f9d..4240cb83 100644
--- a/src/de/lmu/ifi/dbs/elki/data/model/CorrelationAnalysisSolution.java
+++ b/src/de/lmu/ifi/dbs/elki/data/model/CorrelationAnalysisSolution.java
@@ -50,7 +50,7 @@ import de.lmu.ifi.dbs.elki.result.textwriter.TextWriterStream;
*
* @param <V> the type of NumberVector handled by this Result
*/
-public class CorrelationAnalysisSolution<V extends NumberVector<V, ?>> implements TextWriteable, Result, Model {
+public class CorrelationAnalysisSolution<V extends NumberVector<?>> implements TextWriteable, Result, Model {
/**
* Stores the solution equations.
*/
diff --git a/src/de/lmu/ifi/dbs/elki/data/model/CorrelationModel.java b/src/de/lmu/ifi/dbs/elki/data/model/CorrelationModel.java
index fa4d33fa..1a111985 100644
--- a/src/de/lmu/ifi/dbs/elki/data/model/CorrelationModel.java
+++ b/src/de/lmu/ifi/dbs/elki/data/model/CorrelationModel.java
@@ -38,7 +38,7 @@ import de.lmu.ifi.dbs.elki.utilities.FormatUtil;
*
* @param <V> Vector type
*/
-public class CorrelationModel<V extends FeatureVector<V, ?>> extends BaseModel implements TextWriteable {
+public class CorrelationModel<V extends FeatureVector<?>> extends BaseModel implements TextWriteable {
/**
* The computed PCA result of this cluster.
*/
diff --git a/src/de/lmu/ifi/dbs/elki/data/model/DendrogramModel.java b/src/de/lmu/ifi/dbs/elki/data/model/DendrogramModel.java
index e4bcba52..0ea1fe2b 100644
--- a/src/de/lmu/ifi/dbs/elki/data/model/DendrogramModel.java
+++ b/src/de/lmu/ifi/dbs/elki/data/model/DendrogramModel.java
@@ -33,9 +33,16 @@ import de.lmu.ifi.dbs.elki.result.textwriter.TextWriterStream;
*/
// TODO: comments
public class DendrogramModel<D extends Distance<D>> extends BaseModel {
-
+ /**
+ * Distance to child cluster
+ */
private D distance;
+ /**
+ * Constructor.
+ *
+ * @param distance Distance to child cluster.
+ */
public DendrogramModel(D distance) {
super();
this.distance = distance;
diff --git a/src/de/lmu/ifi/dbs/elki/data/model/EMModel.java b/src/de/lmu/ifi/dbs/elki/data/model/EMModel.java
index 3ba6a981..3856b9b1 100644
--- a/src/de/lmu/ifi/dbs/elki/data/model/EMModel.java
+++ b/src/de/lmu/ifi/dbs/elki/data/model/EMModel.java
@@ -35,7 +35,7 @@ import de.lmu.ifi.dbs.elki.result.textwriter.TextWriterStream;
*
* @param <V> Vector type
*/
-public class EMModel<V extends FeatureVector<V, ?>> extends MeanModel<V> {
+public class EMModel<V extends FeatureVector<?>> extends MeanModel<V> {
/**
* Cluster covariance matrix
*/
diff --git a/src/de/lmu/ifi/dbs/elki/data/model/KMeansModel.java b/src/de/lmu/ifi/dbs/elki/data/model/KMeansModel.java
new file mode 100644
index 00000000..da6e81f8
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/data/model/KMeansModel.java
@@ -0,0 +1,44 @@
+package de.lmu.ifi.dbs.elki.data.model;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+import de.lmu.ifi.dbs.elki.data.NumberVector;
+
+/**
+ * Trivial subclass of the {@link MeanModel} that indicates the clustering to be
+ * produced by k-means (so the Voronoi cell visualization is sensible).
+ *
+ * @author Erich Schubert
+ *
+ * @param <V> Vector type.
+ */
+public class KMeansModel<V extends NumberVector<?>> extends MeanModel<V> {
+ /**
+ * Constructor with mean.
+ *
+ * @param mean Mean vector.
+ */
+ public KMeansModel(V mean) {
+ super(mean);
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/data/model/MeanModel.java b/src/de/lmu/ifi/dbs/elki/data/model/MeanModel.java
index 761837be..15c3d571 100644
--- a/src/de/lmu/ifi/dbs/elki/data/model/MeanModel.java
+++ b/src/de/lmu/ifi/dbs/elki/data/model/MeanModel.java
@@ -34,7 +34,7 @@ import de.lmu.ifi.dbs.elki.result.textwriter.TextWriterStream;
*
* @param <V> Vector type
*/
-public class MeanModel<V extends FeatureVector<V, ?>> extends BaseModel implements TextWriteable{
+public class MeanModel<V extends FeatureVector<?>> extends BaseModel implements TextWriteable{
/**
* Cluster mean
*/
diff --git a/src/de/lmu/ifi/dbs/elki/data/model/Model.java b/src/de/lmu/ifi/dbs/elki/data/model/Model.java
index 4ffac679..627bc084 100644
--- a/src/de/lmu/ifi/dbs/elki/data/model/Model.java
+++ b/src/de/lmu/ifi/dbs/elki/data/model/Model.java
@@ -23,12 +23,11 @@ package de.lmu.ifi.dbs.elki.data.model;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-
/**
* Base interface for Model classes.
*
* @author Erich Schubert
- *
+ *
* @apiviz.landmark
*/
public interface Model {
diff --git a/src/de/lmu/ifi/dbs/elki/data/model/SubspaceModel.java b/src/de/lmu/ifi/dbs/elki/data/model/SubspaceModel.java
index f4db1424..65607d8a 100644
--- a/src/de/lmu/ifi/dbs/elki/data/model/SubspaceModel.java
+++ b/src/de/lmu/ifi/dbs/elki/data/model/SubspaceModel.java
@@ -40,11 +40,11 @@ import de.lmu.ifi.dbs.elki.result.textwriter.TextWriterStream;
*
* @param <V> the type of FeatureVector the subspace contains
*/
-public class SubspaceModel<V extends FeatureVector<V, ?>> extends MeanModel<V> implements TextWriteable {
+public class SubspaceModel<V extends FeatureVector<?>> extends MeanModel<V> implements TextWriteable {
/**
* The subspace of the cluster.
*/
- private final Subspace<V> subspace;
+ private final Subspace subspace;
/**
* Creates a new SubspaceModel for the specified subspace with the given
@@ -53,7 +53,7 @@ public class SubspaceModel<V extends FeatureVector<V, ?>> extends MeanModel<V> i
* @param subspace the subspace of the cluster
* @param mean the cluster mean
*/
- public SubspaceModel(Subspace<V> subspace, V mean) {
+ public SubspaceModel(Subspace subspace, V mean) {
super(mean);
this.subspace = subspace;
}
@@ -63,7 +63,7 @@ public class SubspaceModel<V extends FeatureVector<V, ?>> extends MeanModel<V> i
*
* @return the subspace
*/
- public Subspace<V> getSubspace() {
+ public Subspace getSubspace() {
return subspace;
}
@@ -77,9 +77,6 @@ public class SubspaceModel<V extends FeatureVector<V, ?>> extends MeanModel<V> i
return subspace.getDimensions();
}
- /**
- * Implementation of {@link TextWriteable} interface.
- */
@Override
public void writeToText(TextWriterStream out, String label) {
super.writeToText(out, label);
diff --git a/src/de/lmu/ifi/dbs/elki/data/package-info.java b/src/de/lmu/ifi/dbs/elki/data/package-info.java
index 51c7c716..8d1eb82f 100644
--- a/src/de/lmu/ifi/dbs/elki/data/package-info.java
+++ b/src/de/lmu/ifi/dbs/elki/data/package-info.java
@@ -4,6 +4,12 @@
* @apiviz.exclude de.lmu.ifi.dbs.elki.algorithm.*
* @apiviz.exclude de.lmu.ifi.dbs.elki.result.textwriter.*
* @apiviz.exclude de.lmu.ifi.dbs.elki.visualization.*
+ * @apiviz.exclude de.lmu.ifi.dbs.elki.datasource.parser.*
+ * @apiviz.exclude de.lmu.ifi.dbs.elki.datasource.filter.*
+ * @apiviz.exclude de.lmu.ifi.dbs.elki.math.linearalgebra.*
+ * @apiviz.exclude java.io.*
+ * @apiviz.exclude java.util.*
+ * @apiviz.exclude java.lang.*
*/
/*
This file is part of ELKI:
@@ -27,4 +33,4 @@ GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package de.lmu.ifi.dbs.elki.data; \ No newline at end of file
+package de.lmu.ifi.dbs.elki.data;
diff --git a/src/de/lmu/ifi/dbs/elki/data/projection/FeatureSelection.java b/src/de/lmu/ifi/dbs/elki/data/projection/FeatureSelection.java
index 04878aea..bcb64800 100644
--- a/src/de/lmu/ifi/dbs/elki/data/projection/FeatureSelection.java
+++ b/src/de/lmu/ifi/dbs/elki/data/projection/FeatureSelection.java
@@ -23,9 +23,11 @@ package de.lmu.ifi.dbs.elki.data.projection;
*/
import de.lmu.ifi.dbs.elki.data.FeatureVector;
+import de.lmu.ifi.dbs.elki.data.FeatureVector.Factory;
import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.data.type.SimpleTypeInformation;
import de.lmu.ifi.dbs.elki.data.type.TypeInformation;
+import de.lmu.ifi.dbs.elki.data.type.VectorFieldTypeInformation;
import de.lmu.ifi.dbs.elki.data.type.VectorTypeInformation;
import de.lmu.ifi.dbs.elki.utilities.datastructures.arraylike.ArrayAdapter;
import de.lmu.ifi.dbs.elki.utilities.datastructures.arraylike.ArrayLikeUtil;
@@ -39,24 +41,24 @@ import de.lmu.ifi.dbs.elki.utilities.datastructures.arraylike.SubsetArrayAdapter
* @param <V> Vector type
* @param <F> Feature type
*/
-public class FeatureSelection<V extends FeatureVector<V, F>, F> implements Projection<V, V> {
+public class FeatureSelection<V extends FeatureVector<F>, F> implements Projection<V, V> {
/**
- * Minimum dimensionality required for projection
+ * Minimum dimensionality required for projection.
*/
private int mindim;
/**
- * Object factory
+ * Object factory.
*/
- private V factory;
+ private FeatureVector.Factory<V, F> factory;
/**
- * Output dimensionality
+ * Output dimensionality.
*/
private int dimensionality;
/**
- * Array adapter
+ * Array adapter.
*/
protected ArrayAdapter<F, V> adapter;
@@ -66,43 +68,44 @@ public class FeatureSelection<V extends FeatureVector<V, F>, F> implements Proje
* @param dims Dimensions
* @param factory Object factory
*/
- public FeatureSelection(int[] dims, V factory) {
+ public FeatureSelection(int[] dims, FeatureVector.Factory<V, F> factory) {
this.adapter = new SubsetArrayAdapter<F, V>(getAdapter(factory), dims);
this.factory = factory;
this.dimensionality = dims.length;
- int mindim = 0;
+ int mind = 0;
for(int dim : dims) {
- mindim = Math.max(mindim, dim + 1);
+ mind = Math.max(mind, dim + 1);
}
- this.mindim = mindim;
+ this.mindim = mind;
}
@Override
public V project(V data) {
- return data.newFeatureVector(data, adapter);
+ return factory.newFeatureVector(data, adapter);
}
/**
* Choose the best adapter for this.
*
* @param factory Object factory, for type inference
+ * @param <V> Vector type
+ * @param <F> Value type
* @return Adapter
*/
@SuppressWarnings("unchecked")
- private static <V extends FeatureVector<V, F>, F> ArrayAdapter<F, ? super V> getAdapter(V factory) {
- if(factory instanceof NumberVector) {
- ArrayAdapter<?, ?> ret = ArrayLikeUtil.numberVectorAdapter((NumberVector<?, ?>) factory);
- return (ArrayAdapter<F, ? super V>) ret;
+ private static <V extends FeatureVector<F>, F> ArrayAdapter<F, ? super V> getAdapter(Factory<V, F> factory) {
+ if(factory instanceof NumberVector.Factory) {
+ return (ArrayAdapter<F, ? super V>) ArrayLikeUtil.NUMBERVECTORADAPTER;
}
- return ArrayLikeUtil.featureVectorAdapter(factory);
+ return (ArrayAdapter<F, ? super V>) ArrayLikeUtil.FEATUREVECTORADAPTER;
}
@Override
public SimpleTypeInformation<V> getOutputDataTypeInformation() {
@SuppressWarnings("unchecked")
final Class<V> cls = (Class<V>) factory.getClass();
- return new VectorTypeInformation<V>(cls, dimensionality, dimensionality);
+ return new VectorFieldTypeInformation<V>(cls, dimensionality);
}
@Override
diff --git a/src/de/lmu/ifi/dbs/elki/data/projection/NumericalFeatureSelection.java b/src/de/lmu/ifi/dbs/elki/data/projection/NumericalFeatureSelection.java
index bd41e1cf..9154839f 100644
--- a/src/de/lmu/ifi/dbs/elki/data/projection/NumericalFeatureSelection.java
+++ b/src/de/lmu/ifi/dbs/elki/data/projection/NumericalFeatureSelection.java
@@ -28,6 +28,7 @@ import java.util.BitSet;
import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.data.type.SimpleTypeInformation;
import de.lmu.ifi.dbs.elki.data.type.TypeInformation;
+import de.lmu.ifi.dbs.elki.data.type.VectorFieldTypeInformation;
import de.lmu.ifi.dbs.elki.data.type.VectorTypeInformation;
/**
@@ -37,24 +38,24 @@ import de.lmu.ifi.dbs.elki.data.type.VectorTypeInformation;
*
* @param <V> Vector type
*/
-public class NumericalFeatureSelection<V extends NumberVector<V, ?>> implements Projection<V, V> {
+public class NumericalFeatureSelection<V extends NumberVector<?>> implements Projection<V, V> {
/**
- * Minimum dimensionality required for projection
+ * Minimum dimensionality required for projection.
*/
private int mindim;
/**
- * Object factory
+ * Object factory.
*/
- private V factory;
+ private NumberVector.Factory<V, ?> factory;
/**
- * Output dimensionality
+ * Output dimensionality.
*/
private int dimensionality;
/**
- * Subspace
+ * Subspace.
*/
private BitSet bits;
@@ -64,24 +65,24 @@ public class NumericalFeatureSelection<V extends NumberVector<V, ?>> implements
* @param bits Dimensions
* @param factory Object factory
*/
- public NumericalFeatureSelection(BitSet bits, V factory) {
+ public NumericalFeatureSelection(BitSet bits, NumberVector.Factory<V, ?> factory) {
super();
this.bits = bits;
this.factory = factory;
this.dimensionality = bits.cardinality();
- int mindim = 0;
+ int mind = 0;
for(int i = bits.nextSetBit(0); i >= 0; i = bits.nextSetBit(i + 1)) {
- mindim = Math.max(mindim, i + 1);
+ mind = Math.max(mind, i + 1);
}
- this.mindim = mindim;
+ this.mindim = mind;
}
@Override
public V project(V data) {
double[] dbl = new double[dimensionality];
for(int i = bits.nextSetBit(0), j = 0; i >= 0; i = bits.nextSetBit(i + 1), j++) {
- dbl[j] = data.doubleValue(i + 1);
+ dbl[j] = data.doubleValue(i);
}
return factory.newNumberVector(dbl);
}
@@ -89,14 +90,14 @@ public class NumericalFeatureSelection<V extends NumberVector<V, ?>> implements
@Override
public SimpleTypeInformation<V> getOutputDataTypeInformation() {
@SuppressWarnings("unchecked")
- final Class<V> cls = (Class<V>) factory.getClass();
- return new VectorTypeInformation<V>(cls, dimensionality, dimensionality);
+ final Class<V> cls = (Class<V>) factory.getRestrictionClass();
+ return new VectorFieldTypeInformation<V>(cls, dimensionality);
}
@Override
public TypeInformation getInputDataTypeInformation() {
@SuppressWarnings("unchecked")
- final Class<V> cls = (Class<V>) factory.getClass();
+ final Class<V> cls = (Class<V>) factory.getRestrictionClass();
return new VectorTypeInformation<V>(cls, mindim, Integer.MAX_VALUE);
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/data/spatial/Polygon.java b/src/de/lmu/ifi/dbs/elki/data/spatial/Polygon.java
index f485c052..3bb771c1 100644
--- a/src/de/lmu/ifi/dbs/elki/data/spatial/Polygon.java
+++ b/src/de/lmu/ifi/dbs/elki/data/spatial/Polygon.java
@@ -118,7 +118,7 @@ public class Polygon implements Iterable<Vector>, SpatialComparable {
*
* @param buf Buffer to append to
*/
- public void appendToBuffer(StringBuffer buf) {
+ public void appendToBuffer(StringBuilder buf) {
Iterator<Vector> iter = points.iterator();
while(iter.hasNext()) {
double[] data = iter.next().getArrayRef();
@@ -136,7 +136,7 @@ public class Polygon implements Iterable<Vector>, SpatialComparable {
@Override
public String toString() {
- StringBuffer buf = new StringBuffer();
+ StringBuilder buf = new StringBuilder();
appendToBuffer(buf);
return buf.toString();
}
@@ -167,12 +167,12 @@ public class Polygon implements Iterable<Vector>, SpatialComparable {
@Override
public double getMin(int dimension) {
- return min[dimension - 1];
+ return min[dimension];
}
@Override
public double getMax(int dimension) {
- return max[dimension - 1];
+ return max[dimension];
}
/**
diff --git a/src/de/lmu/ifi/dbs/elki/data/spatial/PolygonsObject.java b/src/de/lmu/ifi/dbs/elki/data/spatial/PolygonsObject.java
index 17bed0e1..f329c948 100644
--- a/src/de/lmu/ifi/dbs/elki/data/spatial/PolygonsObject.java
+++ b/src/de/lmu/ifi/dbs/elki/data/spatial/PolygonsObject.java
@@ -69,7 +69,7 @@ public class PolygonsObject implements SpatialComparable {
@Override
public String toString() {
- StringBuffer buf = new StringBuffer();
+ StringBuilder buf = new StringBuilder();
appendToBuffer(buf);
return buf.toString();
}
@@ -79,7 +79,7 @@ public class PolygonsObject implements SpatialComparable {
*
* @param buf Buffer to append to
*/
- public void appendToBuffer(StringBuffer buf) {
+ public void appendToBuffer(StringBuilder buf) {
Iterator<Polygon> iter = polygons.iterator();
while(iter.hasNext()) {
Polygon poly = iter.next();
diff --git a/src/de/lmu/ifi/dbs/elki/data/spatial/SpatialComparable.java b/src/de/lmu/ifi/dbs/elki/data/spatial/SpatialComparable.java
index eb9edf4e..c81ce10e 100644
--- a/src/de/lmu/ifi/dbs/elki/data/spatial/SpatialComparable.java
+++ b/src/de/lmu/ifi/dbs/elki/data/spatial/SpatialComparable.java
@@ -42,7 +42,7 @@ public interface SpatialComparable {
* Returns the minimum coordinate at the specified dimension.
*
* @param dimension the dimension for which the coordinate should be returned,
- * where 1 &le; dimension &le; <code>getDimensionality()</code>
+ * where 0 &le; dimension &lt; <code>getDimensionality()</code>
* @return the minimum coordinate at the specified dimension
*/
double getMin(int dimension);
@@ -51,7 +51,7 @@ public interface SpatialComparable {
* Returns the maximum coordinate at the specified dimension.
*
* @param dimension the dimension for which the coordinate should be returned,
- * where 1 &le; dimension &le; <code>getDimensionality()</code>
+ * where 0 &le; dimension &lt; <code>getDimensionality()</code>
* @return the maximum coordinate at the specified dimension
*/
double getMax(int dimension);
diff --git a/src/de/lmu/ifi/dbs/elki/data/spatial/SpatialUtil.java b/src/de/lmu/ifi/dbs/elki/data/spatial/SpatialUtil.java
index 731c021a..607acd1c 100644
--- a/src/de/lmu/ifi/dbs/elki/data/spatial/SpatialUtil.java
+++ b/src/de/lmu/ifi/dbs/elki/data/spatial/SpatialUtil.java
@@ -42,15 +42,23 @@ import de.lmu.ifi.dbs.elki.utilities.datastructures.arraylike.ArrayAdapter;
// numbers start at 1 or 0 depending on the context!
public final class SpatialUtil {
/**
+ * Fake constructor: do not instantiate.
+ */
+ private SpatialUtil() {
+ // Do not instantiate.
+ }
+
+ /**
* Returns a clone of the minimum hyper point.
*
+ * @param box spatial object
* @return the minimum hyper point
*/
public static double[] getMin(SpatialComparable box) {
final int dim = box.getDimensionality();
double[] min = new double[dim];
for(int i = 0; i < dim; i++) {
- min[i] = box.getMin(i + 1);
+ min[i] = box.getMin(i);
}
return min;
}
@@ -58,13 +66,14 @@ public final class SpatialUtil {
/**
* Returns a clone of the maximum hyper point.
*
+ * @param box spatial object
* @return the maximum hyper point
*/
public static double[] getMax(SpatialComparable box) {
final int dim = box.getDimensionality();
double[] max = new double[dim];
for(int i = 0; i < dim; i++) {
- max[i] = box.getMax(i + 1);
+ max[i] = box.getMax(i);
}
return max;
}
@@ -82,7 +91,7 @@ public final class SpatialUtil {
throw new IllegalArgumentException("The spatial objects do not have the same dimensionality: " + box1.getDimensionality() + " " + box2.getDimensionality());
}
boolean intersect = true;
- for(int i = 1; i <= dim; i++) {
+ for(int i = 0; i < dim; i++) {
if(box1.getMin(i) > box2.getMax(i) || box1.getMax(i) < box2.getMin(i)) {
intersect = false;
break;
@@ -107,7 +116,7 @@ public final class SpatialUtil {
}
boolean contains = true;
- for(int i = 1; i <= dim; i++) {
+ for(int i = 0; i < dim; i++) {
if(box1.getMin(i) > box2.getMin(i) || box1.getMax(i) < box2.getMax(i)) {
contains = false;
break;
@@ -120,6 +129,7 @@ public final class SpatialUtil {
* Returns true if this SpatialComparable contains the given point, false
* otherwise.
*
+ * @param box spatial object
* @param point the point to be tested for containment
* @return true if this SpatialComparable contains the given point, false
* otherwise
@@ -132,7 +142,7 @@ public final class SpatialUtil {
boolean contains = true;
for(int i = 0; i < dim; i++) {
- if(box.getMin(i + 1) > point[i] || box.getMax(i + 1) < point[i]) {
+ if(box.getMin(i) > point[i] || box.getMax(i) < point[i]) {
contains = false;
break;
}
@@ -141,14 +151,15 @@ public final class SpatialUtil {
}
/**
- * Computes the volume of this SpatialComparable
+ * Computes the volume of this SpatialComparable.
*
+ * @param box Spatial object
* @return the volume of this SpatialComparable
*/
public static double volume(SpatialComparable box) {
double vol = 1;
final int dim = box.getDimensionality();
- for(int i = 1; i <= dim; i++) {
+ for(int i = 0; i < dim; i++) {
double delta = box.getMax(i) - box.getMin(i);
if(delta == 0.0) {
return 0.0;
@@ -159,7 +170,7 @@ public final class SpatialUtil {
}
/**
- * Compute the volume (area) of the union of two MBRs
+ * Compute the volume (area) of the union of two MBRs.
*
* @param r1 First object
* @param r2 Second object
@@ -170,7 +181,7 @@ public final class SpatialUtil {
final int dim2 = r2.getDimensionality();
assert (!LoggingConfiguration.DEBUG || dim1 == dim2) : "Computing union with different dimensionality: " + dim1 + " vs. " + dim2;
double volume = 1.0;
- for(int i = 1; i <= dim1; i++) {
+ for(int i = 0; i < dim1; i++) {
final double min = Math.min(r1.getMin(i), r2.getMin(i));
final double max = Math.max(r1.getMax(i), r2.getMax(i));
volume *= (max - min);
@@ -179,15 +190,16 @@ public final class SpatialUtil {
}
/**
- * Computes the volume of this SpatialComparable
+ * Computes the volume of this SpatialComparable.
*
+ * @param box Box
* @param scale Scaling factor
* @return the volume of this SpatialComparable
*/
public static double volumeScaled(SpatialComparable box, double scale) {
double vol = 1;
final int dim = box.getDimensionality();
- for(int i = 1; i <= dim; i++) {
+ for(int i = 0; i < dim; i++) {
double delta = box.getMax(i) - box.getMin(i);
if(delta == 0.0) {
return 0.0;
@@ -198,7 +210,7 @@ public final class SpatialUtil {
}
/**
- * Compute the volume (area) of the union of two MBRs
+ * Compute the volume (area) of the union of two MBRs.
*
* @param r1 First object
* @param r2 Second object
@@ -210,7 +222,7 @@ public final class SpatialUtil {
final int dim2 = r2.getDimensionality();
assert (!LoggingConfiguration.DEBUG || dim1 == dim2) : "Computing union with different dimensionality: " + dim1 + " vs. " + dim2;
double volume = 1.0;
- for(int i = 1; i <= dim1; i++) {
+ for(int i = 0; i < dim1; i++) {
final double min = Math.min(r1.getMin(i), r2.getMin(i));
final double max = Math.max(r1.getMax(i), r2.getMax(i));
volume *= (max - min) * scale;
@@ -231,7 +243,7 @@ public final class SpatialUtil {
assert (!LoggingConfiguration.DEBUG || dim1 == dim2) : "Computing union with different dimensionality: " + dim1 + " vs. " + dim2;
double v1 = 1.0;
double v2 = 1.0;
- for(int i = 1; i <= dim1; i++) {
+ for(int i = 0; i < dim1; i++) {
final double emin = exist.getMin(i);
final double emax = exist.getMax(i);
final double amin = addit.getMin(i);
@@ -259,7 +271,7 @@ public final class SpatialUtil {
assert (!LoggingConfiguration.DEBUG || dim1 == dim2) : "Computing union with different dimensionality: " + dim1 + " vs. " + dim2;
double v1 = 1.0;
double v2 = 1.0;
- for(int i = 1; i <= dim1; i++) {
+ for(int i = 0; i < dim1; i++) {
final double emin = exist.getMin(i);
final double emax = exist.getMax(i);
final double amin = addit.getMin(i);
@@ -276,12 +288,13 @@ public final class SpatialUtil {
/**
* Computes the perimeter of this SpatialComparable.
*
+ * @param box spatial object
* @return the perimeter of this SpatialComparable
*/
public static double perimeter(SpatialComparable box) {
final int dim = box.getDimensionality();
double perimeter = 0;
- for(int i = 1; i <= dim; i++) {
+ for(int i = 0; i < dim; i++) {
perimeter += box.getMax(i) - box.getMin(i);
}
return perimeter;
@@ -306,7 +319,7 @@ public final class SpatialUtil {
// the overlap volume
double overlap = 1.0;
- for(int i = 1; i <= dim; i++) {
+ for(int i = 0; i < dim; i++) {
// The maximal value of that overlap box in the current
// dimension is the minimum of the max values.
omax = Math.min(box1.getMax(i), box2.getMax(i));
@@ -344,7 +357,7 @@ public final class SpatialUtil {
double vol1 = 1.0;
double vol2 = 1.0;
- for(int i = 1; i <= dim; i++) {
+ for(int i = 0; i < dim; i++) {
final double box1min = box1.getMin(i);
final double box1max = box1.getMax(i);
final double box2min = box2.getMin(i);
@@ -383,9 +396,9 @@ public final class SpatialUtil {
double[] min = new double[dim];
double[] max = new double[dim];
- for(int i = 1; i <= dim; i++) {
- min[i - 1] = Math.min(box1.getMin(i), box2.getMin(i));
- max[i - 1] = Math.max(box1.getMax(i), box2.getMax(i));
+ for(int i = 0; i < dim; i++) {
+ min[i] = Math.min(box1.getMin(i), box2.getMin(i));
+ max[i] = Math.max(box1.getMax(i), box2.getMax(i));
}
return new ModifiableHyperBoundingBox(min, max);
}
@@ -414,27 +427,33 @@ public final class SpatialUtil {
/**
* Compute the union of a number of objects as a flat MBR (low-level, for
- * index structures)
+ * index structures).
*
* @param data Object
* @param getter Array adapter
+ * @param <E> object type
+ * @param <A> data value type
* @return Flat MBR
*/
public static <E extends SpatialComparable, A> double[] unionFlatMBR(A data, ArrayAdapter<E, ? super A> getter) {
final int num = getter.size(data);
assert (num > 0) : "Cannot compute MBR of empty set.";
- final E first = getter.get(data, 0);
- final int dim = first.getDimensionality();
- double[] mbr = new double[2 * dim];
- for(int d = 0; d < dim; d++) {
- mbr[d] = first.getMin(d + 1);
- mbr[dim + d] = first.getMax(d + 1);
- }
+ final int dim;
+ final double[] mbr;
+ { // First entry
+ final E first = getter.get(data, 0);
+ dim = first.getDimensionality();
+ mbr = new double[2 * dim];
+ for(int d = 0; d < dim; d++) {
+ mbr[d] = first.getMin(d);
+ mbr[dim + d] = first.getMax(d);
+ }
+ } // Remaining entries
for(int i = 1; i < num; i++) {
E next = getter.get(data, i);
for(int d = 0; d < dim; d++) {
- mbr[d] = Math.min(mbr[d], next.getMin(d + 1));
- mbr[dim + d] = Math.max(mbr[dim + d], next.getMax(d + 1));
+ mbr[d] = Math.min(mbr[d], next.getMin(d));
+ mbr[dim + d] = Math.max(mbr[dim + d], next.getMax(d));
}
}
return mbr;
@@ -449,24 +468,8 @@ public final class SpatialUtil {
public static double[] centroid(SpatialComparable obj) {
final int dim = obj.getDimensionality();
double[] centroid = new double[dim];
- for(int d = 1; d <= dim; d++) {
- centroid[d - 1] = (obj.getMax(d) + obj.getMin(d)) / 2.0;
- }
- return centroid;
- }
-
- /**
- * Returns the centroid of the specified values of this SpatialComparable.
- *
- * @param obj Spatial object to process
- * @param start the start dimension to be considered
- * @param end the end dimension to be considered
- * @return the centroid of the specified values of this SpatialComparable
- */
- public static double[] centroid(SpatialComparable obj, int start, int end) {
- double[] centroid = new double[end - start + 1];
- for(int d = start - 1; d < end; d++) {
- centroid[d - start + 1] = (obj.getMax(d + 1) + obj.getMin(d + 1)) / 2.0;
+ for(int d = 0; d < dim; d++) {
+ centroid[d] = (obj.getMax(d) + obj.getMin(d)) / 2.0;
}
return centroid;
}
@@ -482,7 +485,7 @@ public final class SpatialUtil {
if(box1.getDimensionality() != box2.getDimensionality()) {
return false;
}
- for(int i = 1; i <= box1.getDimensionality(); i++) {
+ for(int i = 0; i < box1.getDimensionality(); i++) {
if(box1.getMin(i) != box2.getMin(i)) {
return false;
}
diff --git a/src/de/lmu/ifi/dbs/elki/data/synthetic/bymodel/GeneratorMain.java b/src/de/lmu/ifi/dbs/elki/data/synthetic/bymodel/GeneratorMain.java
index 4d93e1ea..603507c1 100644
--- a/src/de/lmu/ifi/dbs/elki/data/synthetic/bymodel/GeneratorMain.java
+++ b/src/de/lmu/ifi/dbs/elki/data/synthetic/bymodel/GeneratorMain.java
@@ -68,12 +68,12 @@ import de.lmu.ifi.dbs.elki.utilities.exceptions.UnableToComplyException;
*/
public class GeneratorMain {
/**
- * List of clusters to generate
+ * List of clusters to generate.
*/
protected LinkedList<GeneratorInterface> generators = new LinkedList<GeneratorInterface>();
/**
- * Controls whether points are tested against the model during generation
+ * Controls whether points are tested against the model during generation.
*/
protected boolean testAgainstModel = true;
@@ -89,6 +89,7 @@ public class GeneratorMain {
/**
* Main loop to generate data set.
*
+ * @return Generated data set
* @throws UnableToComplyException when model not satisfiable or no clusters
* specified.
*/
@@ -106,11 +107,9 @@ public class GeneratorMain {
}
}
}
- // Vector factory. TODO: make configurable
- final DoubleVector factory = new DoubleVector(new double[dim]);
// Prepare result bundle
MultipleObjectsBundle bundle = new MultipleObjectsBundle();
- VectorFieldTypeInformation<DoubleVector> type = new VectorFieldTypeInformation<DoubleVector>(DoubleVector.class, dim, factory);
+ VectorFieldTypeInformation<DoubleVector> type = new VectorFieldTypeInformation<DoubleVector>(DoubleVector.FACTORY, dim);
bundle.appendColumn(type, new ArrayList<Object>());
bundle.appendColumn(TypeUtil.CLASSLABEL, new ArrayList<Object>());
bundle.appendColumn(TypeUtil.MODEL, new ArrayList<Model>());
@@ -168,7 +167,7 @@ public class GeneratorMain {
}
/**
- * Return value of the {@link #testAgainstModel} flag
+ * Return value of the {@link #testAgainstModel} flag.
*
* @return value of testAgainstModel
*/
@@ -177,7 +176,7 @@ public class GeneratorMain {
}
/**
- * Set the value of the {@link #testAgainstModel} flag
+ * Set the value of the {@link #testAgainstModel} flag.
*
* @param testAgainstModel New value
*/
diff --git a/src/de/lmu/ifi/dbs/elki/data/type/AlternativeTypeInformation.java b/src/de/lmu/ifi/dbs/elki/data/type/AlternativeTypeInformation.java
index 89fc760a..f4eba35e 100644
--- a/src/de/lmu/ifi/dbs/elki/data/type/AlternativeTypeInformation.java
+++ b/src/de/lmu/ifi/dbs/elki/data/type/AlternativeTypeInformation.java
@@ -69,7 +69,7 @@ public class AlternativeTypeInformation implements TypeInformation {
@Override
public String toString() {
- StringBuffer buf = new StringBuffer();
+ StringBuilder buf = new StringBuilder();
for(int i = 0; i < restrictions.length; i++) {
if(i > 0) {
buf.append(" OR ");
diff --git a/src/de/lmu/ifi/dbs/elki/data/type/CombinedTypeInformation.java b/src/de/lmu/ifi/dbs/elki/data/type/CombinedTypeInformation.java
index 15712262..6d8c490c 100644
--- a/src/de/lmu/ifi/dbs/elki/data/type/CombinedTypeInformation.java
+++ b/src/de/lmu/ifi/dbs/elki/data/type/CombinedTypeInformation.java
@@ -67,7 +67,7 @@ public class CombinedTypeInformation implements TypeInformation {
@Override
public String toString() {
- StringBuffer buf = new StringBuffer();
+ StringBuilder buf = new StringBuilder();
for (int i = 0; i < restrictions.length; i++) {
if (i > 0) {
buf.append(" AND ");
diff --git a/src/de/lmu/ifi/dbs/elki/data/type/NoSupportedDataTypeException.java b/src/de/lmu/ifi/dbs/elki/data/type/NoSupportedDataTypeException.java
index 188f962c..389d86dc 100644
--- a/src/de/lmu/ifi/dbs/elki/data/type/NoSupportedDataTypeException.java
+++ b/src/de/lmu/ifi/dbs/elki/data/type/NoSupportedDataTypeException.java
@@ -66,7 +66,7 @@ public class NoSupportedDataTypeException extends IllegalStateException {
@Override
public String getMessage() {
- StringBuffer buf = new StringBuffer(super.getMessage());
+ StringBuilder buf = new StringBuilder(super.getMessage());
if(types != null) {
buf.append("\nAvailable types:");
for(TypeInformation type : types) {
diff --git a/src/de/lmu/ifi/dbs/elki/data/type/SimpleTypeInformation.java b/src/de/lmu/ifi/dbs/elki/data/type/SimpleTypeInformation.java
index 7fb66462..738c66f8 100644
--- a/src/de/lmu/ifi/dbs/elki/data/type/SimpleTypeInformation.java
+++ b/src/de/lmu/ifi/dbs/elki/data/type/SimpleTypeInformation.java
@@ -1,7 +1,5 @@
package de.lmu.ifi.dbs.elki.data.type;
-import de.lmu.ifi.dbs.elki.persistent.ByteBufferSerializer;
-
/*
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
@@ -25,6 +23,8 @@ import de.lmu.ifi.dbs.elki.persistent.ByteBufferSerializer;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+import de.lmu.ifi.dbs.elki.persistent.ByteBufferSerializer;
+
/**
* Class wrapping a particular data type.
*
@@ -39,12 +39,12 @@ public class SimpleTypeInformation<T> implements TypeInformation {
private Class<? super T> cls;
/**
- * Type label
+ * Type label.
*/
private String label = null;
/**
- * Type serializer
+ * Type serializer.
*/
private ByteBufferSerializer<? super T> serializer = null;
@@ -96,7 +96,6 @@ public class SimpleTypeInformation<T> implements TypeInformation {
this.serializer = serializer;
}
-
/**
* Get the raw restriction class.
*
@@ -108,7 +107,7 @@ public class SimpleTypeInformation<T> implements TypeInformation {
@Override
public boolean isAssignableFromType(TypeInformation type) {
- if(!(type instanceof SimpleTypeInformation)) {
+ if (!(type instanceof SimpleTypeInformation)) {
return false;
}
final SimpleTypeInformation<?> simpleType = (SimpleTypeInformation<?>) type;
@@ -121,7 +120,7 @@ public class SimpleTypeInformation<T> implements TypeInformation {
}
/**
- * Cast the object to type T (actually to the given restriction class!)
+ * Cast the object to type T (actually to the given restriction class!).
*
* @param other Object to cast.
* @return Instance
@@ -137,7 +136,7 @@ public class SimpleTypeInformation<T> implements TypeInformation {
}
/**
- * Get the type label
+ * Get the type label.
*
* @return Label
*/
@@ -153,4 +152,13 @@ public class SimpleTypeInformation<T> implements TypeInformation {
public ByteBufferSerializer<? super T> getSerializer() {
return serializer;
}
-} \ No newline at end of file
+
+ /**
+ * Set the serializer for this type.
+ *
+ * @param serializer Serializer to use
+ */
+ public void setSerializer(ByteBufferSerializer<? super T> serializer) {
+ this.serializer = serializer;
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/data/type/TypeInformationSerializer.java b/src/de/lmu/ifi/dbs/elki/data/type/TypeInformationSerializer.java
new file mode 100644
index 00000000..27eb5c2f
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/data/type/TypeInformationSerializer.java
@@ -0,0 +1,430 @@
+package de.lmu.ifi.dbs.elki.data.type;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+import de.lmu.ifi.dbs.elki.data.DoubleVector;
+import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.persistent.ByteArrayUtil;
+import de.lmu.ifi.dbs.elki.persistent.ByteBufferSerializer;
+import de.lmu.ifi.dbs.elki.utilities.ClassGenericsUtil;
+import de.lmu.ifi.dbs.elki.utilities.exceptions.UnableToComplyException;
+
+/**
+ * Class to handle the serialization and deserialization of type information.
+ *
+ * The serialization format is custom, and not very extensible. However, the
+ * standard Java "serializable" API did not seem well suited for this task, and
+ * assumes an object stream context, while we intend to focus on Java NIO.
+ *
+ * TODO: on the long run, this code needs to be refactored, and a more
+ * extensible format needs to be created. Maybe, a protobuf based API would be
+ * possible.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.composedOf SimpleTypeSerializer
+ * @apiviz.composedOf VectorTypeSerializer
+ * @apiviz.composedOf VectorFieldTypeSerializer
+ */
+public class TypeInformationSerializer implements ByteBufferSerializer<TypeInformation> {
+ /**
+ * Static instance.
+ */
+ public static final TypeInformationSerializer STATIC = new TypeInformationSerializer();
+
+ /**
+ * Tag for simple type.
+ */
+ private static final byte TAG_SIMPLE = 0;
+
+ /**
+ * Tag for non-field vector type.
+ */
+ private static final byte TAG_VECTOR = 1;
+
+ /**
+ * Tag for vector field type.
+ */
+ private static final byte TAG_VECTOR_FIELD = 2;
+
+ @Override
+ public TypeInformation fromByteBuffer(ByteBuffer buffer) throws IOException, UnsupportedOperationException {
+ byte type = buffer.get();
+ switch(type) {
+ case TAG_SIMPLE:
+ return SIMPLE_TYPE_SERIALIZER.fromByteBuffer(buffer);
+ case TAG_VECTOR:
+ return VECTOR_TYPE_SERIALIZER.fromByteBuffer(buffer);
+ case TAG_VECTOR_FIELD:
+ return VECTOR_FIELD_TYPE_SERIALIZER.fromByteBuffer(buffer);
+ default:
+ throw new UnsupportedOperationException("No deserialization known for type " + type);
+ }
+ }
+
+ @Override
+ public void toByteBuffer(ByteBuffer buffer, TypeInformation object) throws IOException, UnsupportedOperationException {
+ final Class<?> clz = object.getClass();
+ // We have three supported type informations.
+ // We need to check with equals, because we canNOT allow subclasses!
+ if (VectorFieldTypeInformation.class.equals(clz)) {
+ buffer.put(TAG_VECTOR_FIELD);
+ VECTOR_FIELD_TYPE_SERIALIZER.toByteBuffer(buffer, (VectorFieldTypeInformation<?>) object);
+ return;
+ }
+ if (VectorTypeInformation.class.equals(clz)) {
+ buffer.put(TAG_VECTOR);
+ VECTOR_TYPE_SERIALIZER.toByteBuffer(buffer, (VectorTypeInformation<?>) object);
+ return;
+ }
+ if (SimpleTypeInformation.class.equals(clz)) {
+ buffer.put(TAG_SIMPLE);
+ SIMPLE_TYPE_SERIALIZER.toByteBuffer(buffer, (SimpleTypeInformation<?>) object);
+ return;
+ }
+ throw new UnsupportedOperationException("Unsupported type information.");
+ }
+
+ @Override
+ public int getByteSize(TypeInformation object) throws IOException, UnsupportedOperationException {
+ final Class<?> clz = object.getClass();
+ // We have three supported type informations.
+ // We need to check with equals, because we canNOT allow subclasses!
+ if (VectorFieldTypeInformation.class.equals(clz)) {
+ return 1 + VECTOR_FIELD_TYPE_SERIALIZER.getByteSize((VectorFieldTypeInformation<?>) object);
+ }
+ if (VectorTypeInformation.class.equals(clz)) {
+ return 1 + VECTOR_TYPE_SERIALIZER.getByteSize((VectorTypeInformation<?>) object);
+ }
+ if (SimpleTypeInformation.class.equals(clz)) {
+ return 1 + SIMPLE_TYPE_SERIALIZER.getByteSize((SimpleTypeInformation<?>) object);
+ }
+ throw new UnsupportedOperationException("Unsupported type information.");
+ }
+
+ /**
+ * Serializer for simple types only.
+ */
+ static final ByteBufferSerializer<SimpleTypeInformation<?>> SIMPLE_TYPE_SERIALIZER = new SimpleTypeSerializer();
+
+ /**
+ * Serializer for non-field vectors.
+ */
+ static final ByteBufferSerializer<VectorTypeInformation<?>> VECTOR_TYPE_SERIALIZER = new VectorTypeSerializer();
+
+ /**
+ * Serializer for vector fields.
+ */
+ static final ByteBufferSerializer<VectorFieldTypeInformation<?>> VECTOR_FIELD_TYPE_SERIALIZER = new VectorFieldTypeSerializer();
+
+ /**
+ * Serialization class for pure simple types.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.uses SimpleTypeInformation
+ */
+ static class SimpleTypeSerializer implements ByteBufferSerializer<SimpleTypeInformation<?>> {
+ @SuppressWarnings("unchecked")
+ @Override
+ public SimpleTypeInformation<?> fromByteBuffer(ByteBuffer buffer) throws IOException, UnsupportedOperationException {
+ try {
+ String typename = ByteArrayUtil.STRING_SERIALIZER.fromByteBuffer(buffer);
+ Class<Object> clz = (Class<Object>) Class.forName(typename);
+ String label = ByteArrayUtil.STRING_SERIALIZER.fromByteBuffer(buffer);
+ label = ("".equals(label)) ? null : label;
+ String sername = ByteArrayUtil.STRING_SERIALIZER.fromByteBuffer(buffer);
+ ByteBufferSerializer<Object> serializer = (ByteBufferSerializer<Object>) Class.forName(sername).newInstance();
+ return new SimpleTypeInformation<Object>(clz, label, serializer);
+ } catch (ClassNotFoundException e) {
+ throw new UnsupportedOperationException("Cannot deserialize - class not found: " + e, e);
+ } catch (InstantiationException e) {
+ throw new UnsupportedOperationException("Cannot deserialize - cannot instantiate serializer: " + e.getMessage(), e);
+ } catch (IllegalAccessException e) {
+ throw new UnsupportedOperationException("Cannot deserialize - cannot instantiate serializer: " + e.getMessage(), e);
+ }
+ }
+
+ @Override
+ public void toByteBuffer(ByteBuffer buffer, SimpleTypeInformation<?> object) throws IOException, UnsupportedOperationException {
+ // First of all, the type needs to have an actual serializer in the type
+ final ByteBufferSerializer<?> serializer = object.getSerializer();
+ if (serializer == null) {
+ throw new UnsupportedOperationException("No serializer for type " + toString() + " available.");
+ }
+ // Make sure there is a constructor for the serialization class:
+ try {
+ serializer.getClass().getDeclaredConstructor();
+ } catch (NoSuchMethodException e) {
+ throw new UnsupportedOperationException("No automatic serialization possible - no default constructor for serializer.");
+ } catch (SecurityException e) {
+ throw new UnsupportedOperationException("Serialization not possible.", e);
+ }
+ // Type class
+ ByteArrayUtil.writeString(buffer, object.getRestrictionClass().getName());
+ // Name, or an empty string.
+ ByteArrayUtil.writeString(buffer, object.getLabel());
+ // Serializer class
+ ByteArrayUtil.writeString(buffer, serializer.getClass().getName());
+ }
+
+ @Override
+ public int getByteSize(SimpleTypeInformation<?> object) throws IOException, UnsupportedOperationException {
+ // First of all, the type needs to have an actual serializer in the type
+ final ByteBufferSerializer<?> serializer = object.getSerializer();
+ if (serializer == null) {
+ throw new UnsupportedOperationException("No serializer for type " + toString() + " available.");
+ }
+ // Make sure there is a constructor for the serialization class:
+ try {
+ serializer.getClass().getDeclaredConstructor();
+ } catch (NoSuchMethodException e) {
+ throw new UnsupportedOperationException("No automatic serialization possible - no default constructor for serializer.");
+ } catch (SecurityException e) {
+ throw new UnsupportedOperationException("Serialization not possible.", e);
+ }
+ int total = 0;
+ // Type class
+ total += ByteArrayUtil.STRING_SERIALIZER.getByteSize(object.getRestrictionClass().getName());
+ // Name, or an empty string.
+ total += ByteArrayUtil.STRING_SERIALIZER.getByteSize(object.getLabel());
+ // Serializer class
+ total += ByteArrayUtil.STRING_SERIALIZER.getByteSize(serializer.getClass().getName());
+ return total;
+ }
+ }
+
+ /**
+ * Serialization class for non-field vector types.
+ *
+ * FIXME: "label" is actually not supported.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.uses VectorTypeInformation
+ */
+ static class VectorTypeSerializer implements ByteBufferSerializer<VectorTypeInformation<?>> {
+ @SuppressWarnings("unchecked")
+ @Override
+ public VectorTypeInformation<?> fromByteBuffer(ByteBuffer buffer) throws IOException, UnsupportedOperationException {
+ try {
+ String typename = ByteArrayUtil.STRING_SERIALIZER.fromByteBuffer(buffer);
+ Class<DoubleVector> clz = (Class<DoubleVector>) Class.forName(typename);
+ String label = ByteArrayUtil.STRING_SERIALIZER.fromByteBuffer(buffer);
+ label = ("".equals(label)) ? null : label;
+ String sername = ByteArrayUtil.STRING_SERIALIZER.fromByteBuffer(buffer);
+ ByteBufferSerializer<DoubleVector> serializer = (ByteBufferSerializer<DoubleVector>) Class.forName(sername).newInstance();
+ int mindim = ByteArrayUtil.readSignedVarint(buffer);
+ int maxdim = ByteArrayUtil.readSignedVarint(buffer);
+ return new VectorTypeInformation<DoubleVector>(clz, serializer, mindim, maxdim);
+ } catch (ClassNotFoundException e) {
+ throw new UnsupportedOperationException("Cannot deserialize - class not found: "+e, e);
+ } catch (InstantiationException e) {
+ throw new UnsupportedOperationException("Cannot deserialize - cannot instantiate serializer: "+e.getMessage(), e);
+ } catch (IllegalAccessException e) {
+ throw new UnsupportedOperationException("Cannot deserialize - cannot instantiate serializer: "+e.getMessage(), e);
+ }
+ }
+
+ @Override
+ public void toByteBuffer(ByteBuffer buffer, VectorTypeInformation<?> object) throws IOException, UnsupportedOperationException {
+ // First of all, the type needs to have an actual serializer in the type
+ final ByteBufferSerializer<?> serializer = object.getSerializer();
+ if (serializer == null) {
+ throw new UnsupportedOperationException("No serializer for type " + toString() + " available.");
+ }
+ // Make sure there is a constructor for the serialization class:
+ try {
+ serializer.getClass().getDeclaredConstructor();
+ } catch (NoSuchMethodException e) {
+ throw new UnsupportedOperationException("No automatic serialization possible - no default constructor for serializer.");
+ } catch (SecurityException e) {
+ throw new UnsupportedOperationException("Serialization not possible.", e);
+ }
+ // Type class
+ ByteArrayUtil.writeString(buffer, object.getRestrictionClass().getName());
+ // Name, or an empty string.
+ ByteArrayUtil.writeString(buffer, object.getLabel());
+ // Serializer class
+ ByteArrayUtil.writeString(buffer, serializer.getClass().getName());
+ ByteArrayUtil.writeSignedVarint(buffer, object.mindim());
+ ByteArrayUtil.writeSignedVarint(buffer, object.maxdim());
+ }
+
+ @Override
+ public int getByteSize(VectorTypeInformation<?> object) throws IOException, UnsupportedOperationException {
+ // First of all, the type needs to have an actual serializer in the type
+ final ByteBufferSerializer<?> serializer = object.getSerializer();
+ if (serializer == null) {
+ throw new UnsupportedOperationException("No serializer for type " + toString() + " available.");
+ }
+ // Make sure there is a constructor for the serialization class:
+ try {
+ serializer.getClass().getDeclaredConstructor();
+ } catch (NoSuchMethodException e) {
+ throw new UnsupportedOperationException("No automatic serialization possible - no default constructor for serializer.");
+ } catch (SecurityException e) {
+ throw new UnsupportedOperationException("Serialization not possible.", e);
+ }
+ int total = 0;
+ // Type class
+ total += ByteArrayUtil.STRING_SERIALIZER.getByteSize(object.getRestrictionClass().getName());
+ // Name, or an empty string.
+ total += ByteArrayUtil.STRING_SERIALIZER.getByteSize(object.getLabel());
+ // Serializer class
+ total += ByteArrayUtil.STRING_SERIALIZER.getByteSize(serializer.getClass().getName());
+ // Dimensionality
+ total += ByteArrayUtil.getSignedVarintSize(object.mindim());
+ total += ByteArrayUtil.getSignedVarintSize(object.maxdim());
+ return total;
+ }
+ }
+
+ /**
+ * Serialization class for field vector types.
+ *
+ * FIXME: "relation label" is actually not properly supported.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.uses VectorFieldTypeInformation
+ */
+ static class VectorFieldTypeSerializer implements ByteBufferSerializer<VectorFieldTypeInformation<?>> {
+ @SuppressWarnings("unchecked")
+ @Override
+ public VectorFieldTypeInformation<?> fromByteBuffer(ByteBuffer buffer) throws IOException, UnsupportedOperationException {
+ try {
+ // Factory type!
+ String typename = ByteArrayUtil.STRING_SERIALIZER.fromByteBuffer(buffer);
+ NumberVector.Factory<DoubleVector, ?> factory = (NumberVector.Factory<DoubleVector, ?>) ClassGenericsUtil.instantiate(NumberVector.Factory.class, typename);
+ // Relation label
+ String label = ByteArrayUtil.STRING_SERIALIZER.fromByteBuffer(buffer);
+ label = ("".equals(label)) ? null : label;
+ // Serialization class
+ String sername = ByteArrayUtil.STRING_SERIALIZER.fromByteBuffer(buffer);
+ ByteBufferSerializer<DoubleVector> serializer = (ByteBufferSerializer<DoubleVector>) Class.forName(sername).newInstance();
+ // Dimensionalities
+ int mindim = ByteArrayUtil.readSignedVarint(buffer);
+ int maxdim = ByteArrayUtil.readSignedVarint(buffer);
+ // Column names
+ int cols = ByteArrayUtil.readUnsignedVarint(buffer);
+ if (cols > 0) {
+ assert(mindim == maxdim && maxdim == cols) : "Inconsistent dimensionality and column names!";
+ String[] labels = new String[cols];
+ for (int i = 0; i < cols; i++) {
+ labels[i] = ByteArrayUtil.readString(buffer);
+ }
+ return new VectorFieldTypeInformation<DoubleVector>(factory, mindim, labels, serializer);
+ } else {
+ return new VectorFieldTypeInformation<DoubleVector>(factory, mindim, maxdim, serializer);
+ }
+ } catch (UnableToComplyException e) {
+ throw new UnsupportedOperationException("Cannot deserialize - cannot instantiate factory: "+e, e);
+ } catch (ClassNotFoundException e) {
+ throw new UnsupportedOperationException("Cannot deserialize - class not found: "+e, e);
+ } catch (InstantiationException e) {
+ throw new UnsupportedOperationException("Cannot deserialize - cannot instantiate serializer: "+e.getMessage(), e);
+ } catch (IllegalAccessException e) {
+ throw new UnsupportedOperationException("Cannot deserialize - cannot instantiate serializer: "+e.getMessage(), e);
+ }
+ }
+
+ @Override
+ public void toByteBuffer(ByteBuffer buffer, VectorFieldTypeInformation<?> object) throws IOException, UnsupportedOperationException {
+ // First of all, the type needs to have an actual serializer in the type
+ final ByteBufferSerializer<?> serializer = object.getSerializer();
+ if (serializer == null) {
+ throw new UnsupportedOperationException("No serializer for type " + toString() + " available.");
+ }
+ // Make sure there is a constructor for the serialization class:
+ try {
+ serializer.getClass().getDeclaredConstructor();
+ } catch (NoSuchMethodException e) {
+ throw new UnsupportedOperationException("No automatic serialization possible - no default constructor for serializer.");
+ } catch (SecurityException e) {
+ throw new UnsupportedOperationException("Serialization not possible.", e);
+ }
+ // Use *factory* class!
+ ByteArrayUtil.writeString(buffer, object.getFactory().getClass().getName());
+ // Name, or an empty string.
+ ByteArrayUtil.writeString(buffer, object.getLabel());
+ // Serializer class
+ ByteArrayUtil.writeString(buffer, serializer.getClass().getName());
+ // Dimensionality
+ ByteArrayUtil.writeSignedVarint(buffer, object.mindim());
+ ByteArrayUtil.writeSignedVarint(buffer, object.maxdim());
+ // Column names
+ String[] labels = object.getLabels();
+ if (labels == null) {
+ ByteArrayUtil.writeUnsignedVarint(buffer, 0);
+ } else {
+ ByteArrayUtil.writeUnsignedVarint(buffer, labels.length);
+ for (String s : labels) {
+ ByteArrayUtil.writeString(buffer, s);
+ }
+ }
+ }
+
+ @Override
+ public int getByteSize(VectorFieldTypeInformation<?> object) throws IOException, UnsupportedOperationException {
+ // First of all, the type needs to have an actual serializer in the type
+ final ByteBufferSerializer<?> serializer = object.getSerializer();
+ if (serializer == null) {
+ throw new UnsupportedOperationException("No serializer for type " + toString() + " available.");
+ }
+ // Make sure there is a constructor for the serialization class:
+ try {
+ serializer.getClass().getDeclaredConstructor();
+ } catch (NoSuchMethodException e) {
+ throw new UnsupportedOperationException("No automatic serialization possible - no default constructor for serializer.");
+ } catch (SecurityException e) {
+ throw new UnsupportedOperationException("Serialization not possible.", e);
+ }
+ int total = 0;
+ // Type class
+ total += ByteArrayUtil.STRING_SERIALIZER.getByteSize(object.getRestrictionClass().getName());
+ // Name, or an empty string.
+ total += ByteArrayUtil.STRING_SERIALIZER.getByteSize(object.getLabel());
+ // Serializer class
+ total += ByteArrayUtil.STRING_SERIALIZER.getByteSize(serializer.getClass().getName());
+ // Dimensionality
+ total += ByteArrayUtil.getSignedVarintSize(object.mindim());
+ total += ByteArrayUtil.getSignedVarintSize(object.maxdim());
+ // Column names
+ String[] labels = object.getLabels();
+ if (labels == null) {
+ total += ByteArrayUtil.getUnsignedVarintSize(0);
+ } else {
+ total += ByteArrayUtil.getUnsignedVarintSize(labels.length);
+ for (String s : labels) {
+ total += ByteArrayUtil.getStringSize(s);
+ }
+ }
+ return total;
+ }
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/data/type/TypeUtil.java b/src/de/lmu/ifi/dbs/elki/data/type/TypeUtil.java
index 4d62a453..ea5f2fc2 100644
--- a/src/de/lmu/ifi/dbs/elki/data/type/TypeUtil.java
+++ b/src/de/lmu/ifi/dbs/elki/data/type/TypeUtil.java
@@ -30,6 +30,7 @@ import de.lmu.ifi.dbs.elki.data.ExternalID;
import de.lmu.ifi.dbs.elki.data.FloatVector;
import de.lmu.ifi.dbs.elki.data.LabelList;
import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.data.SimpleClassLabel;
import de.lmu.ifi.dbs.elki.data.SparseDoubleVector;
import de.lmu.ifi.dbs.elki.data.SparseFloatVector;
import de.lmu.ifi.dbs.elki.data.SparseNumberVector;
@@ -38,13 +39,13 @@ import de.lmu.ifi.dbs.elki.data.spatial.PolygonsObject;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDFactory;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
-import de.lmu.ifi.dbs.elki.database.query.DistanceDBIDResult;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Matrix;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector;
import de.lmu.ifi.dbs.elki.persistent.ByteArrayUtil;
/**
- * Utility package containing various common types
+ * Utility package containing various common types.
*
* @author Erich Schubert
*
@@ -53,70 +54,82 @@ import de.lmu.ifi.dbs.elki.persistent.ByteArrayUtil;
*/
public final class TypeUtil {
/**
- * Input type for algorithms that accept anything
+ * Fake Constructor.
+ */
+ private TypeUtil() {
+ // Do not instantiate.
+ }
+
+ /**
+ * Input type for algorithms that accept anything.
*/
public static final SimpleTypeInformation<Object> ANY = new SimpleTypeInformation<Object>(Object.class);
/**
- * Database IDs
+ * Database IDs.
*/
public static final SimpleTypeInformation<DBID> DBID = new SimpleTypeInformation<DBID>(DBID.class, DBIDFactory.FACTORY.getDBIDSerializer());
/**
- * Database ID lists
+ * Database ID lists.
*/
public static final SimpleTypeInformation<DBIDs> DBIDS = new SimpleTypeInformation<DBIDs>(DBIDs.class);
/**
- * A string
+ * A string.
*/
public static final SimpleTypeInformation<String> STRING = new SimpleTypeInformation<String>(String.class, ByteArrayUtil.STRING_SERIALIZER);
/**
- * A class label
+ * A class label.
*/
public static final SimpleTypeInformation<ClassLabel> CLASSLABEL = new SimpleTypeInformation<ClassLabel>(ClassLabel.class);
/**
+ * Simple class labels.
+ */
+ public static final SimpleTypeInformation<SimpleClassLabel> SIMPLE_CLASSLABEL = new SimpleTypeInformation<SimpleClassLabel>(SimpleClassLabel.class, SimpleClassLabel.SERIALIZER);
+
+ /**
* A list of labels.
*/
- public static final SimpleTypeInformation<LabelList> LABELLIST = new SimpleTypeInformation<LabelList>(LabelList.class);
+ public static final SimpleTypeInformation<LabelList> LABELLIST = new SimpleTypeInformation<LabelList>(LabelList.class, LabelList.SERIALIZER);
/**
- * A list of neighbors
+ * A list of neighbors.
*/
public static final SimpleTypeInformation<DistanceDBIDResult<?>> NEIGHBORLIST = new SimpleTypeInformation<DistanceDBIDResult<?>>(DistanceDBIDResult.class);
/**
* Either class label, object labels or a string - anything that will be
* accepted by
- * {@link de.lmu.ifi.dbs.elki.utilities.DatabaseUtil#guessObjectLabelRepresentation}
+ * {@link de.lmu.ifi.dbs.elki.utilities.DatabaseUtil#guessObjectLabelRepresentation}.
*/
public static final TypeInformation GUESSED_LABEL = new AlternativeTypeInformation(LABELLIST, CLASSLABEL, STRING);
/**
* Number vectors of <em>variable</em> length.
*/
- public static final SimpleTypeInformation<? super NumberVector<?, ?>> NUMBER_VECTOR_VARIABLE_LENGTH = new SimpleTypeInformation<NumberVector<?, ?>>(NumberVector.class);
+ public static final SimpleTypeInformation<? super NumberVector<?>> NUMBER_VECTOR_VARIABLE_LENGTH = new SimpleTypeInformation<NumberVector<?>>(NumberVector.class);
/**
* Input type for algorithms that require number vector fields.
*/
- public static final VectorFieldTypeInformation<NumberVector<?, ?>> NUMBER_VECTOR_FIELD = new VectorFieldTypeInformation<NumberVector<?, ?>>(NumberVector.class);
+ public static final VectorFieldTypeInformation<NumberVector<?>> NUMBER_VECTOR_FIELD = new VectorFieldTypeInformation<NumberVector<?>>(NumberVector.class);
/**
* Input type for algorithms that require number vector fields.
*
* If possible, please use {@link #NUMBER_VECTOR_FIELD}!
*/
- public static final VectorFieldTypeInformation<DoubleVector> DOUBLE_VECTOR_FIELD = new VectorFieldTypeInformation<DoubleVector>(DoubleVector.class, DoubleVector.STATIC);
+ public static final VectorFieldTypeInformation<DoubleVector> DOUBLE_VECTOR_FIELD = new VectorFieldTypeInformation<DoubleVector>(DoubleVector.class);
/**
* Input type for algorithms that require number vector fields.
*
* If possible, please use {@link #NUMBER_VECTOR_FIELD}!
*/
- public static final VectorFieldTypeInformation<FloatVector> FLOAT_VECTOR_FIELD = new VectorFieldTypeInformation<FloatVector>(FloatVector.class, FloatVector.STATIC);
+ public static final VectorFieldTypeInformation<FloatVector> FLOAT_VECTOR_FIELD = new VectorFieldTypeInformation<FloatVector>(FloatVector.class);
/**
* Input type for algorithms that require number vector fields.
@@ -126,12 +139,12 @@ public final class TypeUtil {
/**
* Sparse float vector field.
*/
- public static final SimpleTypeInformation<SparseNumberVector<?, ?>> SPARSE_VECTOR_VARIABLE_LENGTH = new SimpleTypeInformation<SparseNumberVector<?, ?>>(SparseNumberVector.class);
+ public static final SimpleTypeInformation<SparseNumberVector<?>> SPARSE_VECTOR_VARIABLE_LENGTH = new SimpleTypeInformation<SparseNumberVector<?>>(SparseNumberVector.class);
/**
* Sparse vector field.
*/
- public static final VectorFieldTypeInformation<SparseNumberVector<?, ?>> SPARSE_VECTOR_FIELD = new VectorFieldTypeInformation<SparseNumberVector<?, ?>>(SparseNumberVector.class);
+ public static final VectorFieldTypeInformation<SparseNumberVector<?>> SPARSE_VECTOR_FIELD = new VectorFieldTypeInformation<SparseNumberVector<?>>(SparseNumberVector.class);
/**
* Sparse float vector field.
@@ -148,12 +161,12 @@ public final class TypeUtil {
public static final VectorFieldTypeInformation<SparseDoubleVector> SPARSE_DOUBLE_FIELD = new VectorFieldTypeInformation<SparseDoubleVector>(SparseDoubleVector.class);
/**
- * External ID type
+ * External ID type.
*/
public static final SimpleTypeInformation<ExternalID> EXTERNALID = new SimpleTypeInformation<ExternalID>(ExternalID.class);
/**
- * Type for polygons
+ * Type for polygons.
*/
public static final SimpleTypeInformation<PolygonsObject> POLYGON_TYPE = new SimpleTypeInformation<PolygonsObject>(PolygonsObject.class);
@@ -188,7 +201,7 @@ public final class TypeUtil {
* @param ts Types
* @return array
*/
- public static final TypeInformation[] array(TypeInformation... ts) {
+ public static TypeInformation[] array(TypeInformation... ts) {
return ts;
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/data/type/VectorFieldTypeInformation.java b/src/de/lmu/ifi/dbs/elki/data/type/VectorFieldTypeInformation.java
index 9e6c2470..7de2a121 100644
--- a/src/de/lmu/ifi/dbs/elki/data/type/VectorFieldTypeInformation.java
+++ b/src/de/lmu/ifi/dbs/elki/data/type/VectorFieldTypeInformation.java
@@ -33,94 +33,68 @@ import de.lmu.ifi.dbs.elki.persistent.ByteBufferSerializer;
*
* @param <V> Vector type
*/
-public class VectorFieldTypeInformation<V extends FeatureVector<?, ?>> extends VectorTypeInformation<V> {
+public class VectorFieldTypeInformation<V extends FeatureVector<?>> extends VectorTypeInformation<V> {
/**
- * Object factory for producing new instances
+ * Object factory for producing new instances.
*/
- private final V factory;
+ private final FeatureVector.Factory<V, ?> factory;
/**
- * Labels
+ * Labels.
*/
private String[] labels = null;
/**
* Constructor with given dimensionality and factory, so usually an instance.
*
- * @param cls Restriction java class
- * @param serializer Serializer
+ * @param factory Factory class
* @param dim Dimensionality
* @param labels Labels
- * @param factory Factory class
+ * @param serializer Serializer
*/
- public VectorFieldTypeInformation(Class<? super V> cls, ByteBufferSerializer<? super V> serializer, int dim, String[] labels, V factory) {
- super(cls, serializer, dim, dim);
- this.labels = labels;
+ public VectorFieldTypeInformation(FeatureVector.Factory<V, ?> factory, int dim, String[] labels, ByteBufferSerializer<? super V> serializer) {
+ super(factory.getRestrictionClass(), serializer, dim, dim);
this.factory = factory;
+ this.labels = labels;
assert (labels == null || labels.length == dim) : "Created vector field with incomplete labels.";
}
/**
- * Constructor for a request with minimum and maximum dimensionality.
- *
- * @param cls Vector restriction class.
- * @param serializer Serializer
- * @param mindim Minimum dimensionality request
- * @param maxdim Maximum dimensionality request
- */
- public VectorFieldTypeInformation(Class<? super V> cls, ByteBufferSerializer<? super V> serializer, int mindim, int maxdim) {
- super(cls, serializer, mindim, maxdim);
- this.factory = null;
- }
-
- /**
* Constructor with given dimensionality and factory, so usually an instance.
*
- * @param cls Restriction java class
- * @param serializer Serializer
- * @param dim Dimensionality
* @param factory Factory class
- */
- public VectorFieldTypeInformation(Class<? super V> cls, ByteBufferSerializer<? super V> serializer, int dim, V factory) {
- super(cls, serializer, dim, dim);
- this.factory = factory;
- }
-
- /**
- * Constructor for a request with fixed dimensionality.
- *
- * @param cls Vector restriction class.
+ * @param mindim Minimum dimensionality
+ * @param maxdim Maximum dimensionality
* @param serializer Serializer
- * @param dim Dimensionality request
*/
- public VectorFieldTypeInformation(Class<? super V> cls, ByteBufferSerializer<? super V> serializer, int dim) {
- super(cls, serializer, dim, dim);
- this.factory = null;
+ public VectorFieldTypeInformation(FeatureVector.Factory<V, ?> factory, int mindim, int maxdim, ByteBufferSerializer<? super V> serializer) {
+ super(factory.getRestrictionClass(), serializer, mindim, maxdim);
+ this.factory = factory;
}
/**
- * Constructor for a request without fixed dimensionality.
+ * Constructor with given dimensionality and factory, so usually an instance.
*
- * @param cls Vector restriction class.
+ * @param factory Factory class
+ * @param dim Dimensionality
* @param serializer Serializer
*/
- public VectorFieldTypeInformation(Class<? super V> cls, ByteBufferSerializer<? super V> serializer) {
- super(cls, serializer);
- this.factory = null;
+ public VectorFieldTypeInformation(FeatureVector.Factory<V, ?> factory, int dim, ByteBufferSerializer<? super V> serializer) {
+ super(factory.getRestrictionClass(), serializer, dim, dim);
+ this.factory = factory;
}
/**
* Constructor with given dimensionality and factory, so usually an instance.
*
- * @param cls Restriction java class
+ * @param factory Factory class
* @param dim Dimensionality
* @param labels Labels
- * @param factory Factory class
*/
- public VectorFieldTypeInformation(Class<? super V> cls, int dim, String[] labels, V factory) {
- super(cls, dim, dim);
- this.labels = labels;
+ public VectorFieldTypeInformation(FeatureVector.Factory<V, ?> factory, int dim, String[] labels) {
+ super(factory.getRestrictionClass(), factory.getDefaultSerializer(), dim, dim);
this.factory = factory;
+ this.labels = labels;
assert (labels == null || labels.length == dim) : "Created vector field with incomplete labels.";
}
@@ -139,12 +113,11 @@ public class VectorFieldTypeInformation<V extends FeatureVector<?, ?>> extends V
/**
* Constructor with given dimensionality and factory, so usually an instance.
*
- * @param cls Restriction java class
- * @param dim Dimensionality
* @param factory Factory class
+ * @param dim Dimensionality
*/
- public VectorFieldTypeInformation(Class<? super V> cls, int dim, V factory) {
- super(cls, dim, dim);
+ public VectorFieldTypeInformation(FeatureVector.Factory<V, ?> factory, int dim) {
+ super(factory.getRestrictionClass(), factory.getDefaultSerializer(), dim, dim);
this.factory = factory;
}
@@ -172,12 +145,12 @@ public class VectorFieldTypeInformation<V extends FeatureVector<?, ?>> extends V
@Override
public boolean isAssignableFromType(TypeInformation type) {
// Do all checks from superclass
- if(!super.isAssignableFromType(type)) {
+ if (!super.isAssignableFromType(type)) {
return false;
}
// Additionally check that mindim == maxdim.
VectorTypeInformation<?> other = (VectorTypeInformation<?>) type;
- if(other.mindim != other.maxdim) {
+ if (other.mindim != other.maxdim) {
return false;
}
return true;
@@ -188,8 +161,8 @@ public class VectorFieldTypeInformation<V extends FeatureVector<?, ?>> extends V
*
* @return dimensionality
*/
- public int dimensionality() {
- if(mindim != maxdim) {
+ public int getDimensionality() {
+ if (mindim != maxdim) {
throw new UnsupportedOperationException("Requesting dimensionality for a type request without defined dimensionality!");
}
return mindim;
@@ -200,66 +173,49 @@ public class VectorFieldTypeInformation<V extends FeatureVector<?, ?>> extends V
*
* @return the factory
*/
- public V getFactory() {
- if(factory == null) {
+ public FeatureVector.Factory<V, ?> getFactory() {
+ if (factory == null) {
throw new UnsupportedOperationException("Requesting factory for a type request!");
}
return factory;
}
- /**
- * Pseudo constructor that is often convenient to use when T is not completely
- * known.
- *
- * @param <T> Type
- * @param cls Class restriction
- * @return Type
- */
- public static <T extends FeatureVector<?, ?>> VectorFieldTypeInformation<T> get(Class<T> cls) {
- return new VectorFieldTypeInformation<T>(cls);
- }
-
- /**
- * Pseudo constructor that is often convenient to use when T is not completely
- * known, but the dimensionality is fixed.
- *
- * @param <T> Type
- * @param cls Class restriction
- * @param dim Dimensionality (exact)
- * @return Type
- */
- public static <T extends FeatureVector<?, ?>> VectorFieldTypeInformation<T> get(Class<T> cls, int dim) {
- return new VectorFieldTypeInformation<T>(cls, dim);
- }
-
@Override
public String toString() {
- StringBuffer buf = new StringBuffer(getRestrictionClass().getSimpleName());
- if(mindim == maxdim) {
+ StringBuilder buf = new StringBuilder(getRestrictionClass().getSimpleName());
+ if (mindim == maxdim) {
buf.append(",dim=").append(mindim);
- }
- else {
+ } else {
buf.append(",field");
- if(mindim >= 0) {
- buf.append(",mindim=" + mindim);
+ if (mindim >= 0) {
+ buf.append(",mindim=").append(mindim);
}
- if(maxdim < Integer.MAX_VALUE) {
- buf.append(",maxdim=" + maxdim);
+ if (maxdim < Integer.MAX_VALUE) {
+ buf.append(",maxdim=").append(maxdim);
}
}
return buf.toString();
}
/**
- * Get the column label
+ * Get the column label.
*
* @param col Column number
* @return Label
*/
public String getLabel(int col) {
- if(labels == null) {
+ if (labels == null) {
return null;
}
- return labels[col - 1];
+ return labels[col];
+ }
+
+ /**
+ * Get the column labels.
+ *
+ * @return labels
+ */
+ protected String[] getLabels() {
+ return labels;
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/data/type/VectorTypeInformation.java b/src/de/lmu/ifi/dbs/elki/data/type/VectorTypeInformation.java
index a1db6d1c..4ba86a8b 100644
--- a/src/de/lmu/ifi/dbs/elki/data/type/VectorTypeInformation.java
+++ b/src/de/lmu/ifi/dbs/elki/data/type/VectorTypeInformation.java
@@ -33,19 +33,19 @@ import de.lmu.ifi.dbs.elki.persistent.ByteBufferSerializer;
*
* @param <V> Vector type
*/
-public class VectorTypeInformation<V extends FeatureVector<?, ?>> extends SimpleTypeInformation<V> {
+public class VectorTypeInformation<V extends FeatureVector<?>> extends SimpleTypeInformation<V> {
/**
- * Minimum dimensionality
+ * Minimum dimensionality.
*/
protected final int mindim;
/**
- * Maximum dimensionality
+ * Maximum dimensionality.
*/
protected final int maxdim;
-
+
/**
- * Constructor.
+ * Constructor for an actual type.
*
* @param cls base class
* @param serializer Serializer
@@ -60,17 +60,7 @@ public class VectorTypeInformation<V extends FeatureVector<?, ?>> extends Simple
}
/**
- * Constructor without size constraints.
- *
- * @param cls base class
- * @param serializer Serializer
- */
- public VectorTypeInformation(Class<? super V> cls, ByteBufferSerializer<? super V> serializer) {
- this(cls, serializer, -1, Integer.MAX_VALUE);
- }
-
- /**
- * Constructor.
+ * Constructor for a type request.
*
* @param cls base class
* @param mindim Minimum dimensionality
@@ -81,9 +71,9 @@ public class VectorTypeInformation<V extends FeatureVector<?, ?>> extends Simple
}
/**
- * Constructor without size constraints.
+ * Constructor for a type request without dimensionality constraints.
*
- * @param cls
+ * @param cls Class constraint
*/
public VectorTypeInformation(Class<? super V> cls) {
this(cls, null, -1, Integer.MAX_VALUE);
@@ -153,39 +143,15 @@ public class VectorTypeInformation<V extends FeatureVector<?, ?>> extends Simple
return maxdim;
}
- /**
- * Pseudo constructor that is often convenient to use when T is not completely known.
- *
- * @param <T> Type
- * @param cls Class restriction
- * @param mindim Minimum dimensionality
- * @param maxdim Maximum dimensionality
- * @return Type information
- */
- public static <T extends FeatureVector<?, ?>> VectorTypeInformation<T> get(Class<T> cls, int mindim, int maxdim) {
- return new VectorTypeInformation<T>(cls, mindim, maxdim);
- }
-
- /**
- * Pseudo constructor that is often convenient to use when T is not completely known.
- *
- * @param <T> Type
- * @param cls Class restriction
- * @return Type information
- */
- public static <T extends FeatureVector<?, ?>> VectorTypeInformation<T> get(Class<T> cls) {
- return new VectorTypeInformation<T>(cls);
- }
-
@Override
public String toString() {
- StringBuffer buf = new StringBuffer(super.toString());
+ StringBuilder buf = new StringBuilder(super.toString());
buf.append(",variable");
- if (mindim >= 0) {
- buf.append(",mindim="+mindim);
+ if(mindim >= 0) {
+ buf.append(",mindim=").append(mindim);
}
- if (maxdim < Integer.MAX_VALUE) {
- buf.append(",maxdim="+maxdim);
+ if(maxdim < Integer.MAX_VALUE) {
+ buf.append(",maxdim=").append(maxdim);
}
return buf.toString();
}
diff --git a/src/de/lmu/ifi/dbs/elki/database/AbstractDatabase.java b/src/de/lmu/ifi/dbs/elki/database/AbstractDatabase.java
index e0fc6bf2..b62db142 100644
--- a/src/de/lmu/ifi/dbs/elki/database/AbstractDatabase.java
+++ b/src/de/lmu/ifi/dbs/elki/database/AbstractDatabase.java
@@ -32,7 +32,7 @@ import java.util.ListIterator;
import de.lmu.ifi.dbs.elki.data.type.NoSupportedDataTypeException;
import de.lmu.ifi.dbs.elki.data.type.TypeInformation;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreListener;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.query.DatabaseQuery;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.database.query.knn.KNNQuery;
@@ -72,7 +72,7 @@ public abstract class AbstractDatabase extends AbstractHierarchicalResult implem
* Key: {@code -db.index}
* </p>
*/
- public static final OptionID INDEX_ID = OptionID.getOrCreateOptionID("db.index", "Database indexes to add.");
+ public static final OptionID INDEX_ID = new OptionID("db.index", "Database indexes to add.");
/**
* The event manager, collects events and fires them on demand.
@@ -85,12 +85,12 @@ public abstract class AbstractDatabase extends AbstractHierarchicalResult implem
protected final List<Relation<?>> relations = new java.util.Vector<Relation<?>>();
/**
- * Indexes
+ * Indexes.
*/
protected final List<Index> indexes = new java.util.Vector<Index>();
/**
- * Index factories
+ * Index factories.
*/
protected final Collection<IndexFactory<?, ?>> indexFactories = new java.util.Vector<IndexFactory<?, ?>>();
@@ -120,7 +120,7 @@ public abstract class AbstractDatabase extends AbstractHierarchicalResult implem
}
@Override
- public SingleObjectBundle getBundle(DBID id) {
+ public SingleObjectBundle getBundle(DBIDRef id) {
assert (id != null);
// TODO: ensure that the ID actually exists in the database?
try {
@@ -302,5 +302,5 @@ public abstract class AbstractDatabase extends AbstractHierarchicalResult implem
return "database";
}
- abstract protected Logging getLogger();
+ protected abstract Logging getLogger();
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/database/Database.java b/src/de/lmu/ifi/dbs/elki/database/Database.java
index a931d5c8..e5b9f7ff 100644
--- a/src/de/lmu/ifi/dbs/elki/database/Database.java
+++ b/src/de/lmu/ifi/dbs/elki/database/Database.java
@@ -29,7 +29,7 @@ import de.lmu.ifi.dbs.elki.data.type.NoSupportedDataTypeException;
import de.lmu.ifi.dbs.elki.data.type.TypeInformation;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreEvent;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreListener;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.database.query.knn.KNNQuery;
import de.lmu.ifi.dbs.elki.database.query.range.RangeQuery;
@@ -58,7 +58,7 @@ import de.lmu.ifi.dbs.elki.utilities.InspectionUtilFrequentlyScanned;
* @apiviz.has RKNNQuery oneway - - provides
* @apiviz.has Relation oneway - - contains
* @apiviz.has Index oneway - - manages
- * @apiviz.uses DataStoreListener oneway - - invokes
+ * @apiviz.has DataStoreListener oneway - - invokes
*/
public interface Database extends HierarchicalResult, InspectionUtilFrequentlyScanned {
/**
@@ -177,7 +177,7 @@ public interface Database extends HierarchicalResult, InspectionUtilFrequentlySc
* @param id the id of the Object to be obtained from the Database
* @return Bundle containing the objects' data
*/
- SingleObjectBundle getBundle(DBID id);
+ SingleObjectBundle getBundle(DBIDRef id);
/**
* Returns the DatabaseObject represented by the specified id.
@@ -248,4 +248,4 @@ public interface Database extends HierarchicalResult, InspectionUtilFrequentlySc
* @see DataStoreEvent
*/
void flushDataStoreEvents();
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/database/DatabaseEventManager.java b/src/de/lmu/ifi/dbs/elki/database/DatabaseEventManager.java
index 633be46f..ce6e4d9a 100644
--- a/src/de/lmu/ifi/dbs/elki/database/DatabaseEventManager.java
+++ b/src/de/lmu/ifi/dbs/elki/database/DatabaseEventManager.java
@@ -30,7 +30,7 @@ import javax.swing.event.EventListenerList;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreEvent;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreEvent.Type;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreListener;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.ids.HashSetModifiableDBIDs;
@@ -175,8 +175,8 @@ public class DatabaseEventManager {
* @see #fireObjectsChanged
* @see de.lmu.ifi.dbs.elki.database.datastore.DataStoreEvent.Type#INSERT
*/
- public void fireObjectInserted(DBID insertion) {
- fireObjectsChanged(insertion, DataStoreEvent.Type.INSERT);
+ public void fireObjectInserted(DBIDRef insertion) {
+ fireObjectChanged(insertion, DataStoreEvent.Type.INSERT);
}
/**
@@ -211,8 +211,8 @@ public class DatabaseEventManager {
* @see #fireObjectsChanged
* @see de.lmu.ifi.dbs.elki.database.datastore.DataStoreEvent.Type#DELETE
*/
- protected void fireObjectRemoved(DBID deletion) {
- fireObjectsChanged(deletion, DataStoreEvent.Type.DELETE);
+ protected void fireObjectRemoved(DBIDRef deletion) {
+ fireObjectChanged(deletion, DataStoreEvent.Type.DELETE);
}
/**
@@ -245,6 +245,35 @@ public class DatabaseEventManager {
}
/**
+ * Handles a DataStoreEvent with the specified type. If the current event type
+ * is not equal to the specified type, the events accumulated up to now will
+ * be fired first.
+ *
+ * The new event will be aggregated and fired on demand if
+ * {@link #accumulateDataStoreEvents} is set, otherwise all registered
+ * <code>DataStoreListener</code> will be notified immediately that the
+ * content of the database has been changed.
+ *
+ * @param object the object that has been changed, i.e. inserted, deleted
+ * or updated
+ */
+ private void fireObjectChanged(DBIDRef object, DataStoreEvent.Type type) {
+ // flush first
+ if(currentDataStoreEventType != null && !currentDataStoreEventType.equals(type)) {
+ flushDataStoreEvents();
+ }
+ if (this.dataStoreObjects == null) {
+ this.dataStoreObjects = DBIDUtil.newHashSet();
+ }
+ this.dataStoreObjects.add(object);
+ currentDataStoreEventType = type;
+
+ if(!accumulateDataStoreEvents) {
+ flushDataStoreEvents();
+ }
+ }
+
+ /**
* Informs all registered <code>ResultListener</code> that a new result was
* added.
*
diff --git a/src/de/lmu/ifi/dbs/elki/database/HashmapDatabase.java b/src/de/lmu/ifi/dbs/elki/database/HashmapDatabase.java
index a92c4427..069de73e 100644
--- a/src/de/lmu/ifi/dbs/elki/database/HashmapDatabase.java
+++ b/src/de/lmu/ifi/dbs/elki/database/HashmapDatabase.java
@@ -32,6 +32,7 @@ import de.lmu.ifi.dbs.elki.database.ids.ArrayModifiableDBIDs;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDFactory;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.ids.HashSetModifiableDBIDs;
@@ -42,6 +43,7 @@ import de.lmu.ifi.dbs.elki.datasource.DatabaseConnection;
import de.lmu.ifi.dbs.elki.datasource.FileBasedDatabaseConnection;
import de.lmu.ifi.dbs.elki.datasource.bundle.MultipleObjectsBundle;
import de.lmu.ifi.dbs.elki.datasource.bundle.ObjectBundle;
+import de.lmu.ifi.dbs.elki.datasource.bundle.SingleObjectBundle;
import de.lmu.ifi.dbs.elki.index.Index;
import de.lmu.ifi.dbs.elki.index.IndexFactory;
import de.lmu.ifi.dbs.elki.logging.Logging;
@@ -70,7 +72,7 @@ public class HashmapDatabase extends AbstractDatabase implements UpdatableDataba
/**
* Our logger
*/
- private static final Logging logger = Logging.getLogger(HashmapDatabase.class);
+ private static final Logging LOG = Logging.getLogger(HashmapDatabase.class);
/**
* IDs of this database
@@ -102,7 +104,7 @@ public class HashmapDatabase extends AbstractDatabase implements UpdatableDataba
this.addChildResult(idrep);
// Add indexes.
- if(indexFactories != null) {
+ if (indexFactories != null) {
this.indexFactories.addAll(indexFactories);
}
}
@@ -120,7 +122,7 @@ public class HashmapDatabase extends AbstractDatabase implements UpdatableDataba
*/
@Override
public void initialize() {
- if(databaseConnection != null) {
+ if (databaseConnection != null) {
this.insert(databaseConnection.loadData());
// Run at most once.
databaseConnection = null;
@@ -129,7 +131,7 @@ public class HashmapDatabase extends AbstractDatabase implements UpdatableDataba
@Override
public DBIDs insert(ObjectBundle objpackages) {
- if(objpackages.dataLength() == 0) {
+ if (objpackages.dataLength() == 0) {
return DBIDUtil.EMPTYDBIDS;
}
// insert into db
@@ -137,29 +139,28 @@ public class HashmapDatabase extends AbstractDatabase implements UpdatableDataba
Relation<?>[] targets = alignColumns(objpackages);
int idrepnr = -1;
- for(int i = 0; i < targets.length; i++) {
- if(targets[i] == idrep) {
+ for (int i = 0; i < targets.length; i++) {
+ if (targets[i] == idrep) {
idrepnr = i;
break;
}
}
- for(int j = 0; j < objpackages.dataLength(); j++) {
+ for (int j = 0; j < objpackages.dataLength(); j++) {
// insert object
final DBID newid;
- if(idrepnr < 0) {
+ if (idrepnr < 0) {
newid = DBIDUtil.generateSingleDBID();
- }
- else {
+ } else {
newid = (DBID) objpackages.data(j, idrepnr);
}
- if(ids.contains(newid)) {
+ if (ids.contains(newid)) {
throw new AbortException("Duplicate DBID conflict.");
}
ids.add(newid);
- for(int i = 0; i < targets.length; i++) {
+ for (int i = 0; i < targets.length; i++) {
// DBIDs were handled above.
- if(i == idrepnr) {
+ if (i == idrepnr) {
continue;
}
@SuppressWarnings("unchecked")
@@ -170,7 +171,7 @@ public class HashmapDatabase extends AbstractDatabase implements UpdatableDataba
}
// Notify indexes of insertions
- for(Index index : indexes) {
+ for (Index index : indexes) {
index.insertAll(newids);
}
@@ -191,19 +192,19 @@ public class HashmapDatabase extends AbstractDatabase implements UpdatableDataba
Relation<?>[] targets = new Relation<?>[pack.metaLength()];
{
BitSet used = new BitSet(relations.size());
- for(int i = 0; i < targets.length; i++) {
+ for (int i = 0; i < targets.length; i++) {
SimpleTypeInformation<?> meta = pack.meta(i);
// TODO: aggressively try to match exact metas first?
// Try to match unused representations only
- for(int j = used.nextClearBit(0); j >= 0 && j < relations.size(); j = used.nextClearBit(j + 1)) {
+ for (int j = used.nextClearBit(0); j >= 0 && j < relations.size(); j = used.nextClearBit(j + 1)) {
Relation<?> relation = relations.get(j);
- if(relation.getDataTypeInformation().isAssignableFromType(meta)) {
+ if (relation.getDataTypeInformation().isAssignableFromType(meta)) {
targets[i] = relation;
used.set(j);
break;
}
}
- if(targets[i] == null) {
+ if (targets[i] == null) {
targets[i] = addNewRelation(meta);
used.set(relations.size() - 1);
}
@@ -225,8 +226,8 @@ public class HashmapDatabase extends AbstractDatabase implements UpdatableDataba
relations.add(relation);
getHierarchy().add(this, relation);
// Try to add indexes where appropriate
- for(IndexFactory<?, ?> factory : indexFactories) {
- if(factory.getInputTypeRestriction().isAssignableFromType(meta)) {
+ for (IndexFactory<?, ?> factory : indexFactories) {
+ if (factory.getInputTypeRestriction().isAssignableFromType(meta)) {
@SuppressWarnings("unchecked")
final IndexFactory<Object, ?> ofact = (IndexFactory<Object, ?>) factory;
@SuppressWarnings("unchecked")
@@ -238,14 +239,16 @@ public class HashmapDatabase extends AbstractDatabase implements UpdatableDataba
}
/**
- * Removes the objects from the database (by calling {@link #doDelete(DBID)}
+ * Removes the objects from the database (by calling {@link #doDelete(DBIDRef)}
* for each object) and indexes and fires a deletion event.
+ *
+ * {@inheritDoc}
*/
@Override
public MultipleObjectsBundle delete(DBIDs ids) {
// Prepare bundle to return
MultipleObjectsBundle bundle = new MultipleObjectsBundle();
- for(Relation<?> relation : relations) {
+ for (Relation<?> relation : relations) {
ArrayList<Object> data = new ArrayList<Object>(ids.size());
for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
data.add(relation.get(iter));
@@ -254,11 +257,10 @@ public class HashmapDatabase extends AbstractDatabase implements UpdatableDataba
}
// remove from db
for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
- DBID id = iter.getDBID();
- doDelete(id);
+ doDelete(iter);
}
// Remove from indexes
- for(Index index : indexes) {
+ for (Index index : indexes) {
index.deleteAll(ids);
}
// fire deletion event
@@ -268,35 +270,50 @@ public class HashmapDatabase extends AbstractDatabase implements UpdatableDataba
}
/**
+ * Removes the object from the database (by calling {@link #doDelete(DBIDRef)})
+ * and indexes and fires a deletion event.
+ *
+ * {@inheritDoc}
+ */
+ @Override
+ public SingleObjectBundle delete(DBIDRef id) {
+ // Prepare bundle to return
+ SingleObjectBundle bundle = new SingleObjectBundle();
+ for (Relation<?> relation : relations) {
+ bundle.append(relation.getDataTypeInformation(), relation.get(id));
+ }
+ doDelete(id);
+ // Remove from indexes
+ for (Index index : indexes) {
+ index.delete(id);
+ }
+ // fire deletion event
+ eventManager.fireObjectRemoved(id);
+
+ return bundle;
+ }
+
+ /**
* Removes the object with the specified id from this database.
*
* @param id id the id of the object to be removed
*/
- private void doDelete(DBID id) {
+ private void doDelete(DBIDRef id) {
// Remove id
ids.remove(id);
// Remove from all representations.
- for(Relation<?> relation : relations) {
+ for (Relation<?> relation : relations) {
// ID has already been removed, and this would loop...
- if(relation != idrep) {
+ if (relation != idrep) {
relation.delete(id);
}
}
- restoreID(id);
- }
-
- /**
- * Makes the given id reusable for new insertion operations.
- *
- * @param id the id to become reusable
- */
- private void restoreID(final DBID id) {
DBIDFactory.FACTORY.deallocateSingleDBID(id);
}
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -322,12 +339,12 @@ public class HashmapDatabase extends AbstractDatabase implements UpdatableDataba
super.makeOptions(config);
// Get database connection.
final ObjectParameter<DatabaseConnection> dbcP = new ObjectParameter<DatabaseConnection>(OptionID.DATABASE_CONNECTION, DatabaseConnection.class, FileBasedDatabaseConnection.class);
- if(config.grab(dbcP)) {
+ if (config.grab(dbcP)) {
databaseConnection = dbcP.instantiateClass(config);
}
// Get indexes.
final ObjectListParameter<IndexFactory<?, ?>> indexFactoryP = new ObjectListParameter<IndexFactory<?, ?>>(INDEX_ID, IndexFactory.class, true);
- if(config.grab(indexFactoryP)) {
+ if (config.grab(indexFactoryP)) {
indexFactories = indexFactoryP.instantiateClasses(config);
}
}
@@ -337,4 +354,4 @@ public class HashmapDatabase extends AbstractDatabase implements UpdatableDataba
return new HashmapDatabase(databaseConnection, indexFactories);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/database/ProxyDatabase.java b/src/de/lmu/ifi/dbs/elki/database/ProxyDatabase.java
index 0f411cb4..3f41326a 100644
--- a/src/de/lmu/ifi/dbs/elki/database/ProxyDatabase.java
+++ b/src/de/lmu/ifi/dbs/elki/database/ProxyDatabase.java
@@ -40,7 +40,7 @@ public class ProxyDatabase extends AbstractDatabase {
/**
* Logger class.
*/
- private static final Logging logger = Logging.getLogger(ProxyDatabase.class);
+ private static final Logging LOG = Logging.getLogger(ProxyDatabase.class);
/**
* Our DBIDs
@@ -120,6 +120,6 @@ public class ProxyDatabase extends AbstractDatabase {
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/database/StaticArrayDatabase.java b/src/de/lmu/ifi/dbs/elki/database/StaticArrayDatabase.java
index 79143ad8..c2d66f5c 100644
--- a/src/de/lmu/ifi/dbs/elki/database/StaticArrayDatabase.java
+++ b/src/de/lmu/ifi/dbs/elki/database/StaticArrayDatabase.java
@@ -31,6 +31,7 @@ import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
import de.lmu.ifi.dbs.elki.database.ids.ArrayDBIDs;
import de.lmu.ifi.dbs.elki.database.ids.ArrayModifiableDBIDs;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.relation.DBIDView;
import de.lmu.ifi.dbs.elki.database.relation.MaterializedRelation;
@@ -67,7 +68,7 @@ public class StaticArrayDatabase extends AbstractDatabase implements Parameteriz
/**
* Our logger
*/
- private static final Logging logger = Logging.getLogger(StaticArrayDatabase.class);
+ private static final Logging LOG = Logging.getLogger(StaticArrayDatabase.class);
/**
* IDs of this database
@@ -116,8 +117,8 @@ public class StaticArrayDatabase extends AbstractDatabase implements Parameteriz
@Override
public void initialize() {
if(databaseConnection != null) {
- if(logger.isDebugging()) {
- logger.debugFine("Loading data from database connection.");
+ if(LOG.isDebugging()) {
+ LOG.debugFine("Loading data from database connection.");
}
MultipleObjectsBundle objpackages = databaseConnection.loadData();
// Run at most once.
@@ -146,9 +147,9 @@ public class StaticArrayDatabase extends AbstractDatabase implements Parameteriz
// insert into db - note: DBIDs should have been prepared before this!
Relation<?>[] targets = alignColumns(objpackages);
- for(int j = 0; j < objpackages.dataLength(); j++) {
+ DBIDIter newid = ids.iter();
+ for(int j = 0; j < objpackages.dataLength(); j++, newid.advance()) {
// insert object
- final DBID newid = ids.get(j);
for(int i = 0; i < targets.length; i++) {
// DBIDs were handled above.
if(i == idrepnr) {
@@ -183,8 +184,8 @@ public class StaticArrayDatabase extends AbstractDatabase implements Parameteriz
@Override
public void addIndex(Index index) {
- if(logger.isDebuggingFiner()) {
- logger.debugFine("Adding index: " + index);
+ if(LOG.isDebuggingFiner()) {
+ LOG.debugFine("Adding index: " + index);
}
this.indexes.add(index);
// TODO: actually add index to the representation used?
@@ -256,7 +257,7 @@ public class StaticArrayDatabase extends AbstractDatabase implements Parameteriz
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
diff --git a/src/de/lmu/ifi/dbs/elki/database/UpdatableDatabase.java b/src/de/lmu/ifi/dbs/elki/database/UpdatableDatabase.java
index 56a988f8..86555a86 100644
--- a/src/de/lmu/ifi/dbs/elki/database/UpdatableDatabase.java
+++ b/src/de/lmu/ifi/dbs/elki/database/UpdatableDatabase.java
@@ -23,8 +23,10 @@ package de.lmu.ifi.dbs.elki.database;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.datasource.bundle.ObjectBundle;
+import de.lmu.ifi.dbs.elki.datasource.bundle.SingleObjectBundle;
import de.lmu.ifi.dbs.elki.utilities.exceptions.UnableToComplyException;
/**
@@ -52,4 +54,14 @@ public interface UpdatableDatabase extends Database {
* @throws UnableToComplyException if deletion is not possible
*/
ObjectBundle delete(DBIDs ids);
+
+ /**
+ * Removes and returns the specified objects with the given ids from the
+ * database.
+ *
+ * @param id the id of the object to be removed from the database
+ * @return the object that have been removed
+ * @throws UnableToComplyException if deletion is not possible
+ */
+ SingleObjectBundle delete(DBIDRef id);
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/database/ids/integer/TroveIteratorAdapter.java b/src/de/lmu/ifi/dbs/elki/database/datastore/DBIDDataStore.java
index 2f596f47..7941219f 100644
--- a/src/de/lmu/ifi/dbs/elki/database/ids/integer/TroveIteratorAdapter.java
+++ b/src/de/lmu/ifi/dbs/elki/database/datastore/DBIDDataStore.java
@@ -1,4 +1,4 @@
-package de.lmu.ifi.dbs.elki.database.ids.integer;
+package de.lmu.ifi.dbs.elki.database.datastore;
/*
This file is part of ELKI:
@@ -23,44 +23,30 @@ package de.lmu.ifi.dbs.elki.database.ids.integer;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import gnu.trove.iterator.TIntIterator;
-
-import java.util.Iterator;
-
import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDVar;
/**
- * Adapter for using GNU Trove iterators.
+ * DBID-valued data store (avoids boxing/unboxing).
*
* @author Erich Schubert
*/
-class TroveIteratorAdapter implements Iterator<DBID> {
+public interface DBIDDataStore extends DataStore<DBID> {
/**
- * The actual iterator.
+ * Getter, but using objects.
+ *
+ * @deprecated Use {@link #assignVar} and a {@link DBIDVar} instead, to avoid boxing/unboxing cost.
*/
- private TIntIterator iterator;
+ @Override
+ @Deprecated
+ public DBID get(DBIDRef id);
/**
- * Constructor.
+ * Retrieves an object from the storage.
*
- * @param iterator Trove iterator
+ * @param id Database ID.
+ * @param var Variable to update.
*/
- protected TroveIteratorAdapter(TIntIterator iterator) {
- this.iterator = iterator;
- }
-
- @Override
- public boolean hasNext() {
- return iterator.hasNext();
- }
-
- @Override
- public DBID next() {
- return new IntegerDBID(iterator.next());
- }
-
- @Override
- public void remove() {
- iterator.remove();
- }
-} \ No newline at end of file
+ public void assignVar(DBIDRef id, DBIDVar var);
+}
diff --git a/src/de/lmu/ifi/dbs/elki/database/datastore/DataStoreFactory.java b/src/de/lmu/ifi/dbs/elki/database/datastore/DataStoreFactory.java
index 2ed9f536..aca5b86f 100644
--- a/src/de/lmu/ifi/dbs/elki/database/datastore/DataStoreFactory.java
+++ b/src/de/lmu/ifi/dbs/elki/database/datastore/DataStoreFactory.java
@@ -46,27 +46,27 @@ public interface DataStoreFactory {
/**
* Storage will be used only temporary.
*/
- public final static int HINT_TEMP = 0x01;
+ public static final int HINT_TEMP = 0x01;
/**
* "Hot" data, that will be used a lot, preferring memory storage.
*/
- public final static int HINT_HOT = 0x02;
+ public static final int HINT_HOT = 0x02;
/**
* "static" data, that will not change often
*/
- public final static int HINT_STATIC = 0x04;
+ public static final int HINT_STATIC = 0x04;
/**
* Data that might require sorted access (so hashmaps are suboptimal)
*/
- public final static int HINT_SORTED = 0x08;
+ public static final int HINT_SORTED = 0x08;
/**
* Data that is the main database. Includes HOT, STATIC, SORTED
*/
- public final static int HINT_DB = 0x1E;
+ public static final int HINT_DB = 0x1E;
/**
* Make a new storage, to associate the given ids with an object of class
@@ -88,6 +88,16 @@ public interface DataStoreFactory {
* @param hints Hints for the storage manager
* @return new data store
*/
+ public WritableDBIDDataStore makeDBIDStorage(DBIDs ids, int hints);
+
+ /**
+ * Make a new storage, to associate the given ids with an object of class
+ * dataclass.
+ *
+ * @param ids DBIDs to store data for
+ * @param hints Hints for the storage manager
+ * @return new data store
+ */
public WritableDoubleDataStore makeDoubleStorage(DBIDs ids, int hints);
/**
diff --git a/src/de/lmu/ifi/dbs/elki/database/datastore/DataStoreIDMap.java b/src/de/lmu/ifi/dbs/elki/database/datastore/DataStoreIDMap.java
index dada881a..cba8e4c9 100644
--- a/src/de/lmu/ifi/dbs/elki/database/datastore/DataStoreIDMap.java
+++ b/src/de/lmu/ifi/dbs/elki/database/datastore/DataStoreIDMap.java
@@ -32,10 +32,10 @@ import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
*/
public interface DataStoreIDMap {
/**
- * Map a DBID to a database id.
+ * Map a DBID to an array offset.
*
* @param dbid DBID
* @return record id {@code id >= 0}
*/
- public int map(DBIDRef dbid);
+ int mapDBIDToOffset(DBIDRef dbid);
}
diff --git a/src/de/lmu/ifi/dbs/elki/database/datastore/DataStoreUtil.java b/src/de/lmu/ifi/dbs/elki/database/datastore/DataStoreUtil.java
index a8afeaec..a9052f87 100644
--- a/src/de/lmu/ifi/dbs/elki/database/datastore/DataStoreUtil.java
+++ b/src/de/lmu/ifi/dbs/elki/database/datastore/DataStoreUtil.java
@@ -26,16 +26,18 @@ package de.lmu.ifi.dbs.elki.database.datastore;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
/**
- * Storage utility class. Mostly a shorthand for {@link DataStoreFactory#FACTORY}.
+ * Storage utility class. Mostly a shorthand for
+ * {@link DataStoreFactory#FACTORY}.
*
* @author Erich Schubert
- *
+ *
* @apiviz.landmark
* @apiviz.composedOf de.lmu.ifi.dbs.elki.database.datastore.DataStoreFactory
*/
public final class DataStoreUtil {
/**
- * Make a new storage, to associate the given ids with an object of class dataclass.
+ * Make a new storage, to associate the given ids with an object of class
+ * dataclass.
*
* @param <T> stored data type
* @param ids DBIDs to store data for
@@ -46,9 +48,22 @@ public final class DataStoreUtil {
public static <T> WritableDataStore<T> makeStorage(DBIDs ids, int hints, Class<? super T> dataclass) {
return DataStoreFactory.FACTORY.makeStorage(ids, hints, dataclass);
}
-
+
+ /**
+ * Make a new storage, to associate the given ids with an object of class
+ * dataclass.
+ *
+ * @param ids DBIDs to store data for
+ * @param hints Hints for the storage manager
+ * @return new data store
+ */
+ public static WritableDBIDDataStore makeDBIDStorage(DBIDs ids, int hints) {
+ return DataStoreFactory.FACTORY.makeDBIDStorage(ids, hints);
+ }
+
/**
- * Make a new storage, to associate the given ids with an object of class dataclass.
+ * Make a new storage, to associate the given ids with an object of class
+ * dataclass.
*
* @param ids DBIDs to store data for
* @param hints Hints for the storage manager
@@ -57,9 +72,10 @@ public final class DataStoreUtil {
public static WritableDoubleDataStore makeDoubleStorage(DBIDs ids, int hints) {
return DataStoreFactory.FACTORY.makeDoubleStorage(ids, hints);
}
-
+
/**
- * Make a new storage, to associate the given ids with an object of class dataclass.
+ * Make a new storage, to associate the given ids with an object of class
+ * dataclass.
*
* @param ids DBIDs to store data for
* @param hints Hints for the storage manager
@@ -69,9 +85,10 @@ public final class DataStoreUtil {
public static WritableDoubleDataStore makeDoubleStorage(DBIDs ids, int hints, double def) {
return DataStoreFactory.FACTORY.makeDoubleStorage(ids, hints, def);
}
-
+
/**
- * Make a new storage, to associate the given ids with an object of class dataclass.
+ * Make a new storage, to associate the given ids with an object of class
+ * dataclass.
*
* @param ids DBIDs to store data for
* @param hints Hints for the storage manager
@@ -80,9 +97,10 @@ public final class DataStoreUtil {
public static WritableIntegerDataStore makeIntegerStorage(DBIDs ids, int hints) {
return DataStoreFactory.FACTORY.makeIntegerStorage(ids, hints);
}
-
+
/**
- * Make a new storage, to associate the given ids with an object of class dataclass.
+ * Make a new storage, to associate the given ids with an object of class
+ * dataclass.
*
* @param ids DBIDs to store data for
* @param hints Hints for the storage manager
@@ -92,9 +110,10 @@ public final class DataStoreUtil {
public static WritableIntegerDataStore makeIntegerStorage(DBIDs ids, int hints, int def) {
return DataStoreFactory.FACTORY.makeIntegerStorage(ids, hints, def);
}
-
+
/**
- * Make a new record storage, to associate the given ids with an object of class dataclass.
+ * Make a new record storage, to associate the given ids with an object of
+ * class dataclass.
*
* @param ids DBIDs to store data for
* @param hints Hints for the storage manager
@@ -104,4 +123,4 @@ public final class DataStoreUtil {
public static WritableRecordStore makeRecordStorage(DBIDs ids, int hints, Class<?>... dataclasses) {
return DataStoreFactory.FACTORY.makeRecordStorage(ids, hints, dataclasses);
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/database/datastore/RangeIDMap.java b/src/de/lmu/ifi/dbs/elki/database/datastore/DoubleDistanceDataStore.java
index e00f9ff8..f74b681a 100644
--- a/src/de/lmu/ifi/dbs/elki/database/datastore/RangeIDMap.java
+++ b/src/de/lmu/ifi/dbs/elki/database/datastore/DoubleDistanceDataStore.java
@@ -23,31 +23,29 @@ package de.lmu.ifi.dbs.elki.database.datastore;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import de.lmu.ifi.dbs.elki.database.ids.DBIDRange;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
/**
- * Mapping a static DBID range to storage IDs.
+ * Double-valued data store (avoids boxing/unboxing).
*
* @author Erich Schubert
*/
-public class RangeIDMap implements DataStoreIDMap {
+public interface DoubleDistanceDataStore extends DataStore<DoubleDistance> {
/**
- * Start offset
+ * Getter, but using objects.
+ *
+ * @deprecated Use {@link #doubleValue} instead, to avoid boxing/unboxing cost.
*/
- final DBIDRange range;
-
+ @Override
+ @Deprecated
+ public DoubleDistance get(DBIDRef id);
+
/**
- * Constructor from a static DBID range allocation.
+ * Retrieves an object from the storage.
*
- * @param range DBID range to use
+ * @param id Database ID.
+ * @return Double value
*/
- public RangeIDMap(DBIDRange range) {
- this.range = range;
- }
-
- @Override
- public int map(DBIDRef dbid) {
- return range.getOffset(dbid);
- }
-}
+ public double doubleValue(DBIDRef id);
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/database/query/DistanceResultPair.java b/src/de/lmu/ifi/dbs/elki/database/datastore/WritableDBIDDataStore.java
index b6cc129b..960b79ac 100644
--- a/src/de/lmu/ifi/dbs/elki/database/query/DistanceResultPair.java
+++ b/src/de/lmu/ifi/dbs/elki/database/datastore/WritableDBIDDataStore.java
@@ -1,4 +1,4 @@
-package de.lmu.ifi.dbs.elki.database.query;
+package de.lmu.ifi.dbs.elki.database.datastore;
/*
This file is part of ELKI:
@@ -25,44 +25,39 @@ package de.lmu.ifi.dbs.elki.database.query;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
-import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
-import de.lmu.ifi.dbs.elki.utilities.pairs.PairInterface;
/**
- * Class that consists of a pair (distance, object ID) commonly returned for kNN
- * and range queries.
+ * Data store specialized for doubles. Avoids boxing/unboxing.
*
* @author Erich Schubert
- *
- * @param <D> Distance type
*/
-public interface DistanceResultPair<D extends Distance<?>> extends PairInterface<D, DBID>, Comparable<DistanceResultPair<D>>, DBIDRef {
- /**
- * Getter for first
- *
- * @return first element in pair
- */
- public D getDistance();
-
+public interface WritableDBIDDataStore extends DBIDDataStore, WritableDataStore<DBID> {
/**
- * Setter for first
+ * Setter, but using materialized DBIDs.
*
- * @param first new value for first element
+ * @deprecated Use {@link #putDBID} instead, to avoid boxing/unboxing cost.
*/
- public void setDistance(D first);
+ @Override
+ @Deprecated
+ public DBID put(DBIDRef id, DBID value);
/**
- * Setter for second
+ * Associates the specified value with the specified id in this storage. If
+ * the storage previously contained a value for the id, the previous value is
+ * replaced by the specified value.
*
- * @param second new value for second element
+ * @param id Database ID.
+ * @param value Value to store.
*/
- public void setID(DBID second);
+ public void putDBID(DBIDRef id, DBIDRef value);
/**
- * Compare value, but by distance only.
+ * Associates the specified value with the specified id in this storage. If
+ * the storage previously contained a value for the id, the previous value is
+ * replaced by the specified value.
*
- * @param o Other object
- * @return comparison result, as by Double.compare(this, other)
+ * @param id Database ID.
+ * @param value Value to store.
*/
- public int compareByDistance(DistanceResultPair<D> o);
+ public void put(DBIDRef id, DBIDRef value);
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/database/datastore/WritableDoubleDistanceDataStore.java b/src/de/lmu/ifi/dbs/elki/database/datastore/WritableDoubleDistanceDataStore.java
new file mode 100644
index 00000000..03a3e75e
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/database/datastore/WritableDoubleDistanceDataStore.java
@@ -0,0 +1,65 @@
+package de.lmu.ifi.dbs.elki.database.datastore;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
+
+/**
+ * Data store specialized for doubles. Avoids boxing/unboxing.
+ *
+ * @author Erich Schubert
+ */
+public interface WritableDoubleDistanceDataStore extends DoubleDistanceDataStore, WritableDataStore<DoubleDistance> {
+ /**
+ * Setter, but using objects.
+ *
+ * @deprecated Use {@link #putDouble} instead, to avoid boxing/unboxing cost.
+ */
+ @Override
+ @Deprecated
+ public DoubleDistance put(DBIDRef id, DoubleDistance value);
+
+ /**
+ * Associates the specified value with the specified id in this storage. If
+ * the storage previously contained a value for the id, the previous value is
+ * replaced by the specified value.
+ *
+ * @param id Database ID.
+ * @param value Value to store.
+ * @return previous value
+ */
+ public double putDouble(DBIDRef id, double value);
+
+ /**
+ * Associates the specified value with the specified id in this storage. If
+ * the storage previously contained a value for the id, the previous value is
+ * replaced by the specified value.
+ *
+ * @param id Database ID.
+ * @param value Value to store.
+ * @return previous value
+ */
+ public double put(DBIDRef id, double value);
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/database/datastore/memory/ArrayDBIDStore.java b/src/de/lmu/ifi/dbs/elki/database/datastore/memory/ArrayDBIDStore.java
new file mode 100644
index 00000000..c9bc6438
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/database/datastore/memory/ArrayDBIDStore.java
@@ -0,0 +1,125 @@
+package de.lmu.ifi.dbs.elki.database.datastore.memory;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import de.lmu.ifi.dbs.elki.database.datastore.DataStoreIDMap;
+import de.lmu.ifi.dbs.elki.database.datastore.WritableDBIDDataStore;
+import de.lmu.ifi.dbs.elki.database.ids.ArrayModifiableDBIDs;
+import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDVar;
+
+/**
+ * A class to answer representation queries using the stored Array.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.composedOf de.lmu.ifi.dbs.elki.database.datastore.DataStoreIDMap
+ */
+public class ArrayDBIDStore implements WritableDBIDDataStore {
+ /**
+ * Data array
+ */
+ private ArrayModifiableDBIDs data;
+
+ /**
+ * DBID to index map
+ */
+ private DataStoreIDMap idmap;
+
+ /**
+ * Constructor.
+ *
+ * @param size Size
+ * @param idmap ID map
+ */
+ public ArrayDBIDStore(int size, DataStoreIDMap idmap) {
+ super();
+ this.data = DBIDUtil.newArray(size);
+ // Initialize
+ DBIDRef inv = DBIDUtil.invalid();
+ for (int i = 0; i < size; i++) {
+ data.add(inv);
+ }
+ this.idmap = idmap;
+ }
+
+ @Override
+ @Deprecated
+ public DBID get(DBIDRef id) {
+ try {
+ return data.get(idmap.mapDBIDToOffset(id));
+ } catch (ArrayIndexOutOfBoundsException e) {
+ return null;
+ }
+ }
+
+ @Override
+ public void assignVar(DBIDRef id, DBIDVar var) {
+ data.assign(idmap.mapDBIDToOffset(id), var);
+ }
+
+ @Override
+ @Deprecated
+ public DBID put(DBIDRef id, DBID value) {
+ final int off = idmap.mapDBIDToOffset(id);
+ DBID ret = data.get(off);
+ data.set(off, value);
+ return ret;
+ }
+
+ @Override
+ public void putDBID(DBIDRef id, DBIDRef value) {
+ final int off = idmap.mapDBIDToOffset(id);
+ data.set(off, value);
+ }
+
+ @Override
+ public void put(DBIDRef id, DBIDRef value) {
+ final int off = idmap.mapDBIDToOffset(id);
+ data.set(off, value);
+ }
+
+ @Override
+ public void destroy() {
+ data = null;
+ idmap = null;
+ }
+
+ @Override
+ public void delete(DBIDRef id) {
+ throw new UnsupportedOperationException("Can't delete from a static array storage.");
+ }
+
+ @Override
+ public String getLongName() {
+ return "raw";
+ }
+
+ @Override
+ public String getShortName() {
+ return "raw";
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/database/datastore/memory/ArrayDoubleDistanceStore.java b/src/de/lmu/ifi/dbs/elki/database/datastore/memory/ArrayDoubleDistanceStore.java
new file mode 100644
index 00000000..99ec1382
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/database/datastore/memory/ArrayDoubleDistanceStore.java
@@ -0,0 +1,138 @@
+package de.lmu.ifi.dbs.elki.database.datastore.memory;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.Arrays;
+
+import de.lmu.ifi.dbs.elki.database.datastore.DataStoreIDMap;
+import de.lmu.ifi.dbs.elki.database.datastore.WritableDoubleDistanceDataStore;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
+
+/**
+ * A class to answer representation queries using the stored Array.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.composedOf de.lmu.ifi.dbs.elki.database.datastore.DataStoreIDMap
+ */
+public class ArrayDoubleDistanceStore implements WritableDoubleDistanceDataStore {
+ /**
+ * Data array
+ */
+ private double[] data;
+
+ /**
+ * DBID to index map
+ */
+ private DataStoreIDMap idmap;
+
+ /**
+ * Constructor.
+ *
+ * @param size Size
+ * @param idmap ID map
+ */
+ public ArrayDoubleDistanceStore(int size, DataStoreIDMap idmap) {
+ this(size, idmap, Double.NaN);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param size Size
+ * @param idmap ID map
+ * @param def Default value
+ */
+ public ArrayDoubleDistanceStore(int size, DataStoreIDMap idmap, double def) {
+ super();
+ this.data = new double[size];
+ if(def != 0) {
+ Arrays.fill(this.data, def);
+ }
+ this.idmap = idmap;
+ }
+
+ @Override
+ @Deprecated
+ public DoubleDistance get(DBIDRef id) {
+ try {
+ return new DoubleDistance(data[idmap.mapDBIDToOffset(id)]);
+ }
+ catch(ArrayIndexOutOfBoundsException e) {
+ return null;
+ }
+ }
+
+ @Override
+ @Deprecated
+ public DoubleDistance put(DBIDRef id, DoubleDistance value) {
+ final int off = idmap.mapDBIDToOffset(id);
+ double ret = data[off];
+ data[off] = value.doubleValue();
+ return new DoubleDistance(ret);
+ }
+
+ @Override
+ public double doubleValue(DBIDRef id) {
+ return data[idmap.mapDBIDToOffset(id)];
+ }
+
+ @Override
+ public double putDouble(DBIDRef id, double value) {
+ final int off = idmap.mapDBIDToOffset(id);
+ final double ret = data[off];
+ data[off] = value;
+ return ret;
+ }
+
+ @Override
+ public double put(DBIDRef id, double value) {
+ final int off = idmap.mapDBIDToOffset(id);
+ final double ret = data[off];
+ data[off] = value;
+ return ret;
+ }
+
+ @Override
+ public void destroy() {
+ data = null;
+ idmap = null;
+ }
+
+ @Override
+ public void delete(DBIDRef id) {
+ throw new UnsupportedOperationException("Can't delete from a static array storage.");
+ }
+
+ @Override
+ public String getLongName() {
+ return "raw";
+ }
+
+ @Override
+ public String getShortName() {
+ return "raw";
+ }
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/database/datastore/memory/ArrayDoubleStore.java b/src/de/lmu/ifi/dbs/elki/database/datastore/memory/ArrayDoubleStore.java
index de22a6b3..1d8920bf 100644
--- a/src/de/lmu/ifi/dbs/elki/database/datastore/memory/ArrayDoubleStore.java
+++ b/src/de/lmu/ifi/dbs/elki/database/datastore/memory/ArrayDoubleStore.java
@@ -77,7 +77,7 @@ public class ArrayDoubleStore implements WritableDoubleDataStore {
@Deprecated
public Double get(DBIDRef id) {
try {
- return data[idmap.map(id)];
+ return Double.valueOf(data[idmap.mapDBIDToOffset(id)]);
}
catch(ArrayIndexOutOfBoundsException e) {
return null;
@@ -87,20 +87,20 @@ public class ArrayDoubleStore implements WritableDoubleDataStore {
@Override
@Deprecated
public Double put(DBIDRef id, Double value) {
- final int off = idmap.map(id);
+ final int off = idmap.mapDBIDToOffset(id);
double ret = data[off];
- data[off] = value;
- return ret;
+ data[off] = value.doubleValue();
+ return Double.valueOf(ret);
}
@Override
public double doubleValue(DBIDRef id) {
- return data[idmap.map(id)];
+ return data[idmap.mapDBIDToOffset(id)];
}
@Override
public double putDouble(DBIDRef id, double value) {
- final int off = idmap.map(id);
+ final int off = idmap.mapDBIDToOffset(id);
final double ret = data[off];
data[off] = value;
return ret;
@@ -108,7 +108,7 @@ public class ArrayDoubleStore implements WritableDoubleDataStore {
@Override
public double put(DBIDRef id, double value) {
- final int off = idmap.map(id);
+ final int off = idmap.mapDBIDToOffset(id);
final double ret = data[off];
data[off] = value;
return ret;
diff --git a/src/de/lmu/ifi/dbs/elki/database/datastore/memory/ArrayIntegerStore.java b/src/de/lmu/ifi/dbs/elki/database/datastore/memory/ArrayIntegerStore.java
index 8caa7ec3..bb250164 100644
--- a/src/de/lmu/ifi/dbs/elki/database/datastore/memory/ArrayIntegerStore.java
+++ b/src/de/lmu/ifi/dbs/elki/database/datastore/memory/ArrayIntegerStore.java
@@ -77,7 +77,7 @@ public class ArrayIntegerStore implements WritableIntegerDataStore {
@Deprecated
public Integer get(DBIDRef id) {
try {
- return data[idmap.map(id)];
+ return Integer.valueOf(data[idmap.mapDBIDToOffset(id)]);
}
catch(ArrayIndexOutOfBoundsException e) {
return null;
@@ -87,20 +87,20 @@ public class ArrayIntegerStore implements WritableIntegerDataStore {
@Override
@Deprecated
public Integer put(DBIDRef id, Integer value) {
- final int off = idmap.map(id);
+ final int off = idmap.mapDBIDToOffset(id);
int ret = data[off];
- data[off] = value;
- return ret;
+ data[off] = value.intValue();
+ return Integer.valueOf(ret);
}
@Override
public int intValue(DBIDRef id) {
- return data[idmap.map(id)];
+ return data[idmap.mapDBIDToOffset(id)];
}
@Override
public int putInt(DBIDRef id, int value) {
- final int off = idmap.map(id);
+ final int off = idmap.mapDBIDToOffset(id);
final int ret = data[off];
data[off] = value;
return ret;
@@ -108,7 +108,7 @@ public class ArrayIntegerStore implements WritableIntegerDataStore {
@Override
public int put(DBIDRef id, int value) {
- final int off = idmap.map(id);
+ final int off = idmap.mapDBIDToOffset(id);
final int ret = data[off];
data[off] = value;
return ret;
diff --git a/src/de/lmu/ifi/dbs/elki/database/datastore/memory/ArrayRecordStore.java b/src/de/lmu/ifi/dbs/elki/database/datastore/memory/ArrayRecordStore.java
index 6e578b61..4dd9a684 100644
--- a/src/de/lmu/ifi/dbs/elki/database/datastore/memory/ArrayRecordStore.java
+++ b/src/de/lmu/ifi/dbs/elki/database/datastore/memory/ArrayRecordStore.java
@@ -75,7 +75,7 @@ public class ArrayRecordStore implements WritableRecordStore {
@SuppressWarnings("unchecked")
protected <T> T get(DBIDRef id, int index) {
try {
- return (T) data[idmap.map(id)][index];
+ return (T) data[idmap.mapDBIDToOffset(id)][index];
}
catch(ArrayIndexOutOfBoundsException e) {
return null;
@@ -98,8 +98,8 @@ public class ArrayRecordStore implements WritableRecordStore {
*/
@SuppressWarnings("unchecked")
protected <T> T set(DBIDRef id, int index, T value) {
- T ret = (T) data[idmap.map(id)][index];
- data[idmap.map(id)][index] = value;
+ T ret = (T) data[idmap.mapDBIDToOffset(id)][index];
+ data[idmap.mapDBIDToOffset(id)][index] = value;
return ret;
}
diff --git a/src/de/lmu/ifi/dbs/elki/database/datastore/memory/ArrayStore.java b/src/de/lmu/ifi/dbs/elki/database/datastore/memory/ArrayStore.java
index a7ce310b..29658818 100644
--- a/src/de/lmu/ifi/dbs/elki/database/datastore/memory/ArrayStore.java
+++ b/src/de/lmu/ifi/dbs/elki/database/datastore/memory/ArrayStore.java
@@ -38,17 +38,20 @@ import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
*/
public class ArrayStore<T> implements WritableDataStore<T> {
/**
- * Data array
+ * Data array.
*/
private Object[] data;
/**
- * DBID to index map
+ * DBID to index map.
*/
private DataStoreIDMap idmap;
/**
* Constructor.
+ *
+ * @param data Data array
+ * @param idmap DBID to offset mapping
*/
public ArrayStore(Object[] data, DataStoreIDMap idmap) {
super();
@@ -60,7 +63,7 @@ public class ArrayStore<T> implements WritableDataStore<T> {
@Override
public T get(DBIDRef id) {
try {
- return (T) data[idmap.map(id)];
+ return (T) data[idmap.mapDBIDToOffset(id)];
}
catch(ArrayIndexOutOfBoundsException e) {
return null;
@@ -76,7 +79,7 @@ public class ArrayStore<T> implements WritableDataStore<T> {
@Override
public T put(DBIDRef id, T value) {
T ret = get(id);
- data[idmap.map(id)] = value;
+ data[idmap.mapDBIDToOffset(id)] = value;
return ret;
}
diff --git a/src/de/lmu/ifi/dbs/elki/database/datastore/memory/MapIntegerDBIDDBIDStore.java b/src/de/lmu/ifi/dbs/elki/database/datastore/memory/MapIntegerDBIDDBIDStore.java
new file mode 100644
index 00000000..565d0848
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/database/datastore/memory/MapIntegerDBIDDBIDStore.java
@@ -0,0 +1,104 @@
+package de.lmu.ifi.dbs.elki.database.datastore.memory;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import gnu.trove.map.TIntIntMap;
+import gnu.trove.map.hash.TIntIntHashMap;
+import de.lmu.ifi.dbs.elki.database.datastore.WritableDBIDDataStore;
+import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDFactory;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDVar;
+
+/**
+ * Writable data store for double values.
+ *
+ * @author Erich Schubert
+ */
+public class MapIntegerDBIDDBIDStore implements WritableDBIDDataStore {
+ /**
+ * Data storage.
+ */
+ private TIntIntMap map;
+
+ /**
+ * Constructor.
+ *
+ * @param size Expected size
+ */
+ public MapIntegerDBIDDBIDStore(int size) {
+ super();
+ map = new TIntIntHashMap(size, 0.5f, Integer.MIN_VALUE, DBIDUtil.invalid().internalGetIndex());
+ }
+
+ @Override
+ @Deprecated
+ public DBID get(DBIDRef id) {
+ return DBIDUtil.importInteger(map.get(DBIDUtil.asInteger(id)));
+ }
+
+ @Override
+ public String getLongName() {
+ return "raw";
+ }
+
+ @Override
+ public String getShortName() {
+ return "raw";
+ }
+
+ @Override
+ @Deprecated
+ public DBID put(DBIDRef id, DBID value) {
+ return DBIDUtil.importInteger(map.put(id.internalGetIndex(), value.internalGetIndex()));
+ }
+
+ @Override
+ public void destroy() {
+ map.clear();
+ map = null;
+ }
+
+ @Override
+ public void delete(DBIDRef id) {
+ map.remove(DBIDUtil.asInteger(id));
+ }
+
+ @Override
+ public void put(DBIDRef id, DBIDRef value) {
+ map.put(id.internalGetIndex(), value.internalGetIndex());
+ }
+
+ @Override
+ public void putDBID(DBIDRef id, DBIDRef value) {
+ map.put(id.internalGetIndex(), value.internalGetIndex());
+ }
+
+ @Override
+ public void assignVar(DBIDRef id, DBIDVar var) {
+ final int val = map.get(id.internalGetIndex());
+ DBIDFactory.FACTORY.assignVar(var, val);
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/database/datastore/memory/MapIntegerDBIDDoubleDistanceStore.java b/src/de/lmu/ifi/dbs/elki/database/datastore/memory/MapIntegerDBIDDoubleDistanceStore.java
new file mode 100644
index 00000000..82570639
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/database/datastore/memory/MapIntegerDBIDDoubleDistanceStore.java
@@ -0,0 +1,111 @@
+package de.lmu.ifi.dbs.elki.database.datastore.memory;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import gnu.trove.map.TIntDoubleMap;
+import gnu.trove.map.hash.TIntDoubleHashMap;
+import de.lmu.ifi.dbs.elki.database.datastore.WritableDoubleDistanceDataStore;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
+import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
+
+/**
+ * Writable data store for double values.
+ *
+ * @author Erich Schubert
+ */
+public class MapIntegerDBIDDoubleDistanceStore implements WritableDoubleDistanceDataStore {
+ /**
+ * Data storage.
+ */
+ private TIntDoubleMap map;
+
+ /**
+ * Constructor.
+ *
+ * @param size Expected size
+ */
+ public MapIntegerDBIDDoubleDistanceStore(int size) {
+ this(size, Double.NaN);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param size Expected size
+ * @param def Default value
+ */
+ public MapIntegerDBIDDoubleDistanceStore(int size, double def) {
+ super();
+ map = new TIntDoubleHashMap(size, 0.5f, Integer.MIN_VALUE, def);
+ }
+
+ @Override
+ @Deprecated
+ public DoubleDistance get(DBIDRef id) {
+ return new DoubleDistance(map.get(DBIDUtil.asInteger(id)));
+ }
+
+ @Override
+ public double doubleValue(DBIDRef id) {
+ return map.get(DBIDUtil.asInteger(id));
+ }
+
+ @Override
+ public String getLongName() {
+ return "raw";
+ }
+
+ @Override
+ public String getShortName() {
+ return "raw";
+ }
+
+ @Override
+ @Deprecated
+ public DoubleDistance put(DBIDRef id, DoubleDistance value) {
+ return new DoubleDistance(map.put(DBIDUtil.asInteger(id), value.doubleValue()));
+ }
+
+ @Override
+ public void destroy() {
+ map.clear();
+ map = null;
+ }
+
+ @Override
+ public void delete(DBIDRef id) {
+ map.remove(DBIDUtil.asInteger(id));
+ }
+
+ @Override
+ public double putDouble(DBIDRef id, double value) {
+ return map.put(DBIDUtil.asInteger(id), value);
+ }
+
+ @Override
+ public double put(DBIDRef id, double value) {
+ return map.put(DBIDUtil.asInteger(id), value);
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/database/datastore/memory/MapIntegerDBIDDoubleStore.java b/src/de/lmu/ifi/dbs/elki/database/datastore/memory/MapIntegerDBIDDoubleStore.java
index f9f8d48a..944f9710 100644
--- a/src/de/lmu/ifi/dbs/elki/database/datastore/memory/MapIntegerDBIDDoubleStore.java
+++ b/src/de/lmu/ifi/dbs/elki/database/datastore/memory/MapIntegerDBIDDoubleStore.java
@@ -27,6 +27,7 @@ import gnu.trove.map.TIntDoubleMap;
import gnu.trove.map.hash.TIntDoubleHashMap;
import de.lmu.ifi.dbs.elki.database.datastore.WritableDoubleDataStore;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
/**
* Writable data store for double values.
@@ -35,7 +36,7 @@ import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
*/
public class MapIntegerDBIDDoubleStore implements WritableDoubleDataStore {
/**
- * Data storage
+ * Data storage.
*/
private TIntDoubleMap map;
@@ -62,12 +63,12 @@ public class MapIntegerDBIDDoubleStore implements WritableDoubleDataStore {
@Override
@Deprecated
public Double get(DBIDRef id) {
- return map.get(id.getIntegerID());
+ return Double.valueOf(map.get(DBIDUtil.asInteger(id)));
}
@Override
public double doubleValue(DBIDRef id) {
- return map.get(id.getIntegerID());
+ return map.get(DBIDUtil.asInteger(id));
}
@Override
@@ -83,7 +84,7 @@ public class MapIntegerDBIDDoubleStore implements WritableDoubleDataStore {
@Override
@Deprecated
public Double put(DBIDRef id, Double value) {
- return map.put(id.getIntegerID(), value);
+ return Double.valueOf(map.put(DBIDUtil.asInteger(id), value.doubleValue()));
}
@Override
@@ -94,16 +95,16 @@ public class MapIntegerDBIDDoubleStore implements WritableDoubleDataStore {
@Override
public void delete(DBIDRef id) {
- map.remove(id.getIntegerID());
+ map.remove(DBIDUtil.asInteger(id));
}
@Override
public double putDouble(DBIDRef id, double value) {
- return map.put(id.getIntegerID(), value);
+ return map.put(DBIDUtil.asInteger(id), value);
}
@Override
public double put(DBIDRef id, double value) {
- return map.put(id.getIntegerID(), value);
+ return map.put(DBIDUtil.asInteger(id), value);
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/database/datastore/memory/MapIntegerDBIDIntegerStore.java b/src/de/lmu/ifi/dbs/elki/database/datastore/memory/MapIntegerDBIDIntegerStore.java
index f7aea633..f31c97f9 100644
--- a/src/de/lmu/ifi/dbs/elki/database/datastore/memory/MapIntegerDBIDIntegerStore.java
+++ b/src/de/lmu/ifi/dbs/elki/database/datastore/memory/MapIntegerDBIDIntegerStore.java
@@ -27,6 +27,7 @@ import gnu.trove.map.TIntIntMap;
import gnu.trove.map.hash.TIntIntHashMap;
import de.lmu.ifi.dbs.elki.database.datastore.WritableIntegerDataStore;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
/**
* Writable data store for double values.
@@ -35,7 +36,7 @@ import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
*/
public class MapIntegerDBIDIntegerStore implements WritableIntegerDataStore {
/**
- * Data storage
+ * Data storage.
*/
private TIntIntMap map;
@@ -62,12 +63,12 @@ public class MapIntegerDBIDIntegerStore implements WritableIntegerDataStore {
@Override
@Deprecated
public Integer get(DBIDRef id) {
- return map.get(id.getIntegerID());
+ return Integer.valueOf(map.get(DBIDUtil.asInteger(id)));
}
@Override
public int intValue(DBIDRef id) {
- return map.get(id.getIntegerID());
+ return map.get(DBIDUtil.asInteger(id));
}
@Override
@@ -83,7 +84,7 @@ public class MapIntegerDBIDIntegerStore implements WritableIntegerDataStore {
@Override
@Deprecated
public Integer put(DBIDRef id, Integer value) {
- return map.put(id.getIntegerID(), value);
+ return Integer.valueOf(map.put(DBIDUtil.asInteger(id), value.intValue()));
}
@Override
@@ -94,16 +95,16 @@ public class MapIntegerDBIDIntegerStore implements WritableIntegerDataStore {
@Override
public void delete(DBIDRef id) {
- map.remove(id.getIntegerID());
+ map.remove(DBIDUtil.asInteger(id));
}
@Override
public int putInt(DBIDRef id, int value) {
- return map.put(id.getIntegerID(), value);
+ return map.put(DBIDUtil.asInteger(id), value);
}
@Override
public int put(DBIDRef id, int value) {
- return map.put(id.getIntegerID(), value);
+ return map.put(DBIDUtil.asInteger(id), value);
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/database/datastore/memory/MapIntegerDBIDRecordStore.java b/src/de/lmu/ifi/dbs/elki/database/datastore/memory/MapIntegerDBIDRecordStore.java
index 805c6de3..2926f14c 100644
--- a/src/de/lmu/ifi/dbs/elki/database/datastore/memory/MapIntegerDBIDRecordStore.java
+++ b/src/de/lmu/ifi/dbs/elki/database/datastore/memory/MapIntegerDBIDRecordStore.java
@@ -28,6 +28,7 @@ import gnu.trove.map.hash.TIntObjectHashMap;
import de.lmu.ifi.dbs.elki.database.datastore.WritableDataStore;
import de.lmu.ifi.dbs.elki.database.datastore.WritableRecordStore;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
/**
* A class to answer representation queries using a map and an index within the
@@ -39,12 +40,12 @@ import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
*/
public class MapIntegerDBIDRecordStore implements WritableRecordStore {
/**
- * Record length
+ * Record length.
*/
private final int rlen;
/**
- * Storage Map
+ * Storage Map.
*/
private final TIntObjectMap<Object[]> data;
@@ -86,15 +87,16 @@ public class MapIntegerDBIDRecordStore implements WritableRecordStore {
}
/**
- * Actual getter
+ * Actual getter.
*
* @param id Database ID
* @param index column index
+ * @param <T> type
* @return current value
*/
@SuppressWarnings("unchecked")
protected <T> T get(DBIDRef id, int index) {
- Object[] d = data.get(id.getIntegerID());
+ Object[] d = data.get(DBIDUtil.asInteger(id));
if(d == null) {
return null;
}
@@ -110,19 +112,20 @@ public class MapIntegerDBIDRecordStore implements WritableRecordStore {
}
/**
- * Actual setter
+ * Actual setter.
*
* @param id Database ID
* @param index column index
* @param value new value
+ * @param <T> type
* @return previous value
*/
@SuppressWarnings("unchecked")
protected <T> T set(DBIDRef id, int index, T value) {
- Object[] d = data.get(id.getIntegerID());
+ Object[] d = data.get(DBIDUtil.asInteger(id));
if(d == null) {
d = new Object[rlen];
- data.put(id.getIntegerID(), d);
+ data.put(DBIDUtil.asInteger(id), d);
}
T ret = (T) d[index];
d[index] = value;
@@ -186,6 +189,6 @@ public class MapIntegerDBIDRecordStore implements WritableRecordStore {
@Override
public boolean remove(DBIDRef id) {
- return data.remove(id.getIntegerID()) != null;
+ return data.remove(DBIDUtil.asInteger(id)) != null;
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/database/datastore/memory/MapIntegerDBIDStore.java b/src/de/lmu/ifi/dbs/elki/database/datastore/memory/MapIntegerDBIDStore.java
index e04027d0..236389e4 100644
--- a/src/de/lmu/ifi/dbs/elki/database/datastore/memory/MapIntegerDBIDStore.java
+++ b/src/de/lmu/ifi/dbs/elki/database/datastore/memory/MapIntegerDBIDStore.java
@@ -27,6 +27,7 @@ import gnu.trove.map.TIntObjectMap;
import gnu.trove.map.hash.TIntObjectHashMap;
import de.lmu.ifi.dbs.elki.database.datastore.WritableDataStore;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
/**
* A class to answer representation queries using a map. Basically, it is just a
@@ -38,7 +39,7 @@ import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
*/
public class MapIntegerDBIDStore<T> implements WritableDataStore<T> {
/**
- * Storage Map
+ * Storage Map.
*/
private TIntObjectMap<T> data;
@@ -71,15 +72,15 @@ public class MapIntegerDBIDStore<T> implements WritableDataStore<T> {
@Override
public T get(DBIDRef id) {
- return data.get(id.getIntegerID());
+ return data.get(DBIDUtil.asInteger(id));
}
@Override
public T put(DBIDRef id, T value) {
if(value == null) {
- return data.remove(id.getIntegerID());
+ return data.remove(DBIDUtil.asInteger(id));
}
- return data.put(id.getIntegerID(), value);
+ return data.put(DBIDUtil.asInteger(id), value);
}
@Override
@@ -89,7 +90,7 @@ public class MapIntegerDBIDStore<T> implements WritableDataStore<T> {
@Override
public void delete(DBIDRef id) {
- data.remove(id.getIntegerID());
+ data.remove(DBIDUtil.asInteger(id));
}
@Override
diff --git a/src/de/lmu/ifi/dbs/elki/database/datastore/memory/MapRecordStore.java b/src/de/lmu/ifi/dbs/elki/database/datastore/memory/MapRecordStore.java
index 05cf3697..20778327 100644
--- a/src/de/lmu/ifi/dbs/elki/database/datastore/memory/MapRecordStore.java
+++ b/src/de/lmu/ifi/dbs/elki/database/datastore/memory/MapRecordStore.java
@@ -30,6 +30,7 @@ import de.lmu.ifi.dbs.elki.database.datastore.WritableDataStore;
import de.lmu.ifi.dbs.elki.database.datastore.WritableRecordStore;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
/**
* A class to answer representation queries using a map and an index within the
@@ -41,12 +42,12 @@ import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
*/
public class MapRecordStore implements WritableRecordStore {
/**
- * Record length
+ * Record length.
*/
private final int rlen;
/**
- * Storage Map
+ * Storage Map.
*/
// TODO: Use trove maps?
private final Map<DBID, Object[]> data;
@@ -79,15 +80,16 @@ public class MapRecordStore implements WritableRecordStore {
}
/**
- * Actual getter
+ * Actual getter.
*
* @param id Database ID
* @param index column index
+ * @param <T> type
* @return current value
*/
@SuppressWarnings("unchecked")
protected <T> T get(DBIDRef id, int index) {
- Object[] d = data.get(id.getDBID());
+ Object[] d = data.get(DBIDUtil.deref(id));
if(d == null) {
return null;
}
@@ -103,19 +105,20 @@ public class MapRecordStore implements WritableRecordStore {
}
/**
- * Actual setter
+ * Actual setter.
*
* @param id Database ID
* @param index column index
* @param value new value
+ * @param <T> type
* @return previous value
*/
@SuppressWarnings("unchecked")
protected <T> T set(DBIDRef id, int index, T value) {
- Object[] d = data.get(id.getDBID());
+ Object[] d = data.get(DBIDUtil.deref(id));
if(d == null) {
d = new Object[rlen];
- data.put(id.getDBID(), d);
+ data.put(DBIDUtil.deref(id), d);
}
T ret = (T) d[index];
d[index] = value;
diff --git a/src/de/lmu/ifi/dbs/elki/database/datastore/memory/MapStore.java b/src/de/lmu/ifi/dbs/elki/database/datastore/memory/MapStore.java
index 90742993..9818afd2 100644
--- a/src/de/lmu/ifi/dbs/elki/database/datastore/memory/MapStore.java
+++ b/src/de/lmu/ifi/dbs/elki/database/datastore/memory/MapStore.java
@@ -29,6 +29,7 @@ import java.util.Map;
import de.lmu.ifi.dbs.elki.database.datastore.WritableDataStore;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
/**
* A class to answer representation queries using a map. Basically, it is just a
@@ -40,9 +41,8 @@ import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
*/
public class MapStore<T> implements WritableDataStore<T> {
/**
- * Storage Map
+ * Storage Map.
*/
- // TODO: use trove maps?
private Map<DBID, T> data;
/**
@@ -65,15 +65,15 @@ public class MapStore<T> implements WritableDataStore<T> {
@Override
public T get(DBIDRef id) {
- return data.get(id.getDBID());
+ return data.get(DBIDUtil.deref(id));
}
@Override
public T put(DBIDRef id, T value) {
if(value == null) {
- return data.remove(id.getDBID());
+ return data.remove(DBIDUtil.deref(id));
}
- return data.put(id.getDBID(), value);
+ return data.put(DBIDUtil.deref(id), value);
}
@Override
diff --git a/src/de/lmu/ifi/dbs/elki/database/datastore/memory/MemoryDataStoreFactory.java b/src/de/lmu/ifi/dbs/elki/database/datastore/memory/MemoryDataStoreFactory.java
index 683e4c5d..06a80fd7 100644
--- a/src/de/lmu/ifi/dbs/elki/database/datastore/memory/MemoryDataStoreFactory.java
+++ b/src/de/lmu/ifi/dbs/elki/database/datastore/memory/MemoryDataStoreFactory.java
@@ -24,13 +24,15 @@ package de.lmu.ifi.dbs.elki.database.datastore.memory;
*/
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreFactory;
-import de.lmu.ifi.dbs.elki.database.datastore.RangeIDMap;
+import de.lmu.ifi.dbs.elki.database.datastore.WritableDBIDDataStore;
import de.lmu.ifi.dbs.elki.database.datastore.WritableDataStore;
import de.lmu.ifi.dbs.elki.database.datastore.WritableDoubleDataStore;
+import de.lmu.ifi.dbs.elki.database.datastore.WritableDoubleDistanceDataStore;
import de.lmu.ifi.dbs.elki.database.datastore.WritableIntegerDataStore;
import de.lmu.ifi.dbs.elki.database.datastore.WritableRecordStore;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRange;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
+import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
/**
* Simple factory class that will store all data in memory using object arrays
@@ -51,6 +53,9 @@ public class MemoryDataStoreFactory implements DataStoreFactory {
@SuppressWarnings("unchecked")
@Override
public <T> WritableDataStore<T> makeStorage(DBIDs ids, int hints, Class<? super T> dataclass) {
+ if (DoubleDistance.class.equals(dataclass)) {
+ return (WritableDataStore<T>) makeDoubleDistanceStorage(ids, hints);
+ }
if (Double.class.equals(dataclass)) {
return (WritableDataStore<T>) makeDoubleStorage(ids, hints);
}
@@ -60,7 +65,7 @@ public class MemoryDataStoreFactory implements DataStoreFactory {
if(ids instanceof DBIDRange) {
DBIDRange range = (DBIDRange) ids;
Object[] data = new Object[range.size()];
- return new ArrayStore<T>(data, new RangeIDMap(range));
+ return new ArrayStore<T>(data, range);
}
else {
return new MapIntegerDBIDStore<T>(ids.size());
@@ -68,10 +73,38 @@ public class MemoryDataStoreFactory implements DataStoreFactory {
}
@Override
+ public WritableDBIDDataStore makeDBIDStorage(DBIDs ids, int hints) {
+ if(ids instanceof DBIDRange) {
+ DBIDRange range = (DBIDRange) ids;
+ return new ArrayDBIDStore(range.size(), range);
+ }
+ else {
+ return new MapIntegerDBIDDBIDStore(ids.size());
+ }
+ }
+
+ /**
+ * Make a data storage for double distances.
+ *
+ * @param ids IDs to store for
+ * @param hints Storage hints
+ * @return double distance storage
+ */
+ public WritableDoubleDistanceDataStore makeDoubleDistanceStorage(DBIDs ids, int hints) {
+ if(ids instanceof DBIDRange) {
+ DBIDRange range = (DBIDRange) ids;
+ return new ArrayDoubleDistanceStore(range.size(), range);
+ }
+ else {
+ return new MapIntegerDBIDDoubleDistanceStore(ids.size());
+ }
+ }
+
+ @Override
public WritableDoubleDataStore makeDoubleStorage(DBIDs ids, int hints) {
if(ids instanceof DBIDRange) {
DBIDRange range = (DBIDRange) ids;
- return new ArrayDoubleStore(range.size(), new RangeIDMap(range));
+ return new ArrayDoubleStore(range.size(), range);
}
else {
return new MapIntegerDBIDDoubleStore(ids.size());
@@ -82,7 +115,7 @@ public class MemoryDataStoreFactory implements DataStoreFactory {
public WritableDoubleDataStore makeDoubleStorage(DBIDs ids, int hints, double def) {
if(ids instanceof DBIDRange) {
DBIDRange range = (DBIDRange) ids;
- return new ArrayDoubleStore(range.size(), new RangeIDMap(range), def);
+ return new ArrayDoubleStore(range.size(), range, def);
}
else {
return new MapIntegerDBIDDoubleStore(ids.size(), def);
@@ -93,7 +126,7 @@ public class MemoryDataStoreFactory implements DataStoreFactory {
public WritableIntegerDataStore makeIntegerStorage(DBIDs ids, int hints) {
if(ids instanceof DBIDRange) {
DBIDRange range = (DBIDRange) ids;
- return new ArrayIntegerStore(range.size(), new RangeIDMap(range));
+ return new ArrayIntegerStore(range.size(), range);
}
else {
return new MapIntegerDBIDIntegerStore(ids.size());
@@ -104,7 +137,7 @@ public class MemoryDataStoreFactory implements DataStoreFactory {
public WritableIntegerDataStore makeIntegerStorage(DBIDs ids, int hints, int def) {
if(ids instanceof DBIDRange) {
DBIDRange range = (DBIDRange) ids;
- return new ArrayIntegerStore(range.size(), new RangeIDMap(range), def);
+ return new ArrayIntegerStore(range.size(), range, def);
}
else {
return new MapIntegerDBIDIntegerStore(ids.size(), def);
@@ -116,7 +149,7 @@ public class MemoryDataStoreFactory implements DataStoreFactory {
if(ids instanceof DBIDRange) {
DBIDRange range = (DBIDRange) ids;
Object[][] data = new Object[range.size()][dataclasses.length];
- return new ArrayRecordStore(data, new RangeIDMap(range));
+ return new ArrayRecordStore(data, range);
}
else {
return new MapIntegerDBIDRecordStore(ids.size(), dataclasses.length);
diff --git a/src/de/lmu/ifi/dbs/elki/database/datastore/package-info.java b/src/de/lmu/ifi/dbs/elki/database/datastore/package-info.java
index dccd8d4e..e5b4a259 100644
--- a/src/de/lmu/ifi/dbs/elki/database/datastore/package-info.java
+++ b/src/de/lmu/ifi/dbs/elki/database/datastore/package-info.java
@@ -18,6 +18,7 @@
* }</pre>
*
* @apiviz.exclude datastore.memory
+ * @apiviz.exclude index.preprocessed
*/
/*
This file is part of ELKI:
@@ -41,4 +42,4 @@ GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package de.lmu.ifi.dbs.elki.database.datastore; \ No newline at end of file
+package de.lmu.ifi.dbs.elki.database.datastore;
diff --git a/src/de/lmu/ifi/dbs/elki/database/ids/ArrayDBIDs.java b/src/de/lmu/ifi/dbs/elki/database/ids/ArrayDBIDs.java
index 68bbb83d..7e9c55c0 100644
--- a/src/de/lmu/ifi/dbs/elki/database/ids/ArrayDBIDs.java
+++ b/src/de/lmu/ifi/dbs/elki/database/ids/ArrayDBIDs.java
@@ -27,23 +27,35 @@ package de.lmu.ifi.dbs.elki.database.ids;
* Interface for array based DBIDs.
*
* @author Erich Schubert
+ *
+ * @apiviz.has DBIDArrayIter
*/
public interface ArrayDBIDs extends DBIDs {
/**
* Get the i'th entry (starting at 0)
*
+ * If possible, use an {@link DBIDArrayIter} via {@link #iter()} instead!
+ *
* @param i Index
* @return DBID of i'th entry.
*/
public DBID get(int i);
/**
+ * Assign a DBID variable the value of position {@code index}.
+ *
+ * @param index Position
+ * @param var Variable to assign the value to.
+ */
+ public void assign(int index, DBIDVar var);
+
+ /**
* Iterable
*
* @return Iterator
*/
@Override
- public DBIDIter iter();
+ public DBIDArrayIter iter();
/**
* Size of the DBID "collection".
diff --git a/src/de/lmu/ifi/dbs/elki/database/ids/ArrayModifiableDBIDs.java b/src/de/lmu/ifi/dbs/elki/database/ids/ArrayModifiableDBIDs.java
index 95bcc2f7..ffac393b 100644
--- a/src/de/lmu/ifi/dbs/elki/database/ids/ArrayModifiableDBIDs.java
+++ b/src/de/lmu/ifi/dbs/elki/database/ids/ArrayModifiableDBIDs.java
@@ -29,6 +29,8 @@ import java.util.Comparator;
* Array-oriented implementation of a modifiable DBID collection.
*
* @author Erich Schubert
+ *
+ * @apiviz.has DBIDArrayMIter
*/
public interface ArrayModifiableDBIDs extends ModifiableDBIDs, ArrayDBIDs {
/**
@@ -41,7 +43,16 @@ public interface ArrayModifiableDBIDs extends ModifiableDBIDs, ArrayDBIDs {
*
* @param comparator Comparator to use
*/
- void sort(Comparator<? super DBID> comparator);
+ void sort(Comparator<? super DBIDRef> comparator);
+
+ /**
+ * Sort the DBID set.
+ *
+ * @param start Starting index, for partial sorting
+ * @param end End index, for partial sorting (exclusive)
+ * @param comparator Comparator to use
+ */
+ void sort(int start, int end, Comparator<? super DBIDRef> comparator);
/**
* Remove the i'th entry (starting at 0)
@@ -58,8 +69,8 @@ public interface ArrayModifiableDBIDs extends ModifiableDBIDs, ArrayDBIDs {
* @param newval New value
* @return previous value
*/
- public DBID set(int i, DBID newval);
-
+ public DBID set(int i, DBIDRef newval);
+
/**
* Swap DBIDs add positions a and b.
*
@@ -67,4 +78,7 @@ public interface ArrayModifiableDBIDs extends ModifiableDBIDs, ArrayDBIDs {
* @param b Second position
*/
public void swap(int a, int b);
-} \ No newline at end of file
+
+ @Override
+ public DBIDArrayMIter iter();
+}
diff --git a/src/de/lmu/ifi/dbs/elki/database/ids/DBID.java b/src/de/lmu/ifi/dbs/elki/database/ids/DBID.java
index 8d98893d..5abf4377 100644
--- a/src/de/lmu/ifi/dbs/elki/database/ids/DBID.java
+++ b/src/de/lmu/ifi/dbs/elki/database/ids/DBID.java
@@ -37,25 +37,7 @@ package de.lmu.ifi.dbs.elki.database.ids;
*
* @apiviz.landmark
*/
-public interface DBID extends DBIDRef, Comparable<DBIDRef>, ArrayDBIDs {
- /**
- * Compare the <em>current</em> value of two referenced DBIDs.
- *
- * @param other Other DBID reference (or DBID)
- * @return {@code true} when the references <em>currently</em> refer to the same.
- */
- @Override
- public boolean sameDBID(DBIDRef other);
-
- /**
- * Compare two objects by the value of the referenced DBID.
- *
- * @param other Other DBID or object
- * @return -1, 0 or +1
- */
- @Override
- public int compareDBID(DBIDRef other);
-
+public interface DBID extends DBIDRef, Comparable<DBIDRef>, ArrayDBIDs, SetDBIDs {
/**
* In contrast to {@link DBIDRef}, the DBID interface is supposed to have a
* stable hash code. However, it is generally preferred to use optimized
@@ -64,40 +46,29 @@ public interface DBID extends DBIDRef, Comparable<DBIDRef>, ArrayDBIDs {
* @return hash code
*/
@Override
- public int hashCode();
+ int hashCode();
/**
* In contrast to {@link DBIDRef}, the DBID interface is supposed to have a
* stable equals for other DBIDs.
*
- * Yet, {@link #sameDBID} is more type safe and explicit.
+ * Yet, {@link DBIDUtil#equal} is more type safe and explicit.
*
+ * @param obj Other object
* @return true when the object is the same DBID.
*/
@Override
- public boolean equals(Object obj);
-
- /**
- * Part of the DBIDRef API, this <em>must</em> return {@code this} for an
- * actual DBID.
- *
- * @return {@code this}
- * @deprecated When the object is known to be a DBID, the usage of this method
- * is pointless, therefore it is marked as deprecated to cause a
- * warning.
- */
@Deprecated
- @Override
- public DBID getDBID();
+ boolean equals(Object obj);
/**
* Compare two DBIDs for ordering.
*
- * Consider using {@link #compareDBID}, which is more explicit.
+ * Consider using {@link DBIDUtil#compare}, which is more explicit.
*
* @param other Other DBID object
* @return Comparison result
*/
@Override
- public int compareTo(DBIDRef other);
+ int compareTo(DBIDRef other);
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/database/ids/DBIDArrayIter.java b/src/de/lmu/ifi/dbs/elki/database/ids/DBIDArrayIter.java
new file mode 100644
index 00000000..fefe5ad1
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/database/ids/DBIDArrayIter.java
@@ -0,0 +1,34 @@
+package de.lmu.ifi.dbs.elki.database.ids;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+import de.lmu.ifi.dbs.elki.utilities.iterator.ArrayIter;
+
+/**
+ * Array iterators that can also go backwards and seek.
+ *
+ * @author Erich Schubert
+ */
+public interface DBIDArrayIter extends DBIDIter, ArrayIter {
+ // Nothing added - see {@link ArrayIter}!
+}
diff --git a/src/de/lmu/ifi/dbs/elki/database/ids/DBIDArrayMIter.java b/src/de/lmu/ifi/dbs/elki/database/ids/DBIDArrayMIter.java
new file mode 100644
index 00000000..1de202a2
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/database/ids/DBIDArrayMIter.java
@@ -0,0 +1,33 @@
+package de.lmu.ifi.dbs.elki.database.ids;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * Modifiable array iterator.
+ *
+ * @author Erich Schubert
+ */
+public interface DBIDArrayMIter extends DBIDArrayIter, DBIDMIter {
+ // Nothing new, see {@link DBIDArrayIter} and {@link DBIDMIter}
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/database/ids/DBIDFactory.java b/src/de/lmu/ifi/dbs/elki/database/ids/DBIDFactory.java
index 6063508b..646e6e4d 100644
--- a/src/de/lmu/ifi/dbs/elki/database/ids/DBIDFactory.java
+++ b/src/de/lmu/ifi/dbs/elki/database/ids/DBIDFactory.java
@@ -24,6 +24,7 @@ package de.lmu.ifi.dbs.elki.database.ids;
*/
import de.lmu.ifi.dbs.elki.database.ids.integer.TrivialDBIDFactory;
+import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
import de.lmu.ifi.dbs.elki.persistent.ByteBufferSerializer;
import de.lmu.ifi.dbs.elki.persistent.FixedSizeByteBufferSerializer;
@@ -47,29 +48,49 @@ public interface DBIDFactory {
/**
* Static DBID factory to use.
*/
- public static DBIDFactory FACTORY = new TrivialDBIDFactory();
-
+ final static DBIDFactory FACTORY = new TrivialDBIDFactory();
+
/**
- * Import an integer ID
+ * Make a new DBID variable.
+ *
+ * @param val Initial value.
+ * @return Variable
+ */
+ DBIDVar newVar(DBIDRef val);
+
+ /**
+ * Import and integer as DBID.
+ *
+ * Note: this may not be possible for some factories!
*
* @param id Integer ID to import
* @return DBID
*/
- public DBID importInteger(int id);
+ DBID importInteger(int id);
/**
- * Generate a single DBID
+ * Assign an integer value to a DBID variable.
+ *
+ * Note: this may not be possible for some factories!
+ *
+ * @param var Variable
+ * @param val Integer value
+ */
+ void assignVar(DBIDVar var, int val);
+
+ /**
+ * Generate a single DBID.
*
* @return A single DBID
*/
- public DBID generateSingleDBID();
+ DBID generateSingleDBID();
/**
* Return a single DBID for reuse.
*
* @param id DBID to deallocate
*/
- public void deallocateSingleDBID(DBID id);
+ void deallocateSingleDBID(DBIDRef id);
/**
* Generate a static DBID range.
@@ -77,14 +98,14 @@ public interface DBIDFactory {
* @param size Requested size
* @return DBID range
*/
- public DBIDRange generateStaticDBIDRange(int size);
+ DBIDRange generateStaticDBIDRange(int size);
/**
* Deallocate a static DBID range.
*
* @param range Range to deallocate
*/
- public void deallocateDBIDRange(DBIDRange range);
+ void deallocateDBIDRange(DBIDRange range);
/**
* Make a DBID pair from two existing DBIDs.
@@ -94,72 +115,133 @@ public interface DBIDFactory {
*
* @return new pair.
*/
- public DBIDPair makePair(DBIDRef id1, DBIDRef id2);
-
+ DBIDPair newPair(DBIDRef id1, DBIDRef id2);
+
+ /**
+ * Make a double-DBID pair.
+ *
+ * @param val Double value
+ * @param id DBID
+ * @return New pair
+ */
+ DoubleDBIDPair newPair(double val, DBIDRef id);
+
+ /**
+ * Make a new distance-DBID pair.
+ *
+ * @param val Distance value
+ * @param id Object ID
+ * @param <D> Distance type
+ * @return New pair
+ */
+ <D extends Distance<D>> DistanceDBIDPair<D> newDistancePair(D val, DBIDRef id);
+
+ /**
+ * Make a new distance-DBID pair.
+ *
+ * @param val Distance value
+ * @param id Object ID
+ * @return New pair
+ */
+ DoubleDistanceDBIDPair newDistancePair(double val, DBIDRef id);
+
/**
* Make a new (modifiable) array of DBIDs.
*
* @return New array
*/
- public ArrayModifiableDBIDs newArray();
-
+ ArrayModifiableDBIDs newArray();
+
/**
* Make a new (modifiable) hash set of DBIDs.
*
* @return New hash set
*/
- public HashSetModifiableDBIDs newHashSet();
-
+ HashSetModifiableDBIDs newHashSet();
+
/**
* Make a new (modifiable) array of DBIDs.
*
* @param size Size hint
* @return New array
*/
- public ArrayModifiableDBIDs newArray(int size);
-
+ ArrayModifiableDBIDs newArray(int size);
+
/**
* Make a new (modifiable) hash set of DBIDs.
*
* @param size Size hint
* @return New hash set
*/
- public HashSetModifiableDBIDs newHashSet(int size);
-
+ HashSetModifiableDBIDs newHashSet(int size);
+
/**
* Make a new (modifiable) array of DBIDs.
*
* @param existing existing DBIDs to use
* @return New array
*/
- public ArrayModifiableDBIDs newArray(DBIDs existing);
-
+ ArrayModifiableDBIDs newArray(DBIDs existing);
+
/**
* Make a new (modifiable) hash set of DBIDs.
*
* @param existing existing DBIDs to use
* @return New hash set
*/
- public HashSetModifiableDBIDs newHashSet(DBIDs existing);
-
+ HashSetModifiableDBIDs newHashSet(DBIDs existing);
+
/**
- * Get a serializer for DBIDs
+ * Get a serializer for DBIDs.
*
- * @return DBID serializer
+ * @return DBID serializer
*/
- public ByteBufferSerializer<DBID> getDBIDSerializer();
-
+ ByteBufferSerializer<DBID> getDBIDSerializer();
+
/**
- * Get a serializer for DBIDs with static size
+ * Get a serializer for DBIDs with static size.
*
* @return DBID serializer
*/
- public FixedSizeByteBufferSerializer<DBID> getDBIDSerializerStatic();
-
+ FixedSizeByteBufferSerializer<DBID> getDBIDSerializerStatic();
+
/**
- * Get type restriction
+ * Get type restriction.
*
* @return type restriction for DBIDs
*/
- public Class<? extends DBID> getTypeRestriction();
-} \ No newline at end of file
+ Class<? extends DBID> getTypeRestriction();
+
+ /**
+ * Compare two DBIDs, for sorting.
+ *
+ * @param a First
+ * @param b Second
+ * @return Comparison result
+ */
+ int compare(DBIDRef a, DBIDRef b);
+
+ /**
+ * Compare two DBIDs, for equality testing.
+ *
+ * @param a First
+ * @param b Second
+ * @return Comparison result
+ */
+ boolean equal(DBIDRef a, DBIDRef b);
+
+ /**
+ * Print a DBID as string.
+ *
+ * @param id DBID reference
+ * @return Formatted ID
+ */
+ String toString(DBIDRef id);
+
+ /**
+ * Get the invalid DBID value, usable as "undefined" placeholder.
+ *
+ * @return Invalid value
+ */
+ DBIDRef invalid();
+}
diff --git a/src/de/lmu/ifi/dbs/elki/database/ids/DBIDIter.java b/src/de/lmu/ifi/dbs/elki/database/ids/DBIDIter.java
index f2d0ae91..268f4441 100644
--- a/src/de/lmu/ifi/dbs/elki/database/ids/DBIDIter.java
+++ b/src/de/lmu/ifi/dbs/elki/database/ids/DBIDIter.java
@@ -38,7 +38,8 @@ import de.lmu.ifi.dbs.elki.utilities.iterator.Iter;
* <pre>
* {@code
* for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
- * iter.getDBID();
+ * Object o = relation.get(iter); // Many interfaces allow direct use
+ * DBID id = DBIDUtil.deref(iter); // Materialize only if you need to!
* }
* }
* </pre>
@@ -53,16 +54,9 @@ import de.lmu.ifi.dbs.elki.utilities.iterator.Iter;
* </ul>
*
* @author Erich Schubert
+ *
+ * @apiviz.landmark
*/
public interface DBIDIter extends DBIDRef, Iter {
- /**
- * Get the referenced {@link DBID}.
- *
- * Efficiency note: this may require materialization of a DBID object - if
- * possible, use DBIDRef based APIs instead.
- *
- * @return referenced DBID
- */
- @Override
- public DBID getDBID();
+ // Empty - use as DBIDRef or Iter
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/database/ids/DBIDMIter.java b/src/de/lmu/ifi/dbs/elki/database/ids/DBIDMIter.java
index fe3b182c..9f42c5a0 100644
--- a/src/de/lmu/ifi/dbs/elki/database/ids/DBIDMIter.java
+++ b/src/de/lmu/ifi/dbs/elki/database/ids/DBIDMIter.java
@@ -1,4 +1,5 @@
package de.lmu.ifi.dbs.elki.database.ids;
+
/*
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
@@ -22,17 +23,21 @@ package de.lmu.ifi.dbs.elki.database.ids;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+import de.lmu.ifi.dbs.elki.utilities.iterator.MIter;
+
/**
* Modifiable DBID iterator.
*
* @author Erich Schubert
*/
-public interface DBIDMIter extends DBIDIter {
+public interface DBIDMIter extends DBIDIter, MIter {
/**
* Remove the object the iterator currently points to.
*
- * Subsequent calls to {@link #getDBID} may return a different element.
- * Call {@link #advance()} to advance the iterator to the next element for further processing.
+ * Note: Subsequent calls to {@link DBIDUtil#deref} may return a different
+ * element. Call {@link #advance()} to advance the iterator to the next
+ * element for further processing.
*/
+ @Override
void remove();
}
diff --git a/src/de/lmu/ifi/dbs/elki/database/ids/DBIDRange.java b/src/de/lmu/ifi/dbs/elki/database/ids/DBIDRange.java
index 1b9ccc3b..8f7e428d 100644
--- a/src/de/lmu/ifi/dbs/elki/database/ids/DBIDRange.java
+++ b/src/de/lmu/ifi/dbs/elki/database/ids/DBIDRange.java
@@ -1,5 +1,7 @@
package de.lmu.ifi.dbs.elki.database.ids;
+import de.lmu.ifi.dbs.elki.database.datastore.DataStoreIDMap;
+
/*
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
@@ -28,7 +30,7 @@ package de.lmu.ifi.dbs.elki.database.ids;
*
* @author Erich Schubert
*/
-public interface DBIDRange extends ArrayStaticDBIDs {
+public interface DBIDRange extends ArrayStaticDBIDs, DataStoreIDMap {
/**
* Get offset in the array for a particular DBID.
*
@@ -38,5 +40,5 @@ public interface DBIDRange extends ArrayStaticDBIDs {
* @param dbid ID to compute index for
* @return index
*/
- public int getOffset(DBIDRef dbid);
+ int getOffset(DBIDRef dbid);
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/database/ids/DBIDRef.java b/src/de/lmu/ifi/dbs/elki/database/ids/DBIDRef.java
index 0934b10b..fce87c31 100644
--- a/src/de/lmu/ifi/dbs/elki/database/ids/DBIDRef.java
+++ b/src/de/lmu/ifi/dbs/elki/database/ids/DBIDRef.java
@@ -31,28 +31,27 @@ package de.lmu.ifi.dbs.elki.database.ids;
* are a good example how the DBIDRef may change.
*
* @author Erich Schubert
+ *
+ * @apiviz.landmark
+ *
+ * @apiviz.has DBID oneway - - «references»
*/
public interface DBIDRef {
/**
- * Get the referenced {@link DBID}.
+ * Get the internal index.
*
- * Efficiency note: this may require materialization of a DBID object.
+ * <b>NOT FOR PUBLIC USE - ELKI Optimization engine only</b>
*
- * @return referenced DBID
+ * @return Internal index
*/
- public DBID getDBID();
-
- /**
- * Return the integer value of the object ID, if possible.
- *
- * @return integer id
- */
- public int getIntegerID();
-
+ int internalGetIndex();
+
/**
* WARNING: Hash codes of this interface <b>might not be stable</b> (e.g. for
* iterators).
*
+ * Use {@link DBIDUtil#deref} to get an object with a stable hash code!
+ *
* @return current hash code (<b>may change!</b>)
*
* @deprecated Do not use this hash code. Some implementations will not offer
@@ -60,33 +59,19 @@ public interface DBIDRef {
*/
@Override
@Deprecated
- public int hashCode();
+ int hashCode();
/**
* WARNING: calling equality on a reference may be an indicator of incorrect
* usage, as it is not clear whether the programmer meant the references to be
* the same or the DBIDs.
*
+ * Use {@link DBIDUtil#equal} or {@link DBIDUtil#compare}!
+ *
* @param obj Object to compare with
* @return True when they are the same object
*/
@Override
@Deprecated
- public boolean equals(Object obj);
-
- /**
- * Compare the <em>current</em> value of two referenced DBIDs.
- *
- * @param other Other DBID reference (or DBID)
- * @return {@code true} when the references <em>currently</em> refer to the same.
- */
- public boolean sameDBID(DBIDRef other);
-
- /**
- * Compare two objects by the value of the referenced DBID.
- *
- * @param other Other DBID or object
- * @return -1, 0 or +1
- */
- public int compareDBID(DBIDRef other);
+ boolean equals(Object obj);
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/database/ids/DBIDUtil.java b/src/de/lmu/ifi/dbs/elki/database/ids/DBIDUtil.java
index 000659b9..9cb4082a 100644
--- a/src/de/lmu/ifi/dbs/elki/database/ids/DBIDUtil.java
+++ b/src/de/lmu/ifi/dbs/elki/database/ids/DBIDUtil.java
@@ -27,7 +27,13 @@ import java.util.Random;
import de.lmu.ifi.dbs.elki.database.ids.generic.UnmodifiableArrayDBIDs;
import de.lmu.ifi.dbs.elki.database.ids.generic.UnmodifiableDBIDs;
+import de.lmu.ifi.dbs.elki.database.ids.integer.IntegerDBIDs;
+import de.lmu.ifi.dbs.elki.database.ids.integer.TroveArrayDBIDs;
+import de.lmu.ifi.dbs.elki.database.ids.integer.UnmodifiableIntegerArrayDBIDs;
+import de.lmu.ifi.dbs.elki.database.ids.integer.UnmodifiableIntegerDBIDs;
+import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
import de.lmu.ifi.dbs.elki.persistent.ByteBufferSerializer;
+import de.lmu.ifi.dbs.elki.utilities.RandomFactory;
/**
* DBID Utility functions.
@@ -35,7 +41,11 @@ import de.lmu.ifi.dbs.elki.persistent.ByteBufferSerializer;
* @author Erich Schubert
*
* @apiviz.landmark
- * @apiviz.composedOf de.lmu.ifi.dbs.elki.database.ids.DBIDFactory
+ *
+ * @apiviz.has DBID
+ * @apiviz.has DBIDs
+ * @apiviz.uses DBIDRef
+ * @apiviz.composedOf DBIDFactory
*/
public final class DBIDUtil {
/**
@@ -51,9 +61,20 @@ public final class DBIDUtil {
public static final EmptyDBIDs EMPTYDBIDS = new EmptyDBIDs();
/**
- * Import an Integer DBID.
+ * Get the invalid special ID.
+ *
+ * @return invalid ID value
+ */
+ public static DBIDRef invalid() {
+ return DBIDFactory.FACTORY.invalid();
+ }
+
+ /**
+ * Import and integer as DBID.
*
- * @param id Integer ID
+ * Note: this may not be possible for some factories!
+ *
+ * @param id Integer ID to import
* @return DBID
*/
public static DBID importInteger(int id) {
@@ -61,25 +82,102 @@ public final class DBIDUtil {
}
/**
- * Get a serializer for DBIDs
+ * Export a DBID as int.
+ *
+ * Note: this may not be possible for some factories!
+ *
+ * @param id DBID to export
+ * @return integer value
+ */
+ public static int asInteger(DBIDRef id) {
+ return id.internalGetIndex();
+ }
+
+ /**
+ * Compare two DBIDs.
+ *
+ * @param id1 First ID
+ * @param id2 Second ID
+ * @return Comparison result
+ */
+ public static int compare(DBIDRef id1, DBIDRef id2) {
+ return DBIDFactory.FACTORY.compare(id1, id2);
+ }
+
+ /**
+ * Test two DBIDs for equality.
+ *
+ * @param id1 First ID
+ * @param id2 Second ID
+ * @return Comparison result
+ */
+ public static boolean equal(DBIDRef id1, DBIDRef id2) {
+ return DBIDFactory.FACTORY.equal(id1, id2);
+ }
+
+ /**
+ * Dereference a DBID reference.
+ *
+ * @param ref DBID reference
+ * @return DBID
+ */
+ public static DBID deref(DBIDRef ref) {
+ if (ref instanceof DBID) {
+ return (DBID) ref;
+ }
+ return importInteger(ref.internalGetIndex());
+ }
+
+ /**
+ * Format a DBID as string.
+ *
+ * @param id DBID
+ * @return String representation
+ */
+ public static String toString(DBIDRef id) {
+ return DBIDFactory.FACTORY.toString(id);
+ }
+
+ /**
+ * Format a DBID as string.
+ *
+ * @param ids DBIDs
+ * @return String representation
+ */
+ public static String toString(DBIDs ids) {
+ if (ids instanceof DBID) {
+ return DBIDFactory.FACTORY.toString((DBID) ids);
+ }
+ StringBuilder buf = new StringBuilder();
+ for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
+ if (buf.length() > 0) {
+ buf.append(", ");
+ }
+ buf.append(DBIDFactory.FACTORY.toString(iter));
+ }
+ return buf.toString();
+ }
+
+ /**
+ * Get a serializer for DBIDs.
*
* @return DBID serializer
*/
- public ByteBufferSerializer<DBID> getDBIDSerializer() {
+ public static ByteBufferSerializer<DBID> getDBIDSerializer() {
return DBIDFactory.FACTORY.getDBIDSerializer();
}
/**
- * Get a serializer for DBIDs with static size
+ * Get a serializer for DBIDs with static size.
*
* @return DBID serializer
*/
- public ByteBufferSerializer<DBID> getDBIDSerializerStatic() {
+ public static ByteBufferSerializer<DBID> getDBIDSerializerStatic() {
return DBIDFactory.FACTORY.getDBIDSerializerStatic();
}
/**
- * Generate a single DBID
+ * Generate a single DBID.
*
* @return A single DBID
*/
@@ -116,6 +214,25 @@ public final class DBIDUtil {
}
/**
+ * Make a new DBID variable.
+ *
+ * @param val Initial value.
+ * @return Variable
+ */
+ public static DBIDVar newVar(DBIDRef val) {
+ return DBIDFactory.FACTORY.newVar(val);
+ }
+
+ /**
+ * Make a new DBID variable.
+ *
+ * @return Variable
+ */
+ public static DBIDVar newVar() {
+ return DBIDFactory.FACTORY.newVar(DBIDFactory.FACTORY.invalid());
+ }
+
+ /**
* Make a new (modifiable) array of DBIDs.
*
* @return New array
@@ -180,14 +297,14 @@ public final class DBIDUtil {
* @param second Second set
* @return result.
*/
- // TODO: optimize?
+ // TODO: optimize better?
public static ModifiableDBIDs intersection(DBIDs first, DBIDs second) {
- if(first.size() > second.size()) {
+ if (first.size() > second.size()) {
return intersection(second, first);
}
ModifiableDBIDs inter = newHashSet(first.size());
- for(DBIDIter it = first.iter(); it.valid(); it.advance()) {
- if(second.contains(it)) {
+ for (DBIDIter it = first.iter(); it.valid(); it.advance()) {
+ if (second.contains(it)) {
inter.add(it);
}
}
@@ -195,6 +312,26 @@ public final class DBIDUtil {
}
/**
+ * Compute the set intersection size of two sets.
+ *
+ * @param first First set
+ * @param second Second set
+ * @return size
+ */
+ public static int intersectionSize(DBIDs first, DBIDs second) {
+ if (first.size() > second.size()) {
+ return intersectionSize(second, first);
+ }
+ int c = 0;
+ for (DBIDIter it = first.iter(); it.valid(); it.advance()) {
+ if (second.contains(it)) {
+ c++;
+ }
+ }
+ return c;
+ }
+
+ /**
* Compute the set symmetric intersection of two sets.
*
* @param first First set
@@ -205,18 +342,18 @@ public final class DBIDUtil {
*/
// TODO: optimize?
public static void symmetricIntersection(DBIDs first, DBIDs second, HashSetModifiableDBIDs firstonly, HashSetModifiableDBIDs intersection, HashSetModifiableDBIDs secondonly) {
- if(first.size() > second.size()) {
+ if (first.size() > second.size()) {
symmetricIntersection(second, first, secondonly, intersection, firstonly);
return;
}
- assert(firstonly.size() == 0) : "OUTPUT set should be empty!";
- assert(intersection.size() == 0) : "OUTPUT set should be empty!";
- assert(secondonly.size() == 0) : "OUTPUT set should be empty!";
+ assert (firstonly.size() == 0) : "OUTPUT set should be empty!";
+ assert (intersection.size() == 0) : "OUTPUT set should be empty!";
+ assert (secondonly.size() == 0) : "OUTPUT set should be empty!";
// Initialize with second
secondonly.addDBIDs(second);
- for(DBIDIter it = first.iter(); it.valid(); it.advance()) {
+ for (DBIDIter it = first.iter(); it.valid(); it.advance()) {
// Try to remove
- if(secondonly.remove(it)) {
+ if (secondonly.remove(it)) {
intersection.add(it);
} else {
firstonly.add(it);
@@ -258,27 +395,31 @@ public final class DBIDUtil {
* @return Unmodifiable collection
*/
public static StaticDBIDs makeUnmodifiable(DBIDs existing) {
- if(existing instanceof StaticDBIDs) {
+ if (existing instanceof StaticDBIDs) {
return (StaticDBIDs) existing;
}
+ if (existing instanceof TroveArrayDBIDs) {
+ return new UnmodifiableIntegerArrayDBIDs((TroveArrayDBIDs) existing);
+ }
+ if (existing instanceof IntegerDBIDs) {
+ return new UnmodifiableIntegerDBIDs((IntegerDBIDs) existing);
+ }
if (existing instanceof ArrayDBIDs) {
- return new UnmodifiableArrayDBIDs((ArrayDBIDs)existing);
- } else {
- return new UnmodifiableDBIDs(existing);
+ return new UnmodifiableArrayDBIDs((ArrayDBIDs) existing);
}
+ return new UnmodifiableDBIDs(existing);
}
/**
* Ensure that the given DBIDs are array-indexable.
*
- * @param ids
+ * @param ids IDs
* @return Array DBIDs.
*/
public static ArrayDBIDs ensureArray(DBIDs ids) {
- if(ids instanceof ArrayDBIDs) {
+ if (ids instanceof ArrayDBIDs) {
return (ArrayDBIDs) ids;
- }
- else {
+ } else {
return newArray(ids);
}
}
@@ -286,33 +427,31 @@ public final class DBIDUtil {
/**
* Ensure that the given DBIDs support fast "contains" operations.
*
- * @param ids
- * @return Array DBIDs.
+ * @param ids IDs
+ * @return Set DBIDs.
*/
public static SetDBIDs ensureSet(DBIDs ids) {
- if(ids instanceof SetDBIDs) {
+ if (ids instanceof SetDBIDs) {
return (SetDBIDs) ids;
- }
- else {
+ } else {
return newHashSet(ids);
}
}
/**
- * Ensure modifiable
+ * Ensure modifiable.
*
- * @param ids
- * @return Array DBIDs.
+ * @param ids IDs
+ * @return Modifiable DBIDs.
*/
public static ModifiableDBIDs ensureModifiable(DBIDs ids) {
- if(ids instanceof ModifiableDBIDs) {
+ if (ids instanceof ModifiableDBIDs) {
return (ModifiableDBIDs) ids;
- }
- else {
- if(ids instanceof ArrayDBIDs) {
+ } else {
+ if (ids instanceof ArrayDBIDs) {
return newArray(ids);
}
- if(ids instanceof HashSetDBIDs) {
+ if (ids instanceof HashSetDBIDs) {
return newHashSet(ids);
}
return newArray(ids);
@@ -328,11 +467,91 @@ public final class DBIDUtil {
* @return DBID pair
*/
public static DBIDPair newPair(DBIDRef id1, DBIDRef id2) {
- return DBIDFactory.FACTORY.makePair(id1, id2);
+ return DBIDFactory.FACTORY.newPair(id1, id2);
+ }
+
+ /**
+ * Make a DoubleDBIDPair.
+ *
+ * @param val double value
+ * @param id ID
+ * @return new pair
+ */
+ public static DoubleDBIDPair newPair(double val, DBIDRef id) {
+ return DBIDFactory.FACTORY.newPair(val, id);
+ }
+
+ /**
+ * Make a DistanceDBIDPair.
+ *
+ * @param dist Distance value
+ * @param id ID
+ * @return new pair
+ */
+ public static <D extends Distance<D>> DistanceDBIDPair<D> newDistancePair(D dist, DBIDRef id) {
+ return DBIDFactory.FACTORY.newDistancePair(dist, id);
+ }
+
+ /**
+ * Make a DoubleDistanceDBIDPair.
+ *
+ * @param dist Distance value
+ * @param id ID
+ * @return new pair
+ */
+ public static DoubleDistanceDBIDPair newDistancePair(double dist, DBIDRef id) {
+ return DBIDFactory.FACTORY.newDistancePair(dist, id);
}
/**
- * Produce a random sample of the given DBIDs
+ * Produce a random sample of the given DBIDs.
+ *
+ * @param source Original DBIDs
+ * @param k k Parameter
+ * @param rnd Random generator
+ * @return new DBIDs
+ */
+ public static ModifiableDBIDs randomSample(DBIDs source, int k, RandomFactory rnd) {
+ return randomSample(source, k, rnd.getRandom());
+ }
+
+ /**
+ * Produce a random shuffling of the given DBID array.
+ *
+ * @param ids Original DBIDs
+ * @param rnd Random generator
+ */
+ public static void randomShuffle(ArrayModifiableDBIDs ids, RandomFactory rnd) {
+ randomShuffle(ids, rnd.getRandom(), ids.size());
+ }
+
+ /**
+ * Produce a random shuffling of the given DBID array.
+ *
+ * @param ids Original DBIDs
+ * @param random Random generator
+ */
+ public static void randomShuffle(ArrayModifiableDBIDs ids, Random random) {
+ randomShuffle(ids, random, ids.size());
+ }
+
+ /**
+ * Produce a random shuffling of the given DBID array.
+ *
+ * Only the first {@code limit} elements will be randomized.
+ *
+ * @param ids Original DBIDs
+ * @param random Random generator
+ * @param limit Shuffling limit.
+ */
+ public static void randomShuffle(ArrayModifiableDBIDs ids, Random random, final int limit) {
+ for (int i = 1; i < limit; i++) {
+ ids.swap(i - 1, i + random.nextInt(limit - i));
+ }
+ }
+
+ /**
+ * Produce a random sample of the given DBIDs.
*
* @param source Original DBIDs
* @param k k Parameter
@@ -340,11 +559,11 @@ public final class DBIDUtil {
* @return new DBIDs
*/
public static ModifiableDBIDs randomSample(DBIDs source, int k, int seed) {
- return randomSample(source, k, (long) seed);
+ return randomSample(source, k, new Random(seed));
}
/**
- * Produce a random sample of the given DBIDs
+ * Produce a random sample of the given DBIDs.
*
* @param source Original DBIDs
* @param k k Parameter
@@ -352,39 +571,47 @@ public final class DBIDUtil {
* @return new DBIDs
*/
public static ModifiableDBIDs randomSample(DBIDs source, int k, Long seed) {
- if(k <= 0 || k > source.size()) {
- throw new IllegalArgumentException("Illegal value for size of random sample: " + k+ " > "+source.size()+" or < 0");
+ if (seed != null) {
+ return randomSample(source, k, new Random(seed.longValue()));
+ } else {
+ return randomSample(source, k, new Random());
}
- final Random random;
- if(seed != null) {
- random = new Random(seed);
+ }
+
+ /**
+ * Produce a random sample of the given DBIDs.
+ *
+ * @param source Original DBIDs
+ * @param k k Parameter
+ * @param random Random generator
+ * @return new DBIDs
+ */
+ public static ModifiableDBIDs randomSample(DBIDs source, int k, Random random) {
+ if (k <= 0 || k > source.size()) {
+ throw new IllegalArgumentException("Illegal value for size of random sample: " + k + " > " + source.size() + " or < 0");
}
- else {
+ if (random == null) {
random = new Random();
}
// TODO: better balancing for different sizes
// Two methods: constructive vs. destructive
- if(k < source.size() / 2) {
+ if (k < source.size() >> 1) {
ArrayDBIDs aids = DBIDUtil.ensureArray(source);
+ DBIDArrayIter iter = aids.iter();
HashSetModifiableDBIDs sample = DBIDUtil.newHashSet(k);
- while(sample.size() < k) {
- sample.add(aids.get(random.nextInt(aids.size())));
+ while (sample.size() < k) {
+ iter.seek(random.nextInt(aids.size()));
+ sample.add(iter);
}
return sample;
- }
- else {
+ } else {
ArrayModifiableDBIDs sample = DBIDUtil.newArray(source);
- while(sample.size() > k) {
- // Element to remove
- int idx = random.nextInt(sample.size());
- // Remove last element
- DBID last = sample.remove(sample.size() - 1);
- // Replace target element:
- if(idx < sample.size()) {
- sample.set(idx, last);
- }
+ randomShuffle(sample, random, k);
+ // Delete trailing elements
+ for (int i = sample.size() - 1; i > k; i--) {
+ sample.remove(i);
}
return sample;
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/database/ids/DBIDVar.java b/src/de/lmu/ifi/dbs/elki/database/ids/DBIDVar.java
new file mode 100644
index 00000000..b66ae5f5
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/database/ids/DBIDVar.java
@@ -0,0 +1,42 @@
+package de.lmu.ifi.dbs.elki.database.ids;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * (Persistent) variable storing a DBID reference.
+ *
+ * In contrast to the {@link DBIDRef} API, which are read-only references, this
+ * variable can be updated to point to a different DBID, e.g. the current best
+ * candidate.
+ *
+ * @author Erich Schubert
+ */
+public interface DBIDVar extends DBIDRef, ArrayDBIDs, SetDBIDs {
+ /**
+ * Assign a new value for the reference.
+ *
+ * @param ref Reference
+ */
+ void set(DBIDRef ref);
+}
diff --git a/src/de/lmu/ifi/dbs/elki/database/ids/DBIDs.java b/src/de/lmu/ifi/dbs/elki/database/ids/DBIDs.java
index e9a3e0ab..0b9db136 100644
--- a/src/de/lmu/ifi/dbs/elki/database/ids/DBIDs.java
+++ b/src/de/lmu/ifi/dbs/elki/database/ids/DBIDs.java
@@ -23,7 +23,6 @@ package de.lmu.ifi.dbs.elki.database.ids;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.util.Iterator;
/**
* Interface for a collection of database references (IDs).
@@ -34,7 +33,7 @@ import java.util.Iterator;
* @apiviz.composedOf DBID
* @apiviz.has DBIDIter
*/
-public interface DBIDs extends Iterable<DBID> {
+public interface DBIDs {
/**
* Get a DBID iterator (a more efficient API).
*
@@ -73,13 +72,4 @@ public interface DBIDs extends Iterable<DBID> {
* @return true when empty.
*/
public boolean isEmpty();
-
- /**
- * Classic iterator.
- *
- * @deprecated Use {@link DBIDIter} API instead.
- */
- @Override
- @Deprecated
- public Iterator<DBID> iterator();
-}
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/database/ids/DistanceDBIDPair.java b/src/de/lmu/ifi/dbs/elki/database/ids/DistanceDBIDPair.java
new file mode 100644
index 00000000..01a1f407
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/database/ids/DistanceDBIDPair.java
@@ -0,0 +1,52 @@
+package de.lmu.ifi.dbs.elki.database.ids;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
+
+/**
+ * Pair containing a distance an an object ID
+ *
+ * Note: there is no getter for the object, as this is a {@link DBIDRef}.
+ *
+ * @author Erich Schubert
+ *
+ * @param <D> Distance
+ */
+public interface DistanceDBIDPair<D extends Distance<D>> extends DBIDRef {
+ /**
+ * Get the distance.
+ *
+ * @return Distance
+ */
+ public D getDistance();
+
+ /**
+ * Compare to another result, by distance, smaller first.
+ *
+ * @param other Other result
+ * @return Comparison result
+ */
+ public int compareByDistance(DistanceDBIDPair<D> other);
+}
diff --git a/src/de/lmu/ifi/dbs/elki/database/ids/DoubleDBIDPair.java b/src/de/lmu/ifi/dbs/elki/database/ids/DoubleDBIDPair.java
new file mode 100644
index 00000000..06210076
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/database/ids/DoubleDBIDPair.java
@@ -0,0 +1,64 @@
+package de.lmu.ifi.dbs.elki.database.ids;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import de.lmu.ifi.dbs.elki.utilities.pairs.PairInterface;
+
+/**
+ * Pair of a double value and a DBID
+ *
+ * @author Erich Schubert
+ */
+public interface DoubleDBIDPair extends PairInterface<Double, DBID>, DBIDRef, Comparable<DoubleDBIDPair> {
+ /**
+ * Get the double value of the pair.
+ *
+ * @return Double
+ */
+ public double doubleValue();
+
+ /**
+ * Get the first object - note: this may cause autoboxing, use pair.first for
+ * native pairs!
+ *
+ * @deprecated Avoid autoboxing. Use {@link #doubleValue}!
+ *
+ * @return First object
+ */
+ @Override
+ @Deprecated
+ public Double getFirst();
+
+ /**
+ * Get the second object - note: this may cause autoboxing, use pair.second
+ * for native pairs!
+ *
+ * @deprecated Avoid autoboxing! Use {@link DBIDRef} interface!
+ *
+ * @return Second object
+ */
+ @Override
+ @Deprecated
+ public DBID getSecond();
+}
diff --git a/src/de/lmu/ifi/dbs/elki/database/ids/DoubleDistanceDBIDPair.java b/src/de/lmu/ifi/dbs/elki/database/ids/DoubleDistanceDBIDPair.java
new file mode 100644
index 00000000..72a9cfef
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/database/ids/DoubleDistanceDBIDPair.java
@@ -0,0 +1,53 @@
+package de.lmu.ifi.dbs.elki.database.ids;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
+
+/**
+ * Pair containing a double distance a DBID.
+ *
+ * There is no getter for the DBID, as this is a {@link DBIDRef} already.
+ *
+ * @author Erich Schubert
+ */
+public interface DoubleDistanceDBIDPair extends DistanceDBIDPair<DoubleDistance> {
+ /**
+ * Get the distance.
+ *
+ * @deprecated Would produce a DoubleDistance object. Use {@link #doubleDistance} instead!
+ *
+ * @return Distance
+ */
+ @Override
+ @Deprecated
+ public DoubleDistance getDistance();
+
+ /**
+ * Get the distance.
+ *
+ * @return Distance
+ */
+ public double doubleDistance();
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/database/ids/EmptyDBIDs.java b/src/de/lmu/ifi/dbs/elki/database/ids/EmptyDBIDs.java
index a85b8954..995f917c 100644
--- a/src/de/lmu/ifi/dbs/elki/database/ids/EmptyDBIDs.java
+++ b/src/de/lmu/ifi/dbs/elki/database/ids/EmptyDBIDs.java
@@ -23,11 +23,9 @@ package de.lmu.ifi.dbs.elki.database.ids;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.util.Iterator;
import java.util.NoSuchElementException;
import de.lmu.ifi.dbs.elki.logging.LoggingUtil;
-import de.lmu.ifi.dbs.elki.utilities.iterator.EmptyIterator;
/**
* Empty DBID collection.
@@ -38,7 +36,7 @@ import de.lmu.ifi.dbs.elki.utilities.iterator.EmptyIterator;
*/
public class EmptyDBIDs implements ArrayStaticDBIDs, SetDBIDs {
/**
- * Empty DBID iterator
+ * Empty DBID iterator.
*/
public static final EmptyDBIDIterator EMPTY_ITERATOR = new EmptyDBIDIterator();
@@ -55,11 +53,6 @@ public class EmptyDBIDs implements ArrayStaticDBIDs, SetDBIDs {
}
@Override
- public Iterator<DBID> iterator() {
- return EmptyIterator.STATIC();
- }
-
- @Override
public int size() {
return 0;
}
@@ -75,7 +68,12 @@ public class EmptyDBIDs implements ArrayStaticDBIDs, SetDBIDs {
}
@Override
- public DBIDIter iter() {
+ public void assign(int index, DBIDVar var) {
+ throw new ArrayIndexOutOfBoundsException();
+ }
+
+ @Override
+ public DBIDArrayMIter iter() {
return EMPTY_ITERATOR;
}
@@ -85,11 +83,11 @@ public class EmptyDBIDs implements ArrayStaticDBIDs, SetDBIDs {
}
/**
- * Iterator for empty DBIDs
+ * Iterator for empty DBIDs-
*
* @author Erich Schubert
*/
- protected static class EmptyDBIDIterator implements DBIDIter {
+ protected static class EmptyDBIDIterator implements DBIDArrayMIter {
@Override
public boolean valid() {
return false;
@@ -101,12 +99,7 @@ public class EmptyDBIDs implements ArrayStaticDBIDs, SetDBIDs {
}
@Override
- public int getIntegerID() {
- throw new NoSuchElementException();
- }
-
- @Override
- public DBID getDBID() {
+ public int internalGetIndex() {
throw new NoSuchElementException();
}
@@ -119,13 +112,28 @@ public class EmptyDBIDs implements ArrayStaticDBIDs, SetDBIDs {
}
@Override
- public boolean sameDBID(DBIDRef other) {
- return false;
+ public void remove() {
+ throw new NoSuchElementException();
+ }
+
+ @Override
+ public void advance(int count) {
+ assert (count != 0) : "Misplaced call to advance()";
+ }
+
+ @Override
+ public void retract() {
+ assert (false) : "Misplaced call to retract()";
+ }
+
+ @Override
+ public void seek(int off) {
+ // Ignore
}
@Override
- public int compareDBID(DBIDRef other) {
- throw new UnsupportedOperationException("Invalid iterator position. Cannot compare.");
+ public int getOffset() {
+ return 0;
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/database/ids/HashSetModifiableDBIDs.java b/src/de/lmu/ifi/dbs/elki/database/ids/HashSetModifiableDBIDs.java
index f7c8b551..efb39bc8 100644
--- a/src/de/lmu/ifi/dbs/elki/database/ids/HashSetModifiableDBIDs.java
+++ b/src/de/lmu/ifi/dbs/elki/database/ids/HashSetModifiableDBIDs.java
@@ -36,4 +36,8 @@ public interface HashSetModifiableDBIDs extends HashSetDBIDs, ModifiableDBIDs {
* @return true when modified
*/
public boolean retainAll(DBIDs set);
+
+ // To help the compilers...
+ @Override
+ DBIDMIter iter();
}
diff --git a/src/de/lmu/ifi/dbs/elki/database/ids/ModifiableDBIDs.java b/src/de/lmu/ifi/dbs/elki/database/ids/ModifiableDBIDs.java
index c92caef0..547f3297 100644
--- a/src/de/lmu/ifi/dbs/elki/database/ids/ModifiableDBIDs.java
+++ b/src/de/lmu/ifi/dbs/elki/database/ids/ModifiableDBIDs.java
@@ -31,6 +31,8 @@ package de.lmu.ifi.dbs.elki.database.ids;
* <em>deliberately</em>.
*
* @author Erich Schubert
+ *
+ * @apiviz.has DBIDMIter
*/
public interface ModifiableDBIDs extends DBIDs {
/**
diff --git a/src/de/lmu/ifi/dbs/elki/database/ids/generic/DBIDIterAdapter.java b/src/de/lmu/ifi/dbs/elki/database/ids/generic/DBIDIterAdapter.java
index 91e307c2..124d1b28 100644
--- a/src/de/lmu/ifi/dbs/elki/database/ids/generic/DBIDIterAdapter.java
+++ b/src/de/lmu/ifi/dbs/elki/database/ids/generic/DBIDIterAdapter.java
@@ -27,7 +27,6 @@ import java.util.Iterator;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDMIter;
-import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
/**
* Iterator for classic collections.
@@ -36,12 +35,12 @@ import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
*/
public class DBIDIterAdapter implements DBIDMIter {
/**
- * Current DBID
+ * Current DBID.
*/
DBID cur = null;
/**
- * The real iterator
+ * The real iterator.
*/
Iterator<DBID> iter;
@@ -72,29 +71,12 @@ public class DBIDIterAdapter implements DBIDMIter {
}
@Override
- public int getIntegerID() {
- return cur.getIntegerID();
- }
-
- @Override
- public DBID getDBID() {
- return cur;
+ public int internalGetIndex() {
+ return cur.internalGetIndex();
}
@Override
public void remove() {
iter.remove();
}
-
- @Override
- public boolean sameDBID(DBIDRef other) {
- return cur.getIntegerID() == other.getIntegerID();
- }
-
- @Override
- public int compareDBID(DBIDRef o) {
- final int thisVal = cur.getIntegerID();
- final int anotherVal = o.getIntegerID();
- return (thisVal < anotherVal ? -1 : (thisVal == anotherVal ? 0 : 1));
- }
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/database/ids/generic/GenericArrayModifiableDBIDs.java b/src/de/lmu/ifi/dbs/elki/database/ids/generic/GenericArrayModifiableDBIDs.java
deleted file mode 100644
index f02c5064..00000000
--- a/src/de/lmu/ifi/dbs/elki/database/ids/generic/GenericArrayModifiableDBIDs.java
+++ /dev/null
@@ -1,139 +0,0 @@
-package de.lmu.ifi.dbs.elki.database.ids.generic;
-
-/*
- This file is part of ELKI:
- Environment for Developing KDD-Applications Supported by Index-Structures
-
- Copyright (C) 2012
- Ludwig-Maximilians-Universität München
- Lehr- und Forschungseinheit für Datenbanksysteme
- ELKI Development Team
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-
-import de.lmu.ifi.dbs.elki.database.ids.ArrayModifiableDBIDs;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
-import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
-import de.lmu.ifi.dbs.elki.database.ids.DBIDMIter;
-import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
-import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
-
-/**
- * Array-oriented implementation of a modifiable DBID collection.
- *
- * This should only be instantiated by a
- * {@link de.lmu.ifi.dbs.elki.database.ids.DBIDFactory}!
- *
- * Use {@link de.lmu.ifi.dbs.elki.database.ids.DBIDUtil#newArray}!
- *
- * @author Erich Schubert
- *
- * @apiviz.uses DBID
- */
-public class GenericArrayModifiableDBIDs extends ArrayList<DBID> implements ArrayModifiableDBIDs {
- /**
- * Serial version
- */
- private static final long serialVersionUID = 1L;
-
- /**
- * Constructor with size hint.
- *
- * @param initialCapacity Size hint
- */
- public GenericArrayModifiableDBIDs(int initialCapacity) {
- super(initialCapacity);
- }
-
- /**
- * Constructor without extra hints
- */
- public GenericArrayModifiableDBIDs() {
- super();
- }
-
- /**
- * Constructor from existing DBIDs.
- *
- * @param c Existing DBIDs.
- */
- public GenericArrayModifiableDBIDs(DBIDs c) {
- super(c.size());
- addDBIDs(c);
- }
-
- @Override
- public boolean addDBIDs(DBIDs ids) {
- super.ensureCapacity(size() + ids.size());
- boolean changed = false;
- for(DBIDIter id = ids.iter(); id.valid(); id.advance()) {
- changed |= add(id);
- }
- return changed;
- }
-
- @Override
- public boolean removeDBIDs(DBIDs ids) {
- boolean changed = false;
- for(DBIDIter id = ids.iter(); id.valid(); id.advance()) {
- changed |= super.remove(id);
- }
- return changed;
- }
-
- @Override
- public boolean add(DBIDRef id) {
- return add(id.getDBID());
- }
-
- @Override
- public boolean remove(DBIDRef id) {
- return super.remove(id.getDBID());
- }
-
- @Override
- public void sort() {
- Collections.sort(this);
- }
-
- @Override
- public void sort(Comparator<? super DBID> comparator) {
- Collections.sort(this, comparator);
- }
-
- @Override
- public DBIDMIter iter() {
- return new DBIDIterAdapter(iterator());
- }
-
- @Override
- public int binarySearch(DBIDRef key) {
- return Collections.binarySearch(this, key.getDBID());
- }
-
- @Override
- public boolean contains(DBIDRef o) {
- return super.contains(o);
- }
-
- @Override
- public void swap(int a, int b) {
- set(a, set(b, get(a)));
- }
-} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/database/ids/generic/GenericHashSetModifiableDBIDs.java b/src/de/lmu/ifi/dbs/elki/database/ids/generic/GenericHashSetModifiableDBIDs.java
deleted file mode 100644
index 6eadf0b1..00000000
--- a/src/de/lmu/ifi/dbs/elki/database/ids/generic/GenericHashSetModifiableDBIDs.java
+++ /dev/null
@@ -1,130 +0,0 @@
-package de.lmu.ifi.dbs.elki.database.ids.generic;
-
-/*
- This file is part of ELKI:
- Environment for Developing KDD-Applications Supported by Index-Structures
-
- Copyright (C) 2012
- Ludwig-Maximilians-Universität München
- Lehr- und Forschungseinheit für Datenbanksysteme
- ELKI Development Team
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-import java.util.HashSet;
-
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
-import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
-import de.lmu.ifi.dbs.elki.database.ids.DBIDMIter;
-import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
-import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
-import de.lmu.ifi.dbs.elki.database.ids.HashSetModifiableDBIDs;
-
-/**
- * Set-oriented implementation of a modifiable DBID collection.
- *
- * This should only be instantiated by a
- * {@link de.lmu.ifi.dbs.elki.database.ids.DBIDFactory}!
- *
- * Use {@link de.lmu.ifi.dbs.elki.database.ids.DBIDUtil#newHashSet}!
- *
- * @author Erich Schubert
- *
- * @apiviz.uses DBID
- */
-public class GenericHashSetModifiableDBIDs extends HashSet<DBID> implements HashSetModifiableDBIDs {
- /**
- * Serial version
- */
- private static final long serialVersionUID = 1L;
-
- /**
- * Constructor with size hint.
- *
- * @param initialCapacity Size hint
- */
- public GenericHashSetModifiableDBIDs(int initialCapacity) {
- super(initialCapacity);
- }
-
- /**
- * Constructor without extra hints
- */
- public GenericHashSetModifiableDBIDs() {
- super();
- }
-
- /**
- * Constructor from existing DBIDs.
- *
- * @param c Existing DBIDs.
- */
- public GenericHashSetModifiableDBIDs(DBIDs c) {
- super(c.size());
- for(DBIDIter id = c.iter(); id.valid(); id.advance()) {
- add(id);
- }
- }
-
- @Override
- public boolean addDBIDs(DBIDs ids) {
- boolean changed = false;
- for(DBIDIter id = ids.iter(); id.valid(); id.advance()) {
- changed |= add(id);
- }
- return changed;
- }
-
- @Override
- public boolean removeDBIDs(DBIDs ids) {
- boolean changed = false;
- for(DBIDIter id = ids.iter(); id.valid(); id.advance()) {
- changed |= super.remove(id);
- }
- return changed;
- }
-
- @Override
- public boolean add(DBIDRef id) {
- return super.add(id.getDBID());
- }
-
- @Override
- public boolean remove(DBIDRef id) {
- return super.remove(id.getDBID());
- }
-
- @Override
- public boolean retainAll(DBIDs ids) {
- boolean modified = false;
- for(DBIDMIter it = iter(); it.valid(); it.advance()) {
- if(!ids.contains(it)) {
- it.remove();
- modified = true;
- }
- }
- return modified;
- }
-
- @Override
- public DBIDMIter iter() {
- return new DBIDIterAdapter(iterator());
- }
-
- @Override
- public boolean contains(DBIDRef o) {
- return super.contains(o);
- }
-} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/database/ids/generic/MaskedDBIDs.java b/src/de/lmu/ifi/dbs/elki/database/ids/generic/MaskedDBIDs.java
index 13a6cc21..668ac0d8 100644
--- a/src/de/lmu/ifi/dbs/elki/database/ids/generic/MaskedDBIDs.java
+++ b/src/de/lmu/ifi/dbs/elki/database/ids/generic/MaskedDBIDs.java
@@ -24,10 +24,10 @@ package de.lmu.ifi.dbs.elki.database.ids.generic;
*/
import java.util.BitSet;
-import java.util.Iterator;
import de.lmu.ifi.dbs.elki.database.ids.ArrayDBIDs;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDArrayIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDFactory;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
@@ -41,12 +41,12 @@ import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
*/
public class MaskedDBIDs implements DBIDs {
/**
- * Data storage
+ * Data storage.
*/
protected ArrayDBIDs data;
/**
- * The bitmask used for masking
+ * The bitmask used for masking.
*/
protected BitSet bits;
@@ -70,16 +70,6 @@ public class MaskedDBIDs implements DBIDs {
}
@Override
- public Iterator<DBID> iterator() {
- if(inverse) {
- return new InvItr();
- }
- else {
- return new Itr();
- }
- }
-
- @Override
public DBIDIter iter() {
if(inverse) {
return new InvDBIDItr();
@@ -102,8 +92,8 @@ public class MaskedDBIDs implements DBIDs {
@Override
public boolean contains(DBIDRef o) {
// TODO: optimize.
- for(DBID id : this) {
- if(id.sameDBID(o)) {
+ for(DBIDIter iter = iter(); iter.valid(); iter.advance()) {
+ if(DBIDFactory.FACTORY.equal(iter, o)) {
return true;
}
}
@@ -116,61 +106,29 @@ public class MaskedDBIDs implements DBIDs {
}
/**
- * Iterator over set bits
+ * Iterator over set bits.
*
* @author Erich Schubert
*
* @apiviz.exclude
*/
- protected class Itr implements Iterator<DBID> {
+ protected class DBIDItr implements DBIDIter {
/**
- * Next position.
+ * Current position.
*/
private int pos;
/**
- * Constructor
+ * Array iterator, for referencing.
*/
- protected Itr() {
- this.pos = bits.nextSetBit(0);
- }
-
- @Override
- public boolean hasNext() {
- return (pos >= 0) && (pos < data.size());
- }
-
- @Override
- public DBID next() {
- DBID cur = data.get(pos);
- pos = bits.nextSetBit(pos + 1);
- return cur;
- }
-
- @Override
- public void remove() {
- throw new UnsupportedOperationException();
- }
- }
-
- /**
- * Iterator over set bits
- *
- * @author Erich Schubert
- *
- * @apiviz.exclude
- */
- protected class DBIDItr implements DBIDIter {
- /**
- * Next position.
- */
- private int pos;
+ private DBIDArrayIter iter;
/**
- * Constructor
+ * Constructor.
*/
protected DBIDItr() {
this.pos = bits.nextSetBit(0);
+ this.iter = data.iter();
}
@Override
@@ -184,84 +142,36 @@ public class MaskedDBIDs implements DBIDs {
}
@Override
- public int getIntegerID() {
- return data.get(pos).getIntegerID();
- }
-
- @Override
- public DBID getDBID() {
- return data.get(pos);
- }
-
- @Override
- public boolean sameDBID(DBIDRef other) {
- return getIntegerID() == other.getIntegerID();
- }
-
- @Override
- public int compareDBID(DBIDRef o) {
- final int thisVal = getIntegerID();
- final int anotherVal = o.getIntegerID();
- return (thisVal < anotherVal ? -1 : (thisVal == anotherVal ? 0 : 1));
+ public int internalGetIndex() {
+ iter.seek(pos);
+ return iter.internalGetIndex();
}
}
/**
- * Iterator over unset elements.
+ * Iterator over set bits.
*
* @author Erich Schubert
*
* @apiviz.exclude
*/
- protected class InvItr implements Iterator<DBID> {
+ protected class InvDBIDItr implements DBIDIter {
/**
- * Next unset position.
+ * Next position.
*/
private int pos;
/**
- * Constructor
- */
- protected InvItr() {
- this.pos = bits.nextClearBit(0);
- }
-
- @Override
- public boolean hasNext() {
- return (pos >= 0) && (pos < data.size());
- }
-
- @Override
- public DBID next() {
- DBID cur = data.get(pos);
- pos = bits.nextClearBit(pos + 1);
- return cur;
- }
-
- @Override
- public void remove() {
- throw new UnsupportedOperationException();
- }
- }
-
- /**
- * Iterator over set bits
- *
- * @author Erich Schubert
- *
- * @apiviz.exclude
- */
- protected class InvDBIDItr implements DBIDIter {
- /**
- * Next position.
+ * Array iterator, for referencing.
*/
- private int pos;
+ private DBIDArrayIter iter;
/**
- * Constructor
+ * Constructor.
*/
protected InvDBIDItr() {
this.pos = bits.nextClearBit(0);
+ this.iter = data.iter();
}
@Override
@@ -275,25 +185,9 @@ public class MaskedDBIDs implements DBIDs {
}
@Override
- public int getIntegerID() {
- return data.get(pos).getIntegerID();
- }
-
- @Override
- public DBID getDBID() {
- return data.get(pos);
- }
-
- @Override
- public boolean sameDBID(DBIDRef other) {
- return getIntegerID() == other.getIntegerID();
- }
-
- @Override
- public int compareDBID(DBIDRef o) {
- final int thisVal = getIntegerID();
- final int anotherVal = o.getIntegerID();
- return (thisVal < anotherVal ? -1 : (thisVal == anotherVal ? 0 : 1));
+ public int internalGetIndex() {
+ iter.seek(pos);
+ return iter.internalGetIndex();
}
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/database/ids/generic/MergedDBIDs.java b/src/de/lmu/ifi/dbs/elki/database/ids/generic/MergedDBIDs.java
index 16d83a56..9d2583e3 100644
--- a/src/de/lmu/ifi/dbs/elki/database/ids/generic/MergedDBIDs.java
+++ b/src/de/lmu/ifi/dbs/elki/database/ids/generic/MergedDBIDs.java
@@ -23,9 +23,6 @@ package de.lmu.ifi.dbs.elki.database.ids.generic;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.util.Iterator;
-
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
@@ -56,11 +53,6 @@ public class MergedDBIDs implements DBIDs {
}
@Override
- public Iterator<DBID> iterator() {
- throw new AbortException("Merged iterators not completely implemented yet!");
- }
-
- @Override
public DBIDIter iter() {
throw new AbortException("Merged iterators not completely implemented yet!");
}
diff --git a/src/de/lmu/ifi/dbs/elki/database/ids/generic/UnmodifiableArrayDBIDs.java b/src/de/lmu/ifi/dbs/elki/database/ids/generic/UnmodifiableArrayDBIDs.java
index 35b092c2..abcdef54 100644
--- a/src/de/lmu/ifi/dbs/elki/database/ids/generic/UnmodifiableArrayDBIDs.java
+++ b/src/de/lmu/ifi/dbs/elki/database/ids/generic/UnmodifiableArrayDBIDs.java
@@ -23,27 +23,27 @@ package de.lmu.ifi.dbs.elki.database.ids.generic;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.util.Iterator;
-
import de.lmu.ifi.dbs.elki.database.ids.ArrayDBIDs;
import de.lmu.ifi.dbs.elki.database.ids.ArrayStaticDBIDs;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
-import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDArrayIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDMIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
-import de.lmu.ifi.dbs.elki.utilities.iterator.UnmodifiableIterator;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDVar;
/**
* Unmodifiable wrapper for DBIDs.
*
* @author Erich Schubert
*
- * @apiviz.uses de.lmu.ifi.dbs.elki.database.ids.DBIDs
+ * @apiviz.uses ArrayDBIDs
+ * @apiviz.has UnmodifiableDBIDArrayIter
*/
public class UnmodifiableArrayDBIDs implements ArrayStaticDBIDs {
/**
* The DBIDs we wrap.
*/
- final private ArrayDBIDs inner;
+ private final ArrayDBIDs inner;
/**
* Constructor.
@@ -65,15 +65,13 @@ public class UnmodifiableArrayDBIDs implements ArrayStaticDBIDs {
return inner.isEmpty();
}
- @SuppressWarnings("deprecation")
- @Override
- public Iterator<DBID> iterator() {
- return new UnmodifiableIterator<DBID>(inner.iterator());
- }
-
@Override
- public DBIDIter iter() {
- return inner.iter();
+ public DBIDArrayIter iter() {
+ DBIDArrayIter it = inner.iter();
+ if(it instanceof DBIDMIter) {
+ return new UnmodifiableDBIDArrayIter(it);
+ }
+ return it;
}
@Override
@@ -81,9 +79,6 @@ public class UnmodifiableArrayDBIDs implements ArrayStaticDBIDs {
return inner.size();
}
- /**
- * Returns a string representation of the inner DBID collection.
- */
@Override
public String toString() {
return inner.toString();
@@ -95,7 +90,69 @@ public class UnmodifiableArrayDBIDs implements ArrayStaticDBIDs {
}
@Override
+ public void assign(int index, DBIDVar var) {
+ inner.assign(index, var);
+ }
+
+ @Override
public int binarySearch(DBIDRef key) {
return inner.binarySearch(key);
}
+
+ /**
+ * Make an existing DBIDMIter unmodifiable.
+ *
+ * @author Erich Schubert
+ */
+ class UnmodifiableDBIDArrayIter implements DBIDArrayIter {
+ /**
+ * Wrapped iterator.
+ */
+ private DBIDArrayIter it;
+
+ /**
+ * Constructor.
+ *
+ * @param it inner iterator
+ */
+ public UnmodifiableDBIDArrayIter(DBIDArrayIter it) {
+ super();
+ this.it = it;
+ }
+
+ @Override
+ public boolean valid() {
+ return it.valid();
+ }
+
+ @Override
+ public void advance() {
+ it.advance();
+ }
+
+ @Override
+ public int internalGetIndex() {
+ return it.internalGetIndex();
+ }
+
+ @Override
+ public void advance(int count) {
+ it.advance(count);
+ }
+
+ @Override
+ public void retract() {
+ it.retract();
+ }
+
+ @Override
+ public void seek(int off) {
+ it.seek(off);
+ }
+
+ @Override
+ public int getOffset() {
+ return it.getOffset();
+ }
+ }
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/database/ids/generic/UnmodifiableDBIDs.java b/src/de/lmu/ifi/dbs/elki/database/ids/generic/UnmodifiableDBIDs.java
index 682b2e4b..fea35692 100644
--- a/src/de/lmu/ifi/dbs/elki/database/ids/generic/UnmodifiableDBIDs.java
+++ b/src/de/lmu/ifi/dbs/elki/database/ids/generic/UnmodifiableDBIDs.java
@@ -23,27 +23,25 @@ package de.lmu.ifi.dbs.elki.database.ids.generic;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.util.Iterator;
-
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDMIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.ids.StaticDBIDs;
-import de.lmu.ifi.dbs.elki.utilities.iterator.UnmodifiableIterator;
/**
* Unmodifiable wrapper for DBIDs.
*
* @author Erich Schubert
*
- * @apiviz.uses de.lmu.ifi.dbs.elki.database.ids.DBIDs
+ * @apiviz.uses DBIDs
+ * @apiviz.has UnmodifiableDBIDIter
*/
public class UnmodifiableDBIDs implements StaticDBIDs {
/**
* The DBIDs we wrap.
*/
- final private DBIDs inner;
+ private final DBIDs inner;
/**
* Constructor.
@@ -65,15 +63,13 @@ public class UnmodifiableDBIDs implements StaticDBIDs {
return inner.isEmpty();
}
- @SuppressWarnings("deprecation")
- @Override
- public Iterator<DBID> iterator() {
- return new UnmodifiableIterator<DBID>(inner.iterator());
- }
-
@Override
public DBIDIter iter() {
- return inner.iter();
+ DBIDIter it = inner.iter();
+ if(it instanceof DBIDMIter) {
+ return new UnmodifiableDBIDIter(it);
+ }
+ return it;
}
@Override
@@ -81,11 +77,45 @@ public class UnmodifiableDBIDs implements StaticDBIDs {
return inner.size();
}
- /**
- * Returns a string representation of the inner DBID collection.
- */
@Override
public String toString() {
return inner.toString();
}
-}
+
+ /**
+ * Make an existing DBIDMIter unmodifiable.
+ *
+ * @author Erich Schubert
+ */
+ class UnmodifiableDBIDIter implements DBIDIter {
+ /**
+ * Wrapped iterator.
+ */
+ private DBIDIter it;
+
+ /**
+ * Constructor.
+ *
+ * @param it inner iterator
+ */
+ public UnmodifiableDBIDIter(DBIDIter it) {
+ super();
+ this.it = it;
+ }
+
+ @Override
+ public boolean valid() {
+ return it.valid();
+ }
+
+ @Override
+ public void advance() {
+ it.advance();
+ }
+
+ @Override
+ public int internalGetIndex() {
+ return it.internalGetIndex();
+ }
+ }
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/database/ids/integer/DistanceIntegerDBIDPair.java b/src/de/lmu/ifi/dbs/elki/database/ids/integer/DistanceIntegerDBIDPair.java
new file mode 100644
index 00000000..01eec02e
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/database/ids/integer/DistanceIntegerDBIDPair.java
@@ -0,0 +1,95 @@
+package de.lmu.ifi.dbs.elki.database.ids.integer;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import de.lmu.ifi.dbs.elki.database.ids.DistanceDBIDPair;
+import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
+import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
+
+/**
+ * Class storing a double distance a DBID.
+ *
+ * @author Erich Schubert
+ *
+ * @param <D> Distance type
+ */
+class DistanceIntegerDBIDPair<D extends Distance<D>> implements DistanceDBIDPair<D>, IntegerDBIDRef {
+ /**
+ * The distance value.
+ */
+ D distance;
+
+ /**
+ * The integer DBID.
+ */
+ int id;
+
+ /**
+ * Constructor.
+ *
+ * @param distance Distance
+ * @param id Object ID
+ */
+ protected DistanceIntegerDBIDPair(D distance, int id) {
+ super();
+ this.distance = distance;
+ this.id = id;
+ }
+
+ @Override
+ public D getDistance() {
+ return distance;
+ }
+
+ @Override
+ public int internalGetIndex() {
+ return id;
+ }
+
+ @Override
+ public int compareByDistance(DistanceDBIDPair<D> o) {
+ return distance.compareTo(o.getDistance());
+ }
+
+ @Override
+ public String toString() {
+ return distance.toString() + ":" + id;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o instanceof DistanceIntegerDBIDPair) {
+ DistanceIntegerDBIDPair<?> p = (DistanceIntegerDBIDPair<?>) o;
+ return (this.id == p.id) && distance.equals(p.getDistance());
+ }
+ if (o instanceof DoubleDistanceIntegerDBIDPair && distance instanceof DoubleDistance) {
+ DoubleDistanceIntegerDBIDPair p = (DoubleDistanceIntegerDBIDPair) o;
+ return (this.id == p.id) && (((DoubleDistance) this.distance).doubleValue() == p.distance);
+ }
+ return false;
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/database/ids/integer/DoubleDistanceIntegerDBIDPair.java b/src/de/lmu/ifi/dbs/elki/database/ids/integer/DoubleDistanceIntegerDBIDPair.java
new file mode 100644
index 00000000..8334d2e3
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/database/ids/integer/DoubleDistanceIntegerDBIDPair.java
@@ -0,0 +1,103 @@
+package de.lmu.ifi.dbs.elki.database.ids.integer;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+import de.lmu.ifi.dbs.elki.database.ids.DistanceDBIDPair;
+import de.lmu.ifi.dbs.elki.database.ids.DoubleDistanceDBIDPair;
+import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
+
+/**
+ * Class storing a double distance a DBID.
+ *
+ * @author Erich Schubert
+ */
+class DoubleDistanceIntegerDBIDPair implements DoubleDistanceDBIDPair, IntegerDBIDRef {
+ /**
+ * The distance value.
+ */
+ final double distance;
+
+ /**
+ * The integer DBID.
+ */
+ final int id;
+
+ /**
+ * Constructor.
+ *
+ * @param distance Distance value
+ * @param id integer object ID
+ */
+ protected DoubleDistanceIntegerDBIDPair(double distance, int id) {
+ super();
+ this.distance = distance;
+ this.id = id;
+ }
+
+ @Override
+ public double doubleDistance() {
+ return distance;
+ }
+
+ @Override
+ @Deprecated
+ public DoubleDistance getDistance() {
+ return new DoubleDistance(distance);
+ }
+
+ @Override
+ public int internalGetIndex() {
+ return id;
+ }
+
+ @Override
+ public int compareByDistance(DistanceDBIDPair<DoubleDistance> o) {
+ if (o instanceof DoubleDistanceDBIDPair) {
+ return Double.compare(distance, ((DoubleDistanceDBIDPair) o).doubleDistance());
+ }
+ return Double.compare(distance, o.getDistance().doubleValue());
+ }
+
+ @Override
+ public String toString() {
+ return distance + ":" + id;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o instanceof DoubleDistanceIntegerDBIDPair) {
+ DoubleDistanceIntegerDBIDPair p = (DoubleDistanceIntegerDBIDPair) o;
+ return (this.id == p.id) && (this.distance == p.distance);
+ }
+ if (o instanceof DistanceIntegerDBIDPair) {
+ DistanceIntegerDBIDPair<?> p = (DistanceIntegerDBIDPair<?>) o;
+ if (p.distance instanceof DoubleDistance) {
+ return (this.id == p.id) && (this.distance == ((DoubleDistance) p.distance).doubleValue());
+ }
+ }
+ return false;
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/database/ids/integer/IntArrayStaticDBIDs.java b/src/de/lmu/ifi/dbs/elki/database/ids/integer/IntArrayStaticDBIDs.java
new file mode 100644
index 00000000..aa3b3cc0
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/database/ids/integer/IntArrayStaticDBIDs.java
@@ -0,0 +1,165 @@
+package de.lmu.ifi.dbs.elki.database.ids.integer;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.Arrays;
+
+import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDFactory;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDVar;
+import de.lmu.ifi.dbs.elki.logging.LoggingUtil;
+
+/**
+ * Static (no modifications allowed) set of Database Object IDs.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.has IntegerDBID
+ */
+public class IntArrayStaticDBIDs implements IntegerArrayStaticDBIDs {
+ /**
+ * The actual storage.
+ */
+ protected int[] ids;
+
+ /**
+ * Constructor.
+ *
+ * @param ids Array of ids.
+ */
+ public IntArrayStaticDBIDs(int... ids) {
+ super();
+ this.ids = ids;
+ }
+
+ @Override
+ public IntegerDBIDArrayIter iter() {
+ return new DBIDItr();
+ }
+
+ /**
+ * DBID iterator in ELKI/C style.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ protected class DBIDItr implements IntegerDBIDArrayIter {
+ /**
+ * Position within array.
+ */
+ int pos = 0;
+
+ @Override
+ public boolean valid() {
+ return pos < ids.length && pos >= 0;
+ }
+
+ @Override
+ public void advance() {
+ pos++;
+ }
+
+ @Override
+ public void advance(int count) {
+ pos += 0;
+ }
+
+ @Override
+ public void retract() {
+ pos--;
+ }
+
+ @Override
+ public void seek(int off) {
+ pos = off;
+ }
+
+ @Override
+ public int getOffset() {
+ return pos;
+ }
+
+ @Override
+ public int internalGetIndex() {
+ return ids[pos];
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if(other instanceof DBID) {
+ LoggingUtil.warning("Programming error detected: DBIDItr.equals(DBID). Use sameDBID()!", new Throwable());
+ }
+ return super.equals(other);
+ }
+
+ @Override
+ public String toString() {
+ return Integer.toString(internalGetIndex());
+ }
+ }
+
+ @Override
+ public int size() {
+ return ids.length;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return ids.length == 0;
+ }
+
+ @Override
+ public boolean contains(DBIDRef o) {
+ final int oid = DBIDUtil.asInteger(o);
+ for(int i = 0; i < ids.length; i++) {
+ if(ids[i] == oid) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public DBID get(int i) {
+ return DBIDFactory.FACTORY.importInteger(ids[i]);
+ }
+
+ @Override
+ public void assign(int i, DBIDVar var) {
+ if (var instanceof IntegerDBIDVar) {
+ ((IntegerDBIDVar)var).internalSetIndex(ids[i]);
+ } else {
+ // Much less efficient:
+ var.set(get(i));
+ }
+ }
+
+ @Override
+ public int binarySearch(DBIDRef key) {
+ return Arrays.binarySearch(ids, DBIDUtil.asInteger(key));
+ }
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/database/ids/integer/IntegerArrayStaticDBIDs.java b/src/de/lmu/ifi/dbs/elki/database/ids/integer/IntegerArrayStaticDBIDs.java
index 90ab5a6a..4bafd343 100644
--- a/src/de/lmu/ifi/dbs/elki/database/ids/integer/IntegerArrayStaticDBIDs.java
+++ b/src/de/lmu/ifi/dbs/elki/database/ids/integer/IntegerArrayStaticDBIDs.java
@@ -23,168 +23,15 @@ package de.lmu.ifi.dbs.elki.database.ids.integer;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.util.AbstractList;
-import java.util.Arrays;
-import java.util.Iterator;
-
import de.lmu.ifi.dbs.elki.database.ids.ArrayStaticDBIDs;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
-import de.lmu.ifi.dbs.elki.database.ids.DBIDFactory;
-import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
-import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
-import de.lmu.ifi.dbs.elki.logging.LoggingUtil;
/**
- * Static (no modifications allowed) set of Database Object IDs.
+ * Combination of {@link ArrayStaticDBIDs} and {@link IntegerDBIDs}.
*
* @author Erich Schubert
- *
- * @apiviz.has IntegerDBID
+ *
*/
-public class IntegerArrayStaticDBIDs extends AbstractList<DBID> implements ArrayStaticDBIDs {
- /**
- * The actual storage.
- */
- protected int[] ids;
-
- /**
- * Constructor
- *
- * @param ids Array of ids.
- */
- public IntegerArrayStaticDBIDs(int... ids) {
- super();
- this.ids = ids;
- }
-
- @Override
- public Iterator<DBID> iterator() {
- return new Itr();
- }
-
- @Override
- public DBIDIter iter() {
- return new DBIDItr();
- }
-
- /**
- * Iterator class.
- *
- * @author Erich Schubert
- *
- * @apiviz.exclude
- */
- protected class Itr implements Iterator<DBID> {
- int off = 0;
-
- @Override
- public boolean hasNext() {
- return off < ids.length;
- }
-
- @Override
- public DBID next() {
- DBID ret = new IntegerDBID(ids[off]);
- off++;
- return ret;
- }
-
- @Override
- public void remove() {
- throw new UnsupportedOperationException();
- }
- }
-
- /**
- * DBID iterator in ELKI/C style.
- *
- * @author Erich Schubert
- *
- * @apiviz.exclude
- */
- protected class DBIDItr implements DBIDIter {
- int pos = 0;
-
- @Override
- public boolean valid() {
- return pos < ids.length;
- }
-
- @Override
- public void advance() {
- pos++;
- }
-
- @Override
- public int getIntegerID() {
- return ids[pos];
- }
-
- @Override
- public DBID getDBID() {
- return new IntegerDBID(ids[pos]);
- }
-
- @Override
- public boolean sameDBID(DBIDRef other) {
- return ids[pos] == other.getIntegerID();
- }
-
- @Override
- public boolean equals(Object other) {
- if (other instanceof DBID) {
- LoggingUtil.warning("Programming error detected: DBIDItr.equals(DBID). Use sameDBID()!", new Throwable());
- }
- return super.equals(other);
- }
-
- @Override
- public int compareDBID(DBIDRef o) {
- int anotherVal = o.getIntegerID();
- return (ids[pos] < anotherVal ? -1 : (ids[pos] == anotherVal ? 0 : 1));
- }
- }
-
- @Override
- public int size() {
- return ids.length;
- }
-
- @Override
- public boolean contains(DBIDRef o) {
- final int oid = o.getIntegerID();
- for(int i = 0; i < ids.length; i++) {
- if(ids[i] == oid) {
- return true;
- }
- }
- return false;
- }
-
- @SuppressWarnings("unchecked")
- @Override
- public <T> T[] toArray(T[] a) {
- T[] r = a;
- if(a.length < ids.length) {
- r = (T[]) java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), ids.length);
- }
- for(int i = 0; i < ids.length; i++) {
- r[i] = (T) DBIDFactory.FACTORY.importInteger(ids[i]);
- }
- // zero-terminate array
- if(r.length > ids.length) {
- r[ids.length] = null;
- }
- return r;
- }
-
- @Override
- public DBID get(int i) {
- return DBIDFactory.FACTORY.importInteger(ids[i]);
- }
-
+public interface IntegerArrayStaticDBIDs extends ArrayStaticDBIDs, IntegerDBIDs {
@Override
- public int binarySearch(DBIDRef key) {
- return Arrays.binarySearch(ids, key.getIntegerID());
- }
+ IntegerDBIDArrayIter iter();
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/database/ids/integer/IntegerDBID.java b/src/de/lmu/ifi/dbs/elki/database/ids/integer/IntegerDBID.java
index 66c1f980..91c00939 100644
--- a/src/de/lmu/ifi/dbs/elki/database/ids/integer/IntegerDBID.java
+++ b/src/de/lmu/ifi/dbs/elki/database/ids/integer/IntegerDBID.java
@@ -24,11 +24,11 @@ package de.lmu.ifi.dbs.elki.database.ids.integer;
*/
import java.nio.ByteBuffer;
-import java.util.Iterator;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
-import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDArrayIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDVar;
import de.lmu.ifi.dbs.elki.logging.LoggingUtil;
import de.lmu.ifi.dbs.elki.persistent.ByteArrayUtil;
import de.lmu.ifi.dbs.elki.persistent.ByteBufferSerializer;
@@ -51,11 +51,11 @@ import de.lmu.ifi.dbs.elki.persistent.FixedSizeByteBufferSerializer;
* @apiviz.composedOf DynamicSerializer
* @apiviz.composedOf StaticSerializer
*/
-final class IntegerDBID implements DBID {
+final class IntegerDBID implements DBID, IntegerDBIDRef {
/**
* The actual object ID.
*/
- final protected int id;
+ protected final int id;
/**
* Constructor from integer id.
@@ -74,12 +74,7 @@ final class IntegerDBID implements DBID {
*/
protected IntegerDBID(Integer id) {
super();
- this.id = id;
- }
-
- @Override
- public DBID getDBID() {
- return this;
+ this.id = id.intValue();
}
/**
@@ -88,7 +83,7 @@ final class IntegerDBID implements DBID {
* @return integer id
*/
@Override
- public int getIntegerID() {
+ public int internalGetIndex() {
return this.id;
}
@@ -103,8 +98,12 @@ final class IntegerDBID implements DBID {
}
@Override
+ @Deprecated
public boolean equals(Object obj) {
- if(!(obj instanceof IntegerDBID)) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof IntegerDBID)) {
if (obj instanceof DBIDRef) {
LoggingUtil.warning("Programming error: DBID.equals(DBIDRef) is not well-defined. use sameDBID!", new Throwable());
}
@@ -113,47 +112,37 @@ final class IntegerDBID implements DBID {
IntegerDBID other = (IntegerDBID) obj;
return this.id == other.id;
}
-
- @Override
- public boolean sameDBID(DBIDRef other) {
- return this.id == other.getIntegerID();
- }
@Override
public int compareTo(DBIDRef o) {
- int thisVal = this.id;
- int anotherVal = o.getIntegerID();
- return (thisVal < anotherVal ? -1 : (thisVal == anotherVal ? 0 : 1));
+ final int anotherVal = o.internalGetIndex();
+ return (this.id < anotherVal ? -1 : (this.id == anotherVal ? 0 : 1));
}
@Override
- public int compareDBID(DBIDRef o) {
- int thisVal = this.id;
- int anotherVal = o.getIntegerID();
- return (thisVal < anotherVal ? -1 : (thisVal == anotherVal ? 0 : 1));
- }
-
- @Override
- public DBIDIter iter() {
+ public DBIDArrayIter iter() {
return new DBIDItr();
}
@Override
public DBID get(int i) {
- if(i != 0) {
+ if (i != 0) {
throw new ArrayIndexOutOfBoundsException();
}
return this;
}
@Override
- public Iterator<DBID> iterator() {
- return new Itr();
+ public void assign(int index, DBIDVar var) {
+ if (index != 0) {
+ throw new ArrayIndexOutOfBoundsException();
+ }
+ var.set(this);
}
@Override
public boolean contains(DBIDRef o) {
- return o.getIntegerID() == id;
+ return o.internalGetIndex() == id;
}
@Override
@@ -163,7 +152,8 @@ final class IntegerDBID implements DBID {
@Override
public int binarySearch(DBIDRef key) {
- return (id == key.getIntegerID()) ? 0 : -1;
+ final int other = key.internalGetIndex();
+ return (other == id) ? 0 : (other < id) ? -1 : -2;
}
/**
@@ -173,61 +163,51 @@ final class IntegerDBID implements DBID {
*
* @apiviz.exclude
*/
- protected class Itr implements Iterator<DBID> {
+ protected class DBIDItr implements DBIDArrayIter, IntegerDBIDRef {
/**
- * Whether we've already returned our object.
+ * Iterator position: We use an integer so we can support retract().
*/
- boolean first = true;
+ int pos = 0;
@Override
- public boolean hasNext() {
- return first == true;
+ public void advance() {
+ pos++;
}
@Override
- public DBID next() {
- assert (first);
- first = false;
- return IntegerDBID.this;
+ public void advance(int count) {
+ pos += count;
}
@Override
- public void remove() {
- throw new UnsupportedOperationException();
+ public void retract() {
+ pos--;
}
- }
-
- /**
- * Pseudo iterator for DBIDs interface.
- *
- * @author Erich Schubert
- *
- * @apiviz.exclude
- */
- protected class DBIDItr implements DBIDIter {
- /**
- * Whether we've already returned our object.
- */
- boolean first = true;
@Override
- public void advance() {
- first = false;
+ public void seek(int off) {
+ pos = off;
}
@Override
- public int getIntegerID() {
- return id;
+ public int getOffset() {
+ return pos;
}
@Override
- public DBID getDBID() {
- return IntegerDBID.this;
+ public int internalGetIndex() {
+ return IntegerDBID.this.id;
}
@Override
public boolean valid() {
- return first;
+ return (pos == 0);
+ }
+
+ @Override
+ public int hashCode() {
+ // Override, because we also are overriding equals.
+ return super.hashCode();
}
@Override
@@ -239,14 +219,8 @@ final class IntegerDBID implements DBID {
}
@Override
- public boolean sameDBID(DBIDRef other) {
- return id == other.getIntegerID();
- }
-
- @Override
- public int compareDBID(DBIDRef o) {
- int anotherVal = o.getIntegerID();
- return (id < anotherVal ? -1 : (id == anotherVal ? 0 : 1));
+ public String toString() {
+ return Integer.toString(internalGetIndex());
}
}
@@ -321,10 +295,10 @@ final class IntegerDBID implements DBID {
/**
* The public instance to use for dynamic serialization.
*/
- public final static DynamicSerializer dynamicSerializer = new DynamicSerializer();
+ public static final ByteBufferSerializer<DBID> DYNAMIC_SERIALIZER = new DynamicSerializer();
/**
* The public instance to use for static serialization.
*/
- public final static StaticSerializer staticSerializer = new StaticSerializer();
-} \ No newline at end of file
+ public static final FixedSizeByteBufferSerializer<DBID> STATIC_SERIALIZER = new StaticSerializer();
+}
diff --git a/src/de/lmu/ifi/dbs/elki/database/ids/integer/IntegerDBIDArrayIter.java b/src/de/lmu/ifi/dbs/elki/database/ids/integer/IntegerDBIDArrayIter.java
new file mode 100644
index 00000000..b0e2d339
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/database/ids/integer/IntegerDBIDArrayIter.java
@@ -0,0 +1,34 @@
+package de.lmu.ifi.dbs.elki.database.ids.integer;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+import de.lmu.ifi.dbs.elki.database.ids.DBIDArrayIter;
+
+/**
+ * Modifiable integer array iterator.
+ *
+ * @author Erich Schubert
+ */
+public interface IntegerDBIDArrayIter extends IntegerDBIDIter, DBIDArrayIter {
+ // Empty
+}
diff --git a/src/de/lmu/ifi/dbs/elki/database/ids/integer/IntegerDBIDArrayMIter.java b/src/de/lmu/ifi/dbs/elki/database/ids/integer/IntegerDBIDArrayMIter.java
new file mode 100644
index 00000000..21616f75
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/database/ids/integer/IntegerDBIDArrayMIter.java
@@ -0,0 +1,34 @@
+package de.lmu.ifi.dbs.elki.database.ids.integer;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+import de.lmu.ifi.dbs.elki.database.ids.DBIDArrayMIter;
+
+/**
+ * Modifiable integer array iterator.
+ *
+ * @author Erich Schubert
+ */
+public interface IntegerDBIDArrayMIter extends IntegerDBIDArrayIter, IntegerDBIDMIter, DBIDArrayMIter {
+ // Empty
+}
diff --git a/src/de/lmu/ifi/dbs/elki/database/ids/integer/IntegerDBIDArrayQuickSort.java b/src/de/lmu/ifi/dbs/elki/database/ids/integer/IntegerDBIDArrayQuickSort.java
new file mode 100644
index 00000000..2c096ab9
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/database/ids/integer/IntegerDBIDArrayQuickSort.java
@@ -0,0 +1,250 @@
+package de.lmu.ifi.dbs.elki.database.ids.integer;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.Comparator;
+
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
+
+/**
+ * Class to sort an integer DBID array, using a modified quicksort.
+ *
+ * Two array iterators will be used to seek to the elements to compare, while
+ * the backing storage is a plain integer array.
+ *
+ * The implementation is closely based on:
+ * <p>
+ * Dual-Pivot Quicksort<br />
+ * Vladimir Yaroslavskiy
+ * </p>
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.uses TroveArrayModifiableDBIDs
+ */
+@Reference(authors = "Vladimir Yaroslavskiy", title = "Dual-Pivot Quicksort", booktitle = "http://iaroslavski.narod.ru/quicksort/", url = "http://iaroslavski.narod.ru/quicksort/")
+class IntegerDBIDArrayQuickSort {
+ /**
+ * Threshold for using insertion sort. Value taken from Javas QuickSort,
+ * assuming that it will be similar for DBIDs.
+ */
+ private static final int INSERTION_THRESHOLD = 47;
+
+ /**
+ * Sort the full array using the given comparator.
+ *
+ * @param data Data to sort
+ * @param comp Comparator
+ */
+ public static void sort(int[] data, Comparator<? super DBIDRef> comp) {
+ sort(data, 0, data.length, comp);
+ }
+
+ /**
+ * Sort the array using the given comparator.
+ *
+ * @param data Data to sort
+ * @param start First index
+ * @param end Last index (exclusive)
+ * @param comp Comparator
+ */
+ public static void sort(int[] data, int start, int end, Comparator<? super DBIDRef> comp) {
+ quickSort(data, start, end - 1, comp, new IntegerDBIDVar(), new IntegerDBIDVar(), new IntegerDBIDVar());
+ }
+
+ /**
+ * Actual recursive sort function.
+ *
+ * @param data Data to sort
+ * @param start First index
+ * @param end Last index (inclusive!)
+ * @param comp Comparator
+ * @param vl First seeking iterator
+ * @param vk Second seeking iterator
+ * @param vr Third seeking iterator
+ */
+ private static void quickSort(int[] data, final int start, final int end, Comparator<? super DBIDRef> comp, IntegerDBIDVar vl, IntegerDBIDVar vk, IntegerDBIDVar vr) {
+ final int len = end - start;
+ if (len < INSERTION_THRESHOLD) {
+ // Classic insertion sort.
+ for (int i = start + 1; i <= end; i++) {
+ for (int j = i; j > start; j--) {
+ vl.internalSetIndex(data[j]);
+ vr.internalSetIndex(data[j - 1]);
+ if (comp.compare(vl, vr) < 0) {
+ int tmp = data[j - 1];
+ data[j - 1] = data[j];
+ data[j] = tmp;
+ } else {
+ break;
+ }
+ }
+ }
+ return;
+ }
+
+ // Choose pivots by looking at five candidates.
+ final int seventh = (len >> 3) + (len >> 6) + 1;
+ final int m3 = (start + end) >> 1; // middle
+ final int m2 = m3 - seventh;
+ final int m1 = m2 - seventh;
+ final int m4 = m3 + seventh;
+ final int m5 = m4 + seventh;
+
+ // Explicit (and optimal) sorting network for 5 elements
+ // See Knuth for details.
+ if (compare(vl, data[m1], vk, data[m2], comp) > 0) {
+ int tmp = data[m2];
+ data[m2] = data[m1];
+ data[m1] = tmp;
+ }
+ if (compare(vl, data[m1], vk, data[m3], comp) > 0) {
+ int tmp = data[m3];
+ data[m3] = data[m1];
+ data[m1] = tmp;
+ }
+ if (compare(vl, data[m2], vk, data[m3], comp) > 0) {
+ int tmp = data[m3];
+ data[m3] = data[m2];
+ data[m2] = tmp;
+ }
+ if (compare(vl, data[m4], vk, data[m5], comp) > 0) {
+ int tmp = data[m5];
+ data[m5] = data[m4];
+ data[m4] = tmp;
+ }
+ if (compare(vl, data[m1], vk, data[m4], comp) > 0) {
+ int tmp = data[m4];
+ data[m4] = data[m1];
+ data[m1] = tmp;
+ }
+ if (compare(vl, data[m3], vk, data[m4], comp) > 0) {
+ int tmp = data[m4];
+ data[m4] = data[m3];
+ data[m3] = tmp;
+ }
+ if (compare(vl, data[m2], vk, data[m5], comp) > 0) {
+ int tmp = data[m5];
+ data[m5] = data[m2];
+ data[m2] = tmp;
+ }
+ if (compare(vl, data[m2], vk, data[m3], comp) > 0) {
+ int tmp = data[m3];
+ data[m3] = data[m2];
+ data[m2] = tmp;
+ }
+ if (compare(vl, data[m4], vk, data[m5], comp) > 0) {
+ int tmp = data[m5];
+ data[m5] = data[m4];
+ data[m4] = tmp;
+ }
+
+ // Choose the 2 and 4th as pivots, as we want to get three parts
+ // Copy to variables v1 and v3, replace them with the start and end
+ // Note: do not modify v1 or v3 until we put them back!
+ vl.internalSetIndex(data[m2]);
+ vr.internalSetIndex(data[m4]);
+ data[m2] = data[start];
+ data[m4] = data[end];
+
+ // A tie is when the two chosen pivots are the same
+ final boolean tied = comp.compare(vl, vr) == 0;
+
+ // Insertion points for pivot areas.
+ int left = start + 1;
+ int right = end - 1;
+
+ // Note: we merged the ties and no ties cases.
+ // This likely is marginally slower, but not at a macro level
+ // And you never know with hotspot.
+ for (int k = left; k <= right; k++) {
+ int tmp = data[k];
+ vk.internalSetIndex(tmp);
+ final int c = comp.compare(vk, vl);
+ if (c == 0) {
+ continue;
+ } else if (c < 0) {
+ // Traditional quicksort
+ data[k] = data[left];
+ data[left] = tmp;
+ left++;
+ } else if (tied || comp.compare(vk, vr) > 0) {
+ // Now look at the right. First skip correct entries there, too
+ while (true) {
+ vk.internalSetIndex(data[right]);
+ if (comp.compare(vk, vr) > 0 && k < right) {
+ right--;
+ } else {
+ break;
+ }
+ }
+ // Now move tmp from k to the right.
+ data[k] = data[right];
+ data[right] = tmp;
+ right--;
+ // Test the element we just inserted: left or center?
+ vk.internalSetIndex(data[k]);
+ if (comp.compare(vk, vl) < 0) {
+ tmp = data[k];
+ data[k] = data[left];
+ data[left] = tmp;
+ left++;
+ } // else: center. cannot be on right.
+ }
+ }
+ // Put the pivot elements back in.
+ // Remember: we must not modify v1 and v3 above.
+ data[start] = data[left - 1];
+ data[left - 1] = vl.internalGetIndex();
+ data[end] = data[right + 1];
+ data[right + 1] = vr.internalGetIndex();
+ // v1 and v3 are now safe to modify again. Perform recursion:
+ quickSort(data, start, left - 2, comp, vl, vk, vr);
+ // Handle the middle part - if necessary:
+ if (!tied) {
+ // TODO: the original publication had a special tie handling here.
+ // It shouldn't affect correctness, but probably improves situations
+ // with a lot of tied elements.
+ quickSort(data, left, right, comp, vl, vk, vr);
+ }
+ quickSort(data, right + 2, end, comp, vl, vk, vr);
+ }
+
+ /**
+ * Compare two elements.
+ *
+ * @param i1 First scratch variable
+ * @param p1 Value for first
+ * @param i2 Second scratch variable
+ * @param p2 Value for second
+ * @param comp Comparator
+ * @return Comparison result
+ */
+ private static int compare(IntegerDBIDVar i1, int p1, IntegerDBIDVar i2, int p2, Comparator<? super DBIDRef> comp) {
+ i1.internalSetIndex(p1);
+ i2.internalSetIndex(p2);
+ return comp.compare(i1, i2);
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/database/ids/integer/IntegerDBIDIter.java b/src/de/lmu/ifi/dbs/elki/database/ids/integer/IntegerDBIDIter.java
new file mode 100644
index 00000000..9b50d544
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/database/ids/integer/IntegerDBIDIter.java
@@ -0,0 +1,34 @@
+package de.lmu.ifi.dbs.elki.database.ids.integer;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+
+/**
+ * Iterator for integer DBIDs.
+ *
+ * @author Erich Schubert
+ */
+public interface IntegerDBIDIter extends IntegerDBIDRef, DBIDIter {
+ // Empty
+}
diff --git a/src/de/lmu/ifi/dbs/elki/database/ids/integer/IntegerDBIDMIter.java b/src/de/lmu/ifi/dbs/elki/database/ids/integer/IntegerDBIDMIter.java
new file mode 100644
index 00000000..2e339dbc
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/database/ids/integer/IntegerDBIDMIter.java
@@ -0,0 +1,34 @@
+package de.lmu.ifi.dbs.elki.database.ids.integer;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+import de.lmu.ifi.dbs.elki.database.ids.DBIDMIter;
+
+/**
+ * Modifiable iterator interface for integer DBIDs.
+ *
+ * @author Erich Schubert
+ */
+public interface IntegerDBIDMIter extends DBIDMIter, IntegerDBIDIter {
+ // Empty.
+}
diff --git a/src/de/lmu/ifi/dbs/elki/database/ids/integer/IntegerDBIDRange.java b/src/de/lmu/ifi/dbs/elki/database/ids/integer/IntegerDBIDRange.java
index cad771d9..85c47f43 100644
--- a/src/de/lmu/ifi/dbs/elki/database/ids/integer/IntegerDBIDRange.java
+++ b/src/de/lmu/ifi/dbs/elki/database/ids/integer/IntegerDBIDRange.java
@@ -23,30 +23,29 @@ package de.lmu.ifi.dbs.elki.database.ids.integer;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.util.AbstractList;
-import java.util.Iterator;
-
import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDArrayIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDFactory;
-import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRange;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDVar;
/**
- * Representing a DBID range allocation
+ * Representing a DBID range allocation.
*
* @author Erich Schubert
*
* @apiviz.has IntegerDBID
*/
-class IntegerDBIDRange extends AbstractList<DBID> implements DBIDRange {
+class IntegerDBIDRange implements DBIDRange {
/**
- * Start value
+ * Start value.
*/
protected final int start;
/**
- * Length value
+ * Length value.
*/
protected final int len;
@@ -68,71 +67,83 @@ class IntegerDBIDRange extends AbstractList<DBID> implements DBIDRange {
}
@Override
- public Iterator<DBID> iterator() {
- return new Itr();
+ public boolean isEmpty() {
+ return len == 0;
}
@Override
- public DBIDIter iter() {
- return new DBIDItr();
+ public DBIDArrayIter iter() {
+ return new DBIDItr(start, len);
}
/**
- * Iterator class.
+ * Iterator in ELKI/C++ style.
*
* @author Erich Schubert
*
* @apiviz.exclude
*/
- protected class Itr implements Iterator<DBID> {
+ protected static class DBIDItr implements DBIDArrayIter, IntegerDBIDRef {
+ /**
+ * Current position.
+ */
int pos = 0;
+
+ /**
+ * Interval length.
+ */
+ final int len;
+
+ /**
+ * Interval start.
+ */
+ final int start;
+
+ /**
+ * Constructor.
+ *
+ * @param start Interval start
+ * @param len Interval length
+ */
+ DBIDItr(int start, int len) {
+ super();
+ this.start = start;
+ this.len = len;
+ }
@Override
- public boolean hasNext() {
- return pos < len;
+ public boolean valid() {
+ return pos < len && pos >= 0;
}
@Override
- public DBID next() {
- DBID ret = new IntegerDBID(pos + start);
+ public void advance() {
pos++;
- return ret;
}
@Override
- public void remove() {
- throw new UnsupportedOperationException("CompactStaticDBIDs is read-only.");
+ public void advance(int count) {
+ pos += count;
}
- }
-
- /**
- * Iterator in ELKI/C++ style.
- *
- * @author Erich Schubert
- *
- * @apiviz.exclude
- */
- protected class DBIDItr implements DBIDIter {
- int pos = 0;
@Override
- public boolean valid() {
- return pos < len;
+ public void retract() {
+ pos--;
}
@Override
- public void advance() {
- pos++;
+ public void seek(int off) {
+ pos = off;
}
@Override
- public int getIntegerID() {
- return start + pos;
+ public int getOffset() {
+ return pos;
}
@Override
- public DBID getDBID() {
- return new IntegerDBID(start + pos);
+ public int internalGetIndex() {
+ return start + pos;
}
@Override
@@ -141,20 +152,14 @@ class IntegerDBIDRange extends AbstractList<DBID> implements DBIDRange {
}
@Override
- public boolean sameDBID(DBIDRef other) {
- return start + pos == other.getIntegerID();
- }
-
- @Override
- public int compareDBID(DBIDRef o) {
- int anotherVal = o.getIntegerID();
- return (start + pos < anotherVal ? -1 : (start + pos == anotherVal ? 0 : 1));
+ public String toString() {
+ return Integer.toString(internalGetIndex());
}
}
@Override
public boolean contains(DBIDRef o) {
- int oid = o.getIntegerID();
+ int oid = DBIDUtil.asInteger(o);
if(oid < start) {
return false;
}
@@ -164,23 +169,6 @@ class IntegerDBIDRange extends AbstractList<DBID> implements DBIDRange {
return true;
}
- @SuppressWarnings("unchecked")
- @Override
- public <T> T[] toArray(T[] a) {
- T[] r = a;
- if(a.length < start) {
- r = (T[]) java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), len);
- }
- for(int i = 0; i < start; i++) {
- r[i] = (T) DBIDFactory.FACTORY.importInteger(len + i);
- }
- // zero-terminate array
- if(r.length > len) {
- r[len] = null;
- }
- return r;
- }
-
@Override
public DBID get(int i) {
if(i > len || i < 0) {
@@ -192,17 +180,27 @@ class IntegerDBIDRange extends AbstractList<DBID> implements DBIDRange {
/**
* For storage array offsets.
*
- * @param dbid
+ * @param dbid ID reference
* @return array offset
*/
@Override
public int getOffset(DBIDRef dbid) {
- return dbid.getIntegerID() - start;
+ return dbid.internalGetIndex() - start;
+ }
+
+ @Override
+ public void assign(int index, DBIDVar var) {
+ if (var instanceof IntegerDBIDVar) {
+ ((IntegerDBIDVar)var).internalSetIndex(start + index);
+ } else {
+ // Much less efficient:
+ var.set(get(index));
+ }
}
@Override
public int binarySearch(DBIDRef key) {
- int keyid = key.getIntegerID();
+ int keyid = DBIDUtil.asInteger(key);
if(keyid < start) {
return -1;
}
@@ -212,4 +210,14 @@ class IntegerDBIDRange extends AbstractList<DBID> implements DBIDRange {
}
return -(len + 1);
}
+
+ @Override
+ public String toString() {
+ return "[" + start + " to " + (start + len - 1) + "]";
+ }
+
+ @Override
+ public int mapDBIDToOffset(DBIDRef dbid) {
+ return dbid.internalGetIndex() - start;
+ }
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/database/ids/integer/IntegerDBIDRef.java b/src/de/lmu/ifi/dbs/elki/database/ids/integer/IntegerDBIDRef.java
new file mode 100644
index 00000000..45854440
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/database/ids/integer/IntegerDBIDRef.java
@@ -0,0 +1,41 @@
+package de.lmu.ifi.dbs.elki.database.ids.integer;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+
+/**
+ * DBID reference that references an integer value.
+ *
+ * @author Erich Schubert
+ */
+interface IntegerDBIDRef extends DBIDRef {
+ /**
+ * Return the integer value of the object ID.
+ *
+ * @return integer id
+ */
+ @Override
+ int internalGetIndex();
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/database/ids/integer/IntegerDBIDVar.java b/src/de/lmu/ifi/dbs/elki/database/ids/integer/IntegerDBIDVar.java
new file mode 100644
index 00000000..73d164e0
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/database/ids/integer/IntegerDBIDVar.java
@@ -0,0 +1,198 @@
+package de.lmu.ifi.dbs.elki.database.ids.integer;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDArrayIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDVar;
+import de.lmu.ifi.dbs.elki.logging.LoggingUtil;
+
+/**
+ * Variable for storing a single DBID reference.
+ *
+ * TODO: what is the actual memory cost for adding a flag to indicate "null"
+ * values, to allow the variable to be unset? Given 8-byte alignment of Java, it
+ * should come for free!
+ *
+ * @author Erich Schubert
+ */
+class IntegerDBIDVar implements DBIDVar {
+ /**
+ * The actual value.
+ */
+ int id;
+
+ /**
+ * Constructor.
+ */
+ protected IntegerDBIDVar() {
+ this.id = -1;
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param val
+ */
+ protected IntegerDBIDVar(DBIDRef val) {
+ this.id = val.internalGetIndex();
+ }
+
+ @Override
+ public int internalGetIndex() {
+ return id;
+ }
+
+ /**
+ * Internal set to integer.
+ *
+ * @param i integer value
+ */
+ protected void internalSetIndex(int i) {
+ id = i;
+ }
+
+ @Override
+ public void set(DBIDRef ref) {
+ id = ref.internalGetIndex();
+ }
+
+ @Override
+ public DBID get(int i) {
+ if (i != 0) {
+ throw new ArrayIndexOutOfBoundsException();
+ }
+ return new IntegerDBID(i);
+ }
+
+ @Override
+ public DBIDArrayIter iter() {
+ return new DBIDItr();
+ }
+
+ @Override
+ public int size() {
+ return 1;
+ }
+
+ @Override
+ public int binarySearch(DBIDRef key) {
+ final int other = key.internalGetIndex();
+ return (other == id) ? 0 : (other < id) ? -1 : -2;
+ }
+
+ @Override
+ public boolean contains(DBIDRef o) {
+ return id == o.internalGetIndex();
+ }
+
+ /**
+ * Pseudo iterator for DBIDs interface.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ protected class DBIDItr implements DBIDArrayIter, IntegerDBIDRef {
+ /**
+ * Iterator position: We use an integer so we can support retract().
+ */
+ int pos = 0;
+
+ @Override
+ public void advance() {
+ pos++;
+ }
+
+ @Override
+ public void advance(int count) {
+ pos += count;
+ }
+
+ @Override
+ public void retract() {
+ pos--;
+ }
+
+ @Override
+ public void seek(int off) {
+ pos = off;
+ }
+
+ @Override
+ public int getOffset() {
+ return pos;
+ }
+
+ @Override
+ public int internalGetIndex() {
+ return IntegerDBIDVar.this.id;
+ }
+
+ @Override
+ public boolean valid() {
+ return (pos == 0);
+ }
+
+ @Override
+ public int hashCode() {
+ // Override, because we also are overriding equals.
+ return super.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof DBID) {
+ LoggingUtil.warning("Programming error detected: DBIDItr.equals(DBID). Use sameDBID()!", new Throwable());
+ }
+ return super.equals(other);
+ }
+
+ @Override
+ public String toString() {
+ return Integer.toString(internalGetIndex());
+ }
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return false;
+ }
+
+ @Override
+ public void assign(int i, DBIDVar var) {
+ if (var instanceof IntegerDBIDVar) {
+ ((IntegerDBIDVar)var).internalSetIndex(i);
+ } else {
+ // Much less efficient:
+ var.set(get(i));
+ }
+ }
+
+ @Override
+ public String toString() {
+ return Integer.toString(id);
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/database/ids/integer/IntegerDBIDs.java b/src/de/lmu/ifi/dbs/elki/database/ids/integer/IntegerDBIDs.java
new file mode 100644
index 00000000..8ffb9c6b
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/database/ids/integer/IntegerDBIDs.java
@@ -0,0 +1,35 @@
+package de.lmu.ifi.dbs.elki.database.ids.integer;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
+
+/**
+ * Integer DBID collection.
+ *
+ * @author Erich Schubert
+ */
+public interface IntegerDBIDs extends DBIDs {
+ @Override
+ IntegerDBIDIter iter();
+}
diff --git a/src/de/lmu/ifi/dbs/elki/database/ids/integer/IntegerDoubleDBIDPair.java b/src/de/lmu/ifi/dbs/elki/database/ids/integer/IntegerDoubleDBIDPair.java
new file mode 100644
index 00000000..fe8c6a60
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/database/ids/integer/IntegerDoubleDBIDPair.java
@@ -0,0 +1,83 @@
+package de.lmu.ifi.dbs.elki.database.ids.integer;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.database.ids.DoubleDBIDPair;
+
+/**
+ * Pair containing a double value and an integer DBID.
+ *
+ * @author Erich Schubert
+ */
+class IntegerDoubleDBIDPair implements DoubleDBIDPair, IntegerDBIDRef {
+ /**
+ * The double value.
+ */
+ double value;
+
+ /**
+ * The DB id.
+ */
+ int id;
+
+ /**
+ * Constructor.
+ *
+ * @param value Double value
+ * @param id DBID
+ */
+ protected IntegerDoubleDBIDPair(double value, int id) {
+ super();
+ this.value = value;
+ this.id = id;
+ }
+
+ @Override
+ public int internalGetIndex() {
+ return id;
+ }
+
+ @Override
+ public int compareTo(DoubleDBIDPair o) {
+ return Double.compare(value, o.doubleValue());
+ }
+
+ @Override
+ public double doubleValue() {
+ return value;
+ }
+
+ @Override
+ @Deprecated
+ public Double getFirst() {
+ return new Double(value);
+ }
+
+ @Override
+ @Deprecated
+ public DBID getSecond() {
+ return new IntegerDBID(id);
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/database/ids/integer/ReusingDBIDFactory.java b/src/de/lmu/ifi/dbs/elki/database/ids/integer/ReusingDBIDFactory.java
index 01e42fe9..f6df2e0d 100644
--- a/src/de/lmu/ifi/dbs/elki/database/ids/integer/ReusingDBIDFactory.java
+++ b/src/de/lmu/ifi/dbs/elki/database/ids/integer/ReusingDBIDFactory.java
@@ -29,6 +29,7 @@ import java.util.BitSet;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDFactory;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRange;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.logging.Logging;
/**
@@ -45,19 +46,20 @@ public class ReusingDBIDFactory extends SimpleDBIDFactory {
/**
* Logging for error messages.
*/
- private static final Logging logger = Logging.getLogger(ReusingDBIDFactory.class);
-
+ private static final Logging LOG = Logging.getLogger(ReusingDBIDFactory.class);
+
/**
* Bit set to keep track of dynamic DBIDs
*/
BitSet dynamicUsed = new BitSet();
-
+
/**
* Keep track of the lowest unused dynamic DBID
*/
int dynamicStart = 0;
- // TODO: add an offset, to save keeping long bit sets of 1s for heavy dynamic use?
+ // TODO: add an offset, to save keeping long bit sets of 1s for heavy dynamic
+ // use?
/**
* Returned range allocations
@@ -79,12 +81,13 @@ public class ReusingDBIDFactory extends SimpleDBIDFactory {
}
@Override
- public synchronized void deallocateSingleDBID(DBID id) {
- if (id.getIntegerID() >= 0) {
- logger.warning("Single DBID returned is from a range allocation!");
+ public synchronized void deallocateSingleDBID(DBIDRef id) {
+ final int intid = id.internalGetIndex();
+ if (intid >= 0) {
+ LOG.warning("Single DBID returned is from a range allocation!");
return;
}
- int pos = - id.getIntegerID() - 1;
+ final int pos = -intid - 1;
dynamicUsed.clear(pos);
dynamicStart = Math.min(dynamicStart, pos);
}
@@ -113,6 +116,6 @@ public class ReusingDBIDFactory extends SimpleDBIDFactory {
@Override
public synchronized void deallocateDBIDRange(DBIDRange range) {
// TODO: catch an eventual cast exception?
- returnedAllocations.add((IntegerDBIDRange)range);
+ returnedAllocations.add((IntegerDBIDRange) range);
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/database/ids/integer/SimpleDBIDFactory.java b/src/de/lmu/ifi/dbs/elki/database/ids/integer/SimpleDBIDFactory.java
index 8003e4ae..77c1a91b 100644
--- a/src/de/lmu/ifi/dbs/elki/database/ids/integer/SimpleDBIDFactory.java
+++ b/src/de/lmu/ifi/dbs/elki/database/ids/integer/SimpleDBIDFactory.java
@@ -29,8 +29,14 @@ import de.lmu.ifi.dbs.elki.database.ids.DBIDFactory;
import de.lmu.ifi.dbs.elki.database.ids.DBIDPair;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRange;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDVar;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
+import de.lmu.ifi.dbs.elki.database.ids.DistanceDBIDPair;
+import de.lmu.ifi.dbs.elki.database.ids.DoubleDBIDPair;
+import de.lmu.ifi.dbs.elki.database.ids.DoubleDistanceDBIDPair;
import de.lmu.ifi.dbs.elki.database.ids.HashSetModifiableDBIDs;
+import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
+import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
import de.lmu.ifi.dbs.elki.persistent.ByteBufferSerializer;
import de.lmu.ifi.dbs.elki.persistent.FixedSizeByteBufferSerializer;
import de.lmu.ifi.dbs.elki.utilities.exceptions.AbortException;
@@ -52,7 +58,7 @@ import de.lmu.ifi.dbs.elki.utilities.exceptions.AbortException;
*/
public class SimpleDBIDFactory implements DBIDFactory {
/**
- * Keep track of the smallest dynamic DBID offset not used
+ * Keep track of the smallest dynamic DBID offset not used.
*/
int dynamicids = 0;
@@ -60,9 +66,14 @@ public class SimpleDBIDFactory implements DBIDFactory {
* The starting point for static DBID range allocations.
*/
int rangestart = 0;
+
+ /**
+ * Invalid ID.
+ */
+ DBID invalid = new IntegerDBID(Integer.MIN_VALUE);
/**
- * Constructor
+ * Constructor.
*/
public SimpleDBIDFactory() {
super();
@@ -78,8 +89,8 @@ public class SimpleDBIDFactory implements DBIDFactory {
}
@Override
- public void deallocateSingleDBID(DBID id) {
- // ignore.
+ public void deallocateSingleDBID(DBIDRef id) {
+ // ignore for now.
}
@Override
@@ -103,6 +114,37 @@ public class SimpleDBIDFactory implements DBIDFactory {
}
@Override
+ public void assignVar(DBIDVar var, int val) {
+ if (var instanceof IntegerDBIDVar) {
+ ((IntegerDBIDVar)var).internalSetIndex(val);
+ } else {
+ var.set(new IntegerDBID(val));
+ }
+ }
+
+ @Override
+ public int compare(DBIDRef a, DBIDRef b) {
+ final int inta = a.internalGetIndex();
+ final int intb = b.internalGetIndex();
+ return (inta < intb ? -1 : (inta == intb ? 0 : 1));
+ }
+
+ @Override
+ public boolean equal(DBIDRef a, DBIDRef b) {
+ return a.internalGetIndex() == b.internalGetIndex();
+ }
+
+ @Override
+ public String toString(DBIDRef id) {
+ return Integer.toString(id.internalGetIndex());
+ }
+
+ @Override
+ public DBIDVar newVar(DBIDRef val) {
+ return new IntegerDBIDVar(val);
+ }
+
+ @Override
public ArrayModifiableDBIDs newArray() {
return new TroveArrayModifiableDBIDs();
}
@@ -133,22 +175,46 @@ public class SimpleDBIDFactory implements DBIDFactory {
}
@Override
- public DBIDPair makePair(DBIDRef first, DBIDRef second) {
- return new IntegerDBIDPair(first.getIntegerID(), second.getIntegerID());
+ public DBIDPair newPair(DBIDRef first, DBIDRef second) {
+ return new IntegerDBIDPair(first.internalGetIndex(), second.internalGetIndex());
+ }
+
+ @Override
+ public DoubleDBIDPair newPair(double val, DBIDRef id) {
+ return new IntegerDoubleDBIDPair(val, id.internalGetIndex());
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <D extends Distance<D>> DistanceDBIDPair<D> newDistancePair(D val, DBIDRef id) {
+ if (val instanceof DoubleDistance) {
+ return (DistanceDBIDPair<D>) new DoubleDistanceIntegerDBIDPair(((DoubleDistance) val).doubleValue(), id.internalGetIndex());
+ }
+ return new DistanceIntegerDBIDPair<D>(val, id.internalGetIndex());
+ }
+
+ @Override
+ public DoubleDistanceDBIDPair newDistancePair(double val, DBIDRef id) {
+ return new DoubleDistanceIntegerDBIDPair(val, id.internalGetIndex());
}
@Override
public ByteBufferSerializer<DBID> getDBIDSerializer() {
- return IntegerDBID.dynamicSerializer;
+ return IntegerDBID.DYNAMIC_SERIALIZER;
}
@Override
public FixedSizeByteBufferSerializer<DBID> getDBIDSerializerStatic() {
- return IntegerDBID.staticSerializer;
+ return IntegerDBID.STATIC_SERIALIZER;
}
@Override
public Class<? extends DBID> getTypeRestriction() {
return IntegerDBID.class;
}
+
+ @Override
+ public DBIDRef invalid() {
+ return invalid;
+ }
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/database/ids/integer/TrivialDBIDFactory.java b/src/de/lmu/ifi/dbs/elki/database/ids/integer/TrivialDBIDFactory.java
index 80f65f29..a9d8aa90 100644
--- a/src/de/lmu/ifi/dbs/elki/database/ids/integer/TrivialDBIDFactory.java
+++ b/src/de/lmu/ifi/dbs/elki/database/ids/integer/TrivialDBIDFactory.java
@@ -31,15 +31,21 @@ import de.lmu.ifi.dbs.elki.database.ids.DBIDFactory;
import de.lmu.ifi.dbs.elki.database.ids.DBIDPair;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRange;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDVar;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
+import de.lmu.ifi.dbs.elki.database.ids.DistanceDBIDPair;
+import de.lmu.ifi.dbs.elki.database.ids.DoubleDBIDPair;
+import de.lmu.ifi.dbs.elki.database.ids.DoubleDistanceDBIDPair;
import de.lmu.ifi.dbs.elki.database.ids.HashSetModifiableDBIDs;
+import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
+import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
import de.lmu.ifi.dbs.elki.persistent.ByteBufferSerializer;
import de.lmu.ifi.dbs.elki.persistent.FixedSizeByteBufferSerializer;
import de.lmu.ifi.dbs.elki.utilities.exceptions.AbortException;
/**
- * Trivial DBID management, that never reuses IDs and just gives them out in sequence.
- * Statically allocated DBID ranges are given positive values,
+ * Trivial DBID management, that never reuses IDs and just gives them out in
+ * sequence. Statically allocated DBID ranges are given positive values,
* Dynamically allocated DBIDs are given negative values.
*
* @author Erich Schubert
@@ -52,21 +58,26 @@ import de.lmu.ifi.dbs.elki.utilities.exceptions.AbortException;
* @apiviz.uses TroveArrayModifiableDBIDs oneway - - «create»
* @apiviz.uses TroveHashSetModifiableDBIDs oneway - - «create»
*/
-public class TrivialDBIDFactory implements DBIDFactory {
+final public class TrivialDBIDFactory implements DBIDFactory {
/**
- * Keep track of the smallest dynamic DBID offset not used
+ * Keep track of the smallest dynamic DBID offset not used.
*/
AtomicInteger next = new AtomicInteger(1);
-
+
/**
- * Constructor
+ * Invalid ID.
+ */
+ DBID invalid = new IntegerDBID(Integer.MIN_VALUE);
+
+ /**
+ * Constructor.
*/
public TrivialDBIDFactory() {
super();
}
@Override
- public DBID generateSingleDBID() {
+ final public DBID generateSingleDBID() {
final int id = next.getAndIncrement();
if (id == Integer.MAX_VALUE) {
throw new AbortException("DBID allocation error - too many objects allocated!");
@@ -76,12 +87,12 @@ public class TrivialDBIDFactory implements DBIDFactory {
}
@Override
- public void deallocateSingleDBID(DBID id) {
- // ignore.
+ final public void deallocateSingleDBID(DBIDRef id) {
+ // ignore for now
}
@Override
- public DBIDRange generateStaticDBIDRange(int size) {
+ final public DBIDRange generateStaticDBIDRange(int size) {
final int start = next.getAndAdd(size);
if (start > next.get()) {
throw new AbortException("DBID range allocation error - too many objects allocated!");
@@ -101,6 +112,37 @@ public class TrivialDBIDFactory implements DBIDFactory {
}
@Override
+ public void assignVar(DBIDVar var, int val) {
+ if (var instanceof IntegerDBIDVar) {
+ ((IntegerDBIDVar)var).internalSetIndex(val);
+ } else {
+ var.set(new IntegerDBID(val));
+ }
+ }
+
+ @Override
+ public int compare(DBIDRef a, DBIDRef b) {
+ final int inta = a.internalGetIndex();
+ final int intb = b.internalGetIndex();
+ return (inta < intb ? -1 : (inta == intb ? 0 : 1));
+ }
+
+ @Override
+ public boolean equal(DBIDRef a, DBIDRef b) {
+ return a.internalGetIndex() == b.internalGetIndex();
+ }
+
+ @Override
+ public String toString(DBIDRef id) {
+ return Integer.toString(id.internalGetIndex());
+ }
+
+ @Override
+ public DBIDVar newVar(DBIDRef val) {
+ return new IntegerDBIDVar(val);
+ }
+
+ @Override
public ArrayModifiableDBIDs newArray() {
return new TroveArrayModifiableDBIDs();
}
@@ -131,22 +173,46 @@ public class TrivialDBIDFactory implements DBIDFactory {
}
@Override
- public DBIDPair makePair(DBIDRef first, DBIDRef second) {
- return new IntegerDBIDPair(first.getIntegerID(), second.getIntegerID());
+ public DBIDPair newPair(DBIDRef first, DBIDRef second) {
+ return new IntegerDBIDPair(first.internalGetIndex(), second.internalGetIndex());
+ }
+
+ @Override
+ public DoubleDBIDPair newPair(double val, DBIDRef id) {
+ return new IntegerDoubleDBIDPair(val, id.internalGetIndex());
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <D extends Distance<D>> DistanceDBIDPair<D> newDistancePair(D val, DBIDRef id) {
+ if (val instanceof DoubleDistance) {
+ return (DistanceDBIDPair<D>) new DoubleDistanceIntegerDBIDPair(((DoubleDistance) val).doubleValue(), id.internalGetIndex());
+ }
+ return new DistanceIntegerDBIDPair<D>(val, id.internalGetIndex());
+ }
+
+ @Override
+ public DoubleDistanceDBIDPair newDistancePair(double val, DBIDRef id) {
+ return new DoubleDistanceIntegerDBIDPair(val, id.internalGetIndex());
}
@Override
public ByteBufferSerializer<DBID> getDBIDSerializer() {
- return IntegerDBID.dynamicSerializer;
+ return IntegerDBID.DYNAMIC_SERIALIZER;
}
@Override
public FixedSizeByteBufferSerializer<DBID> getDBIDSerializerStatic() {
- return IntegerDBID.staticSerializer;
+ return IntegerDBID.STATIC_SERIALIZER;
}
@Override
public Class<? extends DBID> getTypeRestriction() {
return IntegerDBID.class;
}
-} \ No newline at end of file
+
+ @Override
+ public DBIDRef invalid() {
+ return invalid;
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/database/ids/integer/TroveArrayDBIDs.java b/src/de/lmu/ifi/dbs/elki/database/ids/integer/TroveArrayDBIDs.java
index e2bfbcd5..313a0f3b 100644
--- a/src/de/lmu/ifi/dbs/elki/database/ids/integer/TroveArrayDBIDs.java
+++ b/src/de/lmu/ifi/dbs/elki/database/ids/integer/TroveArrayDBIDs.java
@@ -24,13 +24,12 @@ package de.lmu.ifi.dbs.elki.database.ids.integer;
*/
import gnu.trove.list.TIntList;
-
-import java.util.Iterator;
-
import de.lmu.ifi.dbs.elki.database.ids.ArrayDBIDs;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
-import de.lmu.ifi.dbs.elki.database.ids.DBIDMIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDVar;
import de.lmu.ifi.dbs.elki.logging.LoggingUtil;
/**
@@ -39,23 +38,18 @@ import de.lmu.ifi.dbs.elki.logging.LoggingUtil;
* @author Erich Schubert
*
* @apiviz.has IntegerDBID
- * @apiviz.has TroveIteratorAdapter
+ * @apiviz.has DBIDItr
*/
-public abstract class TroveArrayDBIDs implements ArrayDBIDs {
+public abstract class TroveArrayDBIDs implements ArrayDBIDs, IntegerDBIDs {
/**
- * Get the array store
+ * Get the array store.
*
* @return the store
*/
- abstract protected TIntList getStore();
+ protected abstract TIntList getStore();
@Override
- public Iterator<DBID> iterator() {
- return new TroveIteratorAdapter(getStore().iterator());
- }
-
- @Override
- public DBIDMIter iter() {
+ public IntegerDBIDArrayMIter iter() {
return new DBIDItr(getStore());
}
@@ -65,10 +59,20 @@ public abstract class TroveArrayDBIDs implements ArrayDBIDs {
}
@Override
+ public void assign(int index, DBIDVar var) {
+ if (var instanceof IntegerDBIDVar) {
+ ((IntegerDBIDVar)var).internalSetIndex(getStore().get(index));
+ } else {
+ // Much less efficient:
+ var.set(get(index));
+ }
+ }
+
+ @Override
public int size() {
return getStore().size();
}
-
+
@Override
public boolean isEmpty() {
return getStore().isEmpty();
@@ -76,29 +80,43 @@ public abstract class TroveArrayDBIDs implements ArrayDBIDs {
@Override
public boolean contains(DBIDRef o) {
- return getStore().contains(o.getIntegerID());
+ return getStore().contains(DBIDUtil.asInteger(o));
}
@Override
public int binarySearch(DBIDRef key) {
- return getStore().binarySearch(key.getIntegerID());
+ return getStore().binarySearch(DBIDUtil.asInteger(key));
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder buf = new StringBuilder();
+ buf.append('[');
+ for(DBIDIter iter = iter(); iter.valid(); iter.advance()) {
+ if(buf.length() > 1) {
+ buf.append(", ");
+ }
+ buf.append(((IntegerDBIDRef) iter).internalGetIndex());
+ }
+ buf.append(']');
+ return buf.toString();
}
/**
- * Iterate over a Trove IntList, ELKI/C-style
+ * Iterate over a Trove IntList, ELKI/C-style.
*
* @author Erich Schubert
*
* @apiviz.exclude
*/
- protected static class DBIDItr implements DBIDMIter {
+ protected static class DBIDItr implements IntegerDBIDArrayMIter {
/**
- * Current position
+ * Current position.
*/
int pos = 0;
/**
- * The actual store we use
+ * The actual store we use.
*/
TIntList store;
@@ -114,7 +132,7 @@ public abstract class TroveArrayDBIDs implements ArrayDBIDs {
@Override
public boolean valid() {
- return pos < store.size();
+ return pos < store.size() && pos >= 0;
}
@Override
@@ -123,13 +141,28 @@ public abstract class TroveArrayDBIDs implements ArrayDBIDs {
}
@Override
- public int getIntegerID() {
- return store.get(pos);
+ public void advance(int count) {
+ pos += count;
}
@Override
- public DBID getDBID() {
- return new IntegerDBID(store.get(pos));
+ public void retract() {
+ pos--;
+ }
+
+ @Override
+ public void seek(int off) {
+ pos = off;
+ }
+
+ @Override
+ public int getOffset() {
+ return pos;
+ }
+
+ @Override
+ public int internalGetIndex() {
+ return store.get(pos);
}
@Override
@@ -139,23 +172,22 @@ public abstract class TroveArrayDBIDs implements ArrayDBIDs {
}
@Override
- public boolean sameDBID(DBIDRef other) {
- return store.get(pos) == other.getIntegerID();
+ public int hashCode() {
+ // Since we add a warning to 'equals', we also override hashCode.
+ return super.hashCode();
}
@Override
public boolean equals(Object other) {
- if (other instanceof DBID) {
- LoggingUtil.warning("Programming error detected: DBIDItr.equals(DBID). Use sameDBID()!", new Throwable());
+ if(other instanceof DBID) {
+ LoggingUtil.warning("Programming error detected: DBIDItr.equals(DBID). Use DBIDUtil.equal(iter, id)!", new Throwable());
}
return super.equals(other);
}
@Override
- public int compareDBID(DBIDRef o) {
- final int thisVal = store.get(pos);
- final int anotherVal = o.getIntegerID();
- return (thisVal < anotherVal ? -1 : (thisVal == anotherVal ? 0 : 1));
+ public String toString() {
+ return Integer.toString(internalGetIndex());
}
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/database/ids/integer/TroveArrayModifiableDBIDs.java b/src/de/lmu/ifi/dbs/elki/database/ids/integer/TroveArrayModifiableDBIDs.java
index 379ea8a6..7e84eb56 100644
--- a/src/de/lmu/ifi/dbs/elki/database/ids/integer/TroveArrayModifiableDBIDs.java
+++ b/src/de/lmu/ifi/dbs/elki/database/ids/integer/TroveArrayModifiableDBIDs.java
@@ -25,13 +25,13 @@ package de.lmu.ifi.dbs.elki.database.ids.integer;
import gnu.trove.list.array.TIntArrayList;
-import java.util.Arrays;
import java.util.Comparator;
import de.lmu.ifi.dbs.elki.database.ids.ArrayModifiableDBIDs;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
/**
@@ -81,8 +81,8 @@ class TroveArrayModifiableDBIDs extends TroveArrayDBIDs implements ArrayModifiab
@Override
public boolean addDBIDs(DBIDs ids) {
boolean success = false;
- for(DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
- success |= store.add(iter.getIntegerID());
+ for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
+ success |= store.add(DBIDUtil.asInteger(iter));
}
return success;
}
@@ -90,25 +90,25 @@ class TroveArrayModifiableDBIDs extends TroveArrayDBIDs implements ArrayModifiab
@Override
public boolean removeDBIDs(DBIDs ids) {
boolean success = false;
- for(DBID id : ids) {
- success |= store.remove(id.getIntegerID());
+ for (DBIDIter id = ids.iter(); id.valid(); id.advance()) {
+ success |= store.remove(DBIDUtil.asInteger(id));
}
return success;
}
@Override
public boolean add(DBIDRef e) {
- return store.add(e.getIntegerID());
+ return store.add(DBIDUtil.asInteger(e));
}
@Override
public boolean remove(DBIDRef o) {
- return store.remove(o.getIntegerID());
+ return store.remove(DBIDUtil.asInteger(o));
}
@Override
- public DBID set(int index, DBID element) {
- int prev = store.set(index, element.getIntegerID());
+ public DBID set(int index, DBIDRef element) {
+ int prev = store.set(index, DBIDUtil.asInteger(element));
return new IntegerDBID(prev);
}
@@ -128,23 +128,27 @@ class TroveArrayModifiableDBIDs extends TroveArrayDBIDs implements ArrayModifiab
}
@Override
- public void sort(Comparator<? super DBID> comparator) {
- // FIXME: optimize, avoid the extra copy.
- // Clone data
- DBID[] data = new DBID[store.size()];
- for(int i = 0; i < store.size(); i++) {
- data[i] = new IntegerDBID(store.get(i));
- }
- // Sort
- Arrays.sort(data, comparator);
- // Copy back
- for(int i = 0; i < store.size(); i++) {
- store.set(i, data[i].getIntegerID());
- }
+ public void sort(Comparator<? super DBIDRef> comparator) {
+ // TODO: we no longer produce a lot of DBIDs anymore, but it would be even
+ // cooler if we could access store._data directly...
+ int[] data = store.toArray();
+ IntegerDBIDArrayQuickSort.sort(data, comparator);
+ store.clear();
+ store.add(data);
+ }
+
+ @Override
+ public void sort(int start, int end, Comparator<? super DBIDRef> comparator) {
+ // TODO: we no longer produce a lot of DBIDs anymore, but it would be even
+ // cooler if we could access store._data directly...
+ int[] data = store.toArray();
+ IntegerDBIDArrayQuickSort.sort(data, start, end, comparator);
+ store.clear();
+ store.add(data);
}
@Override
public void swap(int a, int b) {
store.set(a, store.set(b, store.get(a)));
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/database/ids/integer/TroveArrayStaticDBIDs.java b/src/de/lmu/ifi/dbs/elki/database/ids/integer/TroveArrayStaticDBIDs.java
index 53ab34d4..60dd50eb 100644
--- a/src/de/lmu/ifi/dbs/elki/database/ids/integer/TroveArrayStaticDBIDs.java
+++ b/src/de/lmu/ifi/dbs/elki/database/ids/integer/TroveArrayStaticDBIDs.java
@@ -23,16 +23,15 @@ package de.lmu.ifi.dbs.elki.database.ids.integer;
*/
import gnu.trove.list.TIntList;
-import de.lmu.ifi.dbs.elki.database.ids.ArrayStaticDBIDs;
/**
* Class accessing a trove int array.
*
* @author Erich Schubert
*/
-class TroveArrayStaticDBIDs extends TroveArrayDBIDs implements ArrayStaticDBIDs {
+class TroveArrayStaticDBIDs extends TroveArrayDBIDs implements IntegerArrayStaticDBIDs {
/**
- * Actual trove store
+ * Actual trove store.
*/
private final TIntList store;
diff --git a/src/de/lmu/ifi/dbs/elki/database/ids/integer/TroveHashSetModifiableDBIDs.java b/src/de/lmu/ifi/dbs/elki/database/ids/integer/TroveHashSetModifiableDBIDs.java
index 12656f7b..9e65b3ae 100644
--- a/src/de/lmu/ifi/dbs/elki/database/ids/integer/TroveHashSetModifiableDBIDs.java
+++ b/src/de/lmu/ifi/dbs/elki/database/ids/integer/TroveHashSetModifiableDBIDs.java
@@ -1,16 +1,15 @@
package de.lmu.ifi.dbs.elki.database.ids.integer;
-import java.util.Iterator;
-
import gnu.trove.impl.hash.THashPrimitiveIterator;
import gnu.trove.impl.hash.TIntHash;
import gnu.trove.set.hash.TIntHashSet;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDMIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.ids.HashSetModifiableDBIDs;
+import de.lmu.ifi.dbs.elki.utilities.iterator.Iter;
/*
This file is part of ELKI:
@@ -41,9 +40,9 @@ import de.lmu.ifi.dbs.elki.database.ids.HashSetModifiableDBIDs;
* @author Erich Schubert
*
* @apiviz.has IntegerDBID
- * @apiviz.has TroveIteratorAdapter
+ * @apiviz.has DBIDItr
*/
-class TroveHashSetModifiableDBIDs implements HashSetModifiableDBIDs {
+class TroveHashSetModifiableDBIDs implements HashSetModifiableDBIDs, IntegerDBIDs {
/**
* The actual store.
*/
@@ -78,15 +77,15 @@ class TroveHashSetModifiableDBIDs implements HashSetModifiableDBIDs {
}
@Override
- public DBIDMIter iter() {
+ public IntegerDBIDMIter iter() {
return new DBIDItr(store);
}
@Override
public boolean addDBIDs(DBIDs ids) {
boolean success = false;
- for(DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
- success |= store.add(iter.getIntegerID());
+ for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
+ success |= store.add(DBIDUtil.asInteger(iter));
}
return success;
}
@@ -94,28 +93,27 @@ class TroveHashSetModifiableDBIDs implements HashSetModifiableDBIDs {
@Override
public boolean removeDBIDs(DBIDs ids) {
boolean success = false;
- for(DBID id : ids) {
- success |= store.remove(id.getIntegerID());
+ for (DBIDIter id = ids.iter(); id.valid(); id.advance()) {
+ success |= store.remove(DBIDUtil.asInteger(id));
}
return success;
}
@Override
public boolean add(DBIDRef e) {
- return store.add(e.getIntegerID());
+ return store.add(DBIDUtil.asInteger(e));
}
@Override
public boolean remove(DBIDRef o) {
- return store.remove(o.getIntegerID());
+ return store.remove(DBIDUtil.asInteger(o));
}
@Override
public boolean retainAll(DBIDs set) {
boolean modified = false;
- Iterator<DBID> it = iterator();
- while(it.hasNext()) {
- if(!set.contains(it.next())) {
+ for (DBIDMIter it = iter(); it.valid(); it.advance()) {
+ if (!set.contains(it)) {
it.remove();
modified = true;
}
@@ -124,11 +122,6 @@ class TroveHashSetModifiableDBIDs implements HashSetModifiableDBIDs {
}
@Override
- public Iterator<DBID> iterator() {
- return new TroveIteratorAdapter(store.iterator());
- }
-
- @Override
public int size() {
return store.size();
}
@@ -145,7 +138,21 @@ class TroveHashSetModifiableDBIDs implements HashSetModifiableDBIDs {
@Override
public boolean contains(DBIDRef o) {
- return store.contains(o.getIntegerID());
+ return store.contains(DBIDUtil.asInteger(o));
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder buf = new StringBuilder();
+ buf.append('[');
+ for (DBIDIter iter = iter(); iter.valid(); iter.advance()) {
+ if (buf.length() > 1) {
+ buf.append(", ");
+ }
+ buf.append(iter.toString());
+ }
+ buf.append(']');
+ return buf.toString();
}
/**
@@ -155,11 +162,11 @@ class TroveHashSetModifiableDBIDs implements HashSetModifiableDBIDs {
*
* @apiviz.exclude
*/
- protected static class DBIDItr extends THashPrimitiveIterator implements DBIDMIter {
+ protected static class DBIDItr implements IntegerDBIDMIter {
/**
- * The has we access
+ * The actual iterator. We don't have multi inheritance.
*/
- private TIntHash hash;
+ TIntHashItr it;
/**
* Constructor.
@@ -167,41 +174,77 @@ class TroveHashSetModifiableDBIDs implements HashSetModifiableDBIDs {
* @param hash Trove hash
*/
public DBIDItr(TIntHash hash) {
- super(hash);
- this.hash = hash;
- this._index = nextIndex(); // Find first element
+ super();
+ this.it = new TIntHashItr(hash);
}
@Override
public boolean valid() {
- return _index >= 0;
+ return it.valid();
}
@Override
public void advance() {
- this._index = nextIndex();
+ it.advance();
}
@Override
- public int getIntegerID() {
- return hash._set[_index];
+ public int internalGetIndex() {
+ return it.getInt();
}
@Override
- public DBID getDBID() {
- return new IntegerDBID(hash._set[_index]);
+ public String toString() {
+ return Integer.toString(internalGetIndex());
}
@Override
- public boolean sameDBID(DBIDRef other) {
- return getIntegerID() == other.getIntegerID();
+ public void remove() {
+ it.remove();
}
- @Override
- public int compareDBID(DBIDRef o) {
- final int thisVal = hash._set[_index];
- final int anotherVal = o.getIntegerID();
- return (thisVal < anotherVal ? -1 : (thisVal == anotherVal ? 0 : 1));
+ /**
+ * Custom iterator over TIntHash.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ private static class TIntHashItr extends THashPrimitiveIterator implements Iter {
+ /**
+ * The hash we access.
+ */
+ private TIntHash hash;
+
+ /**
+ * Constructor.
+ *
+ * @param hash Hash to iterate over.
+ */
+ public TIntHashItr(TIntHash hash) {
+ super(hash);
+ this.hash = hash;
+ this._index = nextIndex(); // Find first element
+ }
+
+ /**
+ * Get the current value.
+ *
+ * @return Current value
+ */
+ public int getInt() {
+ return hash._set[_index];
+ }
+
+ @Override
+ public void advance() {
+ this._index = nextIndex();
+ }
+
+ @Override
+ public boolean valid() {
+ return _index >= 0;
+ }
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/database/ids/integer/UnmodifiableIntegerArrayDBIDs.java b/src/de/lmu/ifi/dbs/elki/database/ids/integer/UnmodifiableIntegerArrayDBIDs.java
new file mode 100644
index 00000000..14e26748
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/database/ids/integer/UnmodifiableIntegerArrayDBIDs.java
@@ -0,0 +1,160 @@
+package de.lmu.ifi.dbs.elki.database.ids.integer;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDMIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDVar;
+
+/**
+ * Unmodifiable wrapper for DBIDs.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.uses TroveArrayDBIDs
+ * @apiviz.has UnmodifiableDBIDIter
+ */
+public class UnmodifiableIntegerArrayDBIDs implements IntegerArrayStaticDBIDs {
+ /**
+ * The DBIDs we wrap.
+ */
+ private final TroveArrayDBIDs inner;
+
+ /**
+ * Constructor.
+ *
+ * @param inner Inner DBID collection.
+ */
+ public UnmodifiableIntegerArrayDBIDs(TroveArrayDBIDs inner) {
+ super();
+ this.inner = inner;
+ }
+
+ @Override
+ public boolean contains(DBIDRef o) {
+ return inner.contains(o);
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return inner.isEmpty();
+ }
+
+ @Override
+ public IntegerDBIDArrayIter iter() {
+ IntegerDBIDArrayIter it = inner.iter();
+ if (it instanceof DBIDMIter) {
+ return new UnmodifiableDBIDIter(it);
+ }
+ return it;
+ }
+
+ @Override
+ public int size() {
+ return inner.size();
+ }
+
+ @Override
+ public String toString() {
+ return inner.toString();
+ }
+
+ @Override
+ public DBID get(int i) {
+ return inner.get(i);
+ }
+
+ @Override
+ public void assign(int index, DBIDVar var) {
+ inner.assign(index, var);
+ }
+
+ @Override
+ public int binarySearch(DBIDRef key) {
+ return inner.binarySearch(key);
+ }
+
+ /**
+ * Make an existing DBIDMIter unmodifiable.
+ *
+ * @author Erich Schubert
+ */
+ class UnmodifiableDBIDIter implements IntegerDBIDArrayIter {
+ /**
+ * Wrapped iterator.
+ */
+ private IntegerDBIDArrayIter it;
+
+ /**
+ * Constructor.
+ *
+ * @param it inner iterator
+ */
+ public UnmodifiableDBIDIter(IntegerDBIDArrayIter it) {
+ super();
+ this.it = it;
+ }
+
+ @Override
+ public boolean valid() {
+ return it.valid();
+ }
+
+ @Override
+ public void advance() {
+ it.advance();
+ }
+
+ @Override
+ public void advance(int count) {
+ it.advance(count);
+ }
+
+ @Override
+ public void retract() {
+ it.retract();
+ }
+
+ @Override
+ public void seek(int off) {
+ it.seek(off);
+ }
+
+ @Override
+ public int getOffset() {
+ return it.getOffset();
+ }
+
+ @Override
+ public int internalGetIndex() {
+ return it.internalGetIndex();
+ }
+
+ @Override
+ public String toString() {
+ return it.toString();
+ }
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/database/ids/integer/UnmodifiableIntegerDBIDs.java b/src/de/lmu/ifi/dbs/elki/database/ids/integer/UnmodifiableIntegerDBIDs.java
new file mode 100644
index 00000000..1d27f530
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/database/ids/integer/UnmodifiableIntegerDBIDs.java
@@ -0,0 +1,119 @@
+package de.lmu.ifi.dbs.elki.database.ids.integer;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import de.lmu.ifi.dbs.elki.database.ids.DBIDMIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.database.ids.StaticDBIDs;
+
+/**
+ * Unmodifiable wrapper for DBIDs.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.uses IntegerDBIDs
+ * @apiviz.has UnmodifiableDBIDIter
+ */
+public class UnmodifiableIntegerDBIDs implements StaticDBIDs, IntegerDBIDs {
+ /**
+ * The DBIDs we wrap.
+ */
+ private final IntegerDBIDs inner;
+
+ /**
+ * Constructor.
+ *
+ * @param inner Inner DBID collection.
+ */
+ public UnmodifiableIntegerDBIDs(IntegerDBIDs inner) {
+ super();
+ this.inner = inner;
+ }
+
+ @Override
+ public boolean contains(DBIDRef o) {
+ return inner.contains(o);
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return inner.isEmpty();
+ }
+
+ @Override
+ public IntegerDBIDIter iter() {
+ IntegerDBIDIter it = inner.iter();
+ if (it instanceof DBIDMIter) {
+ return new UnmodifiableDBIDIter(it);
+ }
+ return it;
+ }
+
+ @Override
+ public int size() {
+ return inner.size();
+ }
+
+ @Override
+ public String toString() {
+ return inner.toString();
+ }
+
+ /**
+ * Make an existing DBIDMIter unmodifiable.
+ *
+ * @author Erich Schubert
+ */
+ class UnmodifiableDBIDIter implements IntegerDBIDIter {
+ /**
+ * Wrapped iterator.
+ */
+ private IntegerDBIDIter it;
+
+ /**
+ * Constructor.
+ *
+ * @param it inner iterator
+ */
+ public UnmodifiableDBIDIter(IntegerDBIDIter it) {
+ super();
+ this.it = it;
+ }
+
+ @Override
+ public boolean valid() {
+ return it.valid();
+ }
+
+ @Override
+ public void advance() {
+ it.advance();
+ }
+
+ @Override
+ public int internalGetIndex() {
+ return it.internalGetIndex();
+ }
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/database/ids/package-info.java b/src/de/lmu/ifi/dbs/elki/database/ids/package-info.java
index 2c7de1c5..71af955c 100644
--- a/src/de/lmu/ifi/dbs/elki/database/ids/package-info.java
+++ b/src/de/lmu/ifi/dbs/elki/database/ids/package-info.java
@@ -50,7 +50,6 @@
* }</pre>
*
* <h2>Utility functions:</h2>
- * The static {@link de.lmu.ifi.dbs.elki.database.ids.DBIDUtil DBIDUtil} class provides various utility functions, including:
* <ul>
* <li>{@link de.lmu.ifi.dbs.elki.database.ids.DBIDUtil#ensureArray DBIDUtil.ensureArray} to ensure {@link de.lmu.ifi.dbs.elki.database.ids.ArrayDBIDs ArrayDBIDs}</li>
* <li>{@link de.lmu.ifi.dbs.elki.database.ids.DBIDUtil#ensureModifiable DBIDUtil.ensureModifiable} to ensure {@link de.lmu.ifi.dbs.elki.database.ids.ModifiableDBIDs ModifiableDBIDS}</li>
@@ -64,10 +63,18 @@
* <p>{@link de.lmu.ifi.dbs.elki.database.ids.generic.MaskedDBIDs MaskedDBIDs}
* allows masking an ArrayDBIDs with a BitSet.</p>
*
+ * @apiviz.exclude de.lmu.ifi.dbs.elki.database.ids.DBIDFactory
* @apiviz.exclude de.lmu.ifi.dbs.elki.database.ids.integer.*
- * @apiviz.exclude de.lmu.ifi.dbs.elki.database.ids.generic.Generic*
- * @apiviz.exclude de.lmu.ifi.dbs.elki.data.cluster.Cluster
- * @apiviz.exclude de.lmu.ifi.dbs.elki.utilities.datastructures.heap.*
+ * @apiviz.exclude de.lmu.ifi.dbs.elki.database.ids.generic.*
+ * @apiviz.exclude de.lmu.ifi.dbs.elki.database.ids.EmptyDBIDs.EmptyDBIDIterator
+ * @apiviz.exclude de.lmu.ifi.dbs.elki.database.*Database
+ * @apiviz.exclude de.lmu.ifi.dbs.elki.data.Cluster
+ * @apiviz.exclude de.lmu.ifi.dbs.elki.utilities.*
+ * @apiviz.exclude de.lmu.ifi.dbs.elki.datasource.filter.*
+ * @apiviz.exclude de.lmu.ifi.dbs.elki.database.query.*
+ * @apiviz.exclude de.lmu.ifi.dbs.elki.distance.*
+ * @apiviz.exclude de.lmu.ifi.dbs.elki.index.*
+ * @apiviz.exclude de.lmu.ifi.dbs.elki.result.*
* @apiviz.exclude de.lmu.ifi.dbs.elki.persistent.*
* @apiviz.exclude de.lmu.ifi.dbs.elki.algorithm.*
* @apiviz.exclude java.*
@@ -94,4 +101,4 @@
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package de.lmu.ifi.dbs.elki.database.ids; \ No newline at end of file
+package de.lmu.ifi.dbs.elki.database.ids;
diff --git a/src/de/lmu/ifi/dbs/elki/database/package-info.java b/src/de/lmu/ifi/dbs/elki/database/package-info.java
index c39600f7..73cf0596 100644
--- a/src/de/lmu/ifi/dbs/elki/database/package-info.java
+++ b/src/de/lmu/ifi/dbs/elki/database/package-info.java
@@ -1,5 +1,9 @@
/**
* <p>ELKI database layer - loading, storing, indexing and accessing data</p>
+ *
+ * @apiviz.exclude elki.utilities
+ * @apiviz.exclude elki.result
+ * @apiviz.exclude elki.workflow
*/
/*
This file is part of ELKI:
@@ -23,4 +27,4 @@ GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package de.lmu.ifi.dbs.elki.database; \ No newline at end of file
+package de.lmu.ifi.dbs.elki.database;
diff --git a/src/de/lmu/ifi/dbs/elki/database/query/DoubleDistanceResultPair.java b/src/de/lmu/ifi/dbs/elki/database/query/DoubleDistanceResultPair.java
deleted file mode 100644
index 23abc327..00000000
--- a/src/de/lmu/ifi/dbs/elki/database/query/DoubleDistanceResultPair.java
+++ /dev/null
@@ -1,165 +0,0 @@
-package de.lmu.ifi.dbs.elki.database.query;
-
-/*
- This file is part of ELKI:
- Environment for Developing KDD-Applications Supported by Index-Structures
-
- Copyright (C) 2012
- Ludwig-Maximilians-Universität München
- Lehr- und Forschungseinheit für Datenbanksysteme
- ELKI Development Team
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
-import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
-import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
-
-/**
- * Optimized DistanceResultPair that avoids/postpones an extra layer of boxing
- * for double values.
- *
- * @author Erich Schubert
- */
-public class DoubleDistanceResultPair implements DistanceResultPair<DoubleDistance> {
- /**
- * Distance value
- */
- double distance;
-
- /**
- * Object ID
- */
- DBID id;
-
- /**
- * Constructor.
- *
- * @param distance Distance value
- * @param id Object ID
- */
- public DoubleDistanceResultPair(double distance, DBID id) {
- super();
- this.distance = distance;
- this.id = id;
- }
-
- @Override
- public DoubleDistance getDistance() {
- return new DoubleDistance(distance);
- }
-
- @Override
- public void setDistance(DoubleDistance distance) {
- this.distance = distance.doubleValue();
- }
-
- @Override
- public DBID getDBID() {
- return id;
- }
-
- @Override
- public int getIntegerID() {
- return id.getIntegerID();
- }
-
- @Override
- public boolean sameDBID(DBIDRef other) {
- return id.sameDBID(other);
- }
-
- @Override
- public int compareDBID(DBIDRef other) {
- return id.compareDBID(other);
- }
-
- @Override
- public void setID(DBID id) {
- this.id = id;
- }
-
- /**
- * @deprecated Use {@link #getDoubleDistance} or {@link #getDistance} for clearness.
- */
- @Deprecated
- @Override
- public DoubleDistance getFirst() {
- return getDistance();
- }
-
- /**
- * @deprecated Use {@link #getDBID} for clearness.
- */
- @Deprecated
- @Override
- public DBID getSecond() {
- return id;
- }
-
- @Override
- public int compareByDistance(DistanceResultPair<DoubleDistance> o) {
- if(o instanceof DoubleDistanceResultPair) {
- DoubleDistanceResultPair od = (DoubleDistanceResultPair) o;
- final int delta = Double.compare(distance, od.distance);
- if(delta != 0) {
- return delta;
- }
- }
- else {
- final int delta = Double.compare(distance, o.getDistance().doubleValue());
- if(delta != 0) {
- return delta;
- }
- }
- return 0;
- }
-
- @Override
- public int compareTo(DistanceResultPair<DoubleDistance> o) {
- final int delta = this.compareByDistance(o);
- if(delta != 0) {
- return delta;
- }
- return id.compareTo(o.getDBID());
- }
-
- @Override
- public boolean equals(Object obj) {
- if(!(obj instanceof DistanceResultPair)) {
- return false;
- }
- if(obj instanceof DoubleDistanceResultPair) {
- DoubleDistanceResultPair ddrp = (DoubleDistanceResultPair) obj;
- return distance == ddrp.distance && id.sameDBID(ddrp.id);
- }
- DistanceResultPair<?> other = (DistanceResultPair<?>) obj;
- return other.getDistance().equals(distance) && id.sameDBID(other.getDBID());
- }
-
- /**
- * Get the distance as double value.
- *
- * @return distance value
- */
- public double getDoubleDistance() {
- return distance;
- }
-
- @Override
- public String toString() {
- return "DistanceResultPair(" + distance + ", " + id + ")";
- }
-} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/database/query/GenericDistanceResultPair.java b/src/de/lmu/ifi/dbs/elki/database/query/GenericDistanceResultPair.java
deleted file mode 100644
index 414f145a..00000000
--- a/src/de/lmu/ifi/dbs/elki/database/query/GenericDistanceResultPair.java
+++ /dev/null
@@ -1,131 +0,0 @@
-package de.lmu.ifi.dbs.elki.database.query;
-
-/*
- This file is part of ELKI:
- Environment for Developing KDD-Applications Supported by Index-Structures
-
- Copyright (C) 2012
- Ludwig-Maximilians-Universität München
- Lehr- und Forschungseinheit für Datenbanksysteme
- ELKI Development Team
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
-import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
-import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
-import de.lmu.ifi.dbs.elki.utilities.pairs.Pair;
-
-/**
- * Trivial implementation using a generic pair.
- *
- * @author Erich Schubert
- *
- * @param <D> Distance type
- */
-public class GenericDistanceResultPair<D extends Distance<D>> extends Pair<D, DBID> implements DistanceResultPair<D> {
- /**
- * Canonical constructor
- *
- * @param first Distance
- * @param second Object ID
- */
- public GenericDistanceResultPair(D first, DBID second) {
- super(first, second);
- }
-
- /**
- * Getter for first
- *
- * @return first element in pair
- */
- @Override
- public final D getDistance() {
- return first;
- }
-
- /**
- * Setter for first
- *
- * @param first new value for first element
- */
- @Override
- public final void setDistance(D first) {
- this.first = first;
- }
-
- /**
- * Getter for second element in pair
- *
- * @return second element in pair
- */
- @Override
- public final DBID getDBID() {
- return second;
- }
-
- @Override
- public int getIntegerID() {
- return second.getIntegerID();
- }
-
- @Override
- public boolean sameDBID(DBIDRef other) {
- return second.sameDBID(other);
- }
-
- @Override
- public int compareDBID(DBIDRef other) {
- return second.compareDBID(other);
- }
-
- /**
- * Setter for second
- *
- * @param second new value for second element
- */
- @Override
- public final void setID(DBID second) {
- this.second = second;
- }
-
- @Override
- public int compareByDistance(DistanceResultPair<D> o) {
- return first.compareTo(o.getDistance());
- }
-
- @Override
- public int compareTo(DistanceResultPair<D> o) {
- final int ret = compareByDistance(o);
- if(ret != 0) {
- return ret;
- }
- return second.compareTo(o.getDBID());
- }
-
- @Override
- public boolean equals(Object obj) {
- if(!(obj instanceof DistanceResultPair)) {
- return false;
- }
- DistanceResultPair<?> other = (DistanceResultPair<?>) obj;
- return first.equals(other.getDistance()) && second.sameDBID(other.getDBID());
- }
-
- @Override
- public String toString() {
- return "DistanceResultPair(" + getFirst() + ", " + getSecond() + ")";
- }
-} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/database/query/distance/package-info.java b/src/de/lmu/ifi/dbs/elki/database/query/distance/package-info.java
index 2257eb98..fdd116b7 100644
--- a/src/de/lmu/ifi/dbs/elki/database/query/distance/package-info.java
+++ b/src/de/lmu/ifi/dbs/elki/database/query/distance/package-info.java
@@ -1,6 +1,7 @@
/**
* <p>Prepared queries for distances.</p>
*
+ * @apiviz.exclude .*Instance
*/
/*
This file is part of ELKI:
diff --git a/src/de/lmu/ifi/dbs/elki/database/query/knn/AbstractDistanceKNNQuery.java b/src/de/lmu/ifi/dbs/elki/database/query/knn/AbstractDistanceKNNQuery.java
index e5df1452..0ac2e8ee 100644
--- a/src/de/lmu/ifi/dbs/elki/database/query/knn/AbstractDistanceKNNQuery.java
+++ b/src/de/lmu/ifi/dbs/elki/database/query/knn/AbstractDistanceKNNQuery.java
@@ -26,6 +26,7 @@ package de.lmu.ifi.dbs.elki.database.query.knn;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.query.AbstractDataBasedQuery;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
/**
diff --git a/src/de/lmu/ifi/dbs/elki/database/query/knn/KNNQuery.java b/src/de/lmu/ifi/dbs/elki/database/query/knn/KNNQuery.java
index 13bf58c2..5b517509 100644
--- a/src/de/lmu/ifi/dbs/elki/database/query/knn/KNNQuery.java
+++ b/src/de/lmu/ifi/dbs/elki/database/query/knn/KNNQuery.java
@@ -24,14 +24,12 @@ package de.lmu.ifi.dbs.elki.database.query.knn;
*/
import java.util.List;
-import java.util.Map;
import de.lmu.ifi.dbs.elki.database.ids.ArrayDBIDs;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.query.DatabaseQuery;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
-import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.KNNHeap;
/**
* The interface of an actual instance.
@@ -61,17 +59,7 @@ public interface KNNQuery<O, D extends Distance<D>> extends DatabaseQuery {
* @param k Number of neighbors requested
* @return neighbors
*/
- public List<KNNResult<D>> getKNNForBulkDBIDs(ArrayDBIDs ids, int k);
-
- /**
- * Bulk query method configured by a map.
- *
- * Warning: this API is not optimal, and might be removed soon (in fact, it is
- * used in a single place)
- *
- * @param heaps Map of heaps to fill.
- */
- public void getKNNForBulkHeaps(Map<DBID, KNNHeap<D>> heaps);
+ public List<? extends KNNResult<D>> getKNNForBulkDBIDs(ArrayDBIDs ids, int k);
/**
* Get the k nearest neighbors for a particular id.
diff --git a/src/de/lmu/ifi/dbs/elki/database/query/knn/KNNUtil.java b/src/de/lmu/ifi/dbs/elki/database/query/knn/KNNUtil.java
deleted file mode 100644
index 01deeaa0..00000000
--- a/src/de/lmu/ifi/dbs/elki/database/query/knn/KNNUtil.java
+++ /dev/null
@@ -1,429 +0,0 @@
-package de.lmu.ifi.dbs.elki.database.query.knn;
-
-/*
- This file is part of ELKI:
- Environment for Developing KDD-Applications Supported by Index-Structures
-
- Copyright (C) 2012
- Ludwig-Maximilians-Universität München
- Lehr- und Forschungseinheit für Datenbanksysteme
- ELKI Development Team
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-import java.util.AbstractList;
-import java.util.Iterator;
-import java.util.List;
-
-import de.lmu.ifi.dbs.elki.database.ids.ArrayDBIDs;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
-import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
-import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
-import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
-
-/**
- * Helper classes for kNN results.
- *
- * @author Erich Schubert
- *
- * @apiviz.uses KNNResult
- */
-public final class KNNUtil {
- /**
- * Sublist of an existing result to contain only the first k elements.
- *
- * @author Erich Schubert
- *
- * @param <D> Distance
- */
- protected static class KNNSubList<D extends Distance<D>> extends AbstractList<DistanceResultPair<D>> implements KNNResult<D> {
- /**
- * Parameter k
- */
- private final int k;
-
- /**
- * Actual size, including ties
- */
- private final int size;
-
- /**
- * Wrapped inner result.
- */
- private final KNNResult<D> inner;
-
- /**
- * Constructor.
- *
- * @param inner Inner instance
- * @param k k value
- */
- public KNNSubList(KNNResult<D> inner, int k) {
- this.inner = inner;
- this.k = k;
- // Compute list size
- // TODO: optimize for double distances.
- {
- DistanceResultPair<D> dist = inner.get(k);
- int i = k;
- while(i + 1 < inner.size()) {
- if(dist.compareByDistance(inner.get(i + 1)) < 0) {
- break;
- }
- i++;
- }
- size = i;
- }
- }
-
- @Override
- public int getK() {
- return k;
- }
-
- @Override
- public DistanceResultPair<D> get(int index) {
- assert (index < size) : "Access beyond design size of list.";
- return inner.get(index);
- }
-
- @Override
- public D getKNNDistance() {
- return inner.get(k).getDistance();
- }
-
- @Override
- public ArrayDBIDs asDBIDs() {
- return KNNUtil.asDBIDs(this);
- }
-
- @Override
- public List<D> asDistanceList() {
- return KNNUtil.asDistanceList(this);
- }
-
- @Override
- public Iterator<DistanceResultPair<D>> iterator() {
- return new Itr();
- }
-
- @Override
- public int size() {
- return size;
- }
-
- /**
- * Iterator for the sublist.
- *
- * @author Erich Schubert
- *
- * @apiviz.exclude
- */
- private class Itr implements Iterator<DistanceResultPair<D>> {
- /**
- * Current position
- */
- private int pos = -1;
-
- @Override
- public boolean hasNext() {
- return pos + 1 < size;
- }
-
- @Override
- public DistanceResultPair<D> next() {
- pos++;
- return inner.get(pos);
- }
-
- @Override
- public void remove() {
- throw new UnsupportedOperationException("kNN results are unmodifiable.");
- }
- }
- }
-
- /**
- * Proxy iterator for accessing DBIDs.
- *
- * @author Erich Schubert
- */
- protected static class DBIDIterator implements Iterator<DBID> {
- /**
- * The real iterator.
- */
- Iterator<? extends DistanceResultPair<?>> itr;
-
- /**
- * Constructor.
- */
- protected DBIDIterator(Iterator<? extends DistanceResultPair<?>> itr) {
- super();
- this.itr = itr;
- }
-
- @Override
- public boolean hasNext() {
- return itr.hasNext();
- }
-
- @Override
- public DBID next() {
- return itr.next().getDBID();
- }
-
- @Override
- public void remove() {
- itr.remove();
- }
- }
-
- /**
- * Proxy iterator for accessing DBIDs.
- *
- * @author Erich Schubert
- */
- protected static class DBIDItr implements DBIDIter {
- /**
- * Current result
- */
- DistanceResultPair<?> cur;
-
- /**
- * The real iterator.
- */
- Iterator<? extends DistanceResultPair<?>> itr;
-
- /**
- * Constructor.
- */
- protected DBIDItr(Iterator<? extends DistanceResultPair<?>> itr) {
- super();
- this.itr = itr;
- advance();
- }
-
- @Override
- public boolean valid() {
- return cur != null;
- }
-
- @Override
- public void advance() {
- if(itr.hasNext()) {
- cur = itr.next();
- }
- else {
- cur = null;
- }
- }
-
- @Override
- public int getIntegerID() {
- return cur.getDBID().getIntegerID();
- }
-
- @Override
- public DBID getDBID() {
- return cur.getDBID();
- }
-
- @Override
- public boolean sameDBID(DBIDRef other) {
- return getIntegerID() == other.getIntegerID();
- }
-
- @Override
- public int compareDBID(DBIDRef o) {
- final int thisVal = getIntegerID();
- final int anotherVal = o.getIntegerID();
- return (thisVal < anotherVal ? -1 : (thisVal == anotherVal ? 0 : 1));
- }
- }
-
- /**
- * A view on the DBIDs of the result
- *
- * @author Erich Schubert
- */
- protected static class DBIDView implements ArrayDBIDs {
- /**
- * The true list.
- */
- final KNNResult<?> parent;
-
- /**
- * Constructor.
- *
- * @param parent Owner
- */
- public DBIDView(KNNResult<?> parent) {
- super();
- this.parent = parent;
- }
-
- @Override
- public DBID get(int i) {
- return parent.get(i).getDBID();
- }
-
- @Override
- public Iterator<DBID> iterator() {
- return new DBIDIterator(parent.iterator());
- }
-
- @Override
- public DBIDIter iter() {
- return new DBIDItr(parent.iterator());
- }
-
- @Override
- public int size() {
- return parent.size();
- }
-
- @Override
- public boolean contains(DBIDRef o) {
- for (DBIDIter iter = iter(); iter.valid(); iter.advance()) {
- if(iter.sameDBID(o)) {
- return true;
- }
- }
- return false;
- }
-
- @Override
- public boolean isEmpty() {
- return parent.size() == 0;
- }
-
- /**
- * A binary search does not make sense here, as the (read-only) result is sorted by
- * distance, not DBID. Thus unsupported.
- */
- @Override
- @Deprecated
- public int binarySearch(DBIDRef key) {
- throw new UnsupportedOperationException("Since the result is usually not sorted, a binary Search does not make sense!");
- }
- }
-
- /**
- * Proxy iterator for accessing DBIDs.
- *
- * @author Erich Schubert
- */
- protected static class DistanceItr<D extends Distance<D>> implements Iterator<D> {
- /**
- * The real iterator.
- */
- Iterator<? extends DistanceResultPair<D>> itr;
-
- /**
- * Constructor.
- */
- protected DistanceItr(Iterator<? extends DistanceResultPair<D>> itr) {
- super();
- this.itr = itr;
- }
-
- @Override
- public boolean hasNext() {
- return itr.hasNext();
- }
-
- @Override
- public D next() {
- return itr.next().getDistance();
- }
-
- @Override
- public void remove() {
- itr.remove();
- }
- }
-
- /**
- * A view on the Distances of the result
- *
- * @author Erich Schubert
- */
- protected static class DistanceView<D extends Distance<D>> extends AbstractList<D> implements List<D> {
- /**
- * The true list.
- */
- final KNNResult<D> parent;
-
- /**
- * Constructor.
- *
- * @param parent Owner
- */
- public DistanceView(KNNResult<D> parent) {
- super();
- this.parent = parent;
- }
-
- @Override
- public D get(int i) {
- return parent.get(i).getDistance();
- }
-
- @Override
- public Iterator<D> iterator() {
- return new DistanceItr<D>(parent.iterator());
- }
-
- @Override
- public int size() {
- return parent.size();
- }
- }
-
- /**
- * View as ArrayDBIDs
- *
- * @param list Result to proxy
- * @return Static DBIDs
- */
- public static ArrayDBIDs asDBIDs(KNNResult<?> list) {
- return new DBIDView(list);
- }
-
- /**
- * View as list of distances
- *
- * @param list Result to proxy
- * @return List of distances view
- */
- public static <D extends Distance<D>> List<D> asDistanceList(KNNResult<D> list) {
- return new DistanceView<D>(list);
- }
-
- /**
- * Get a subset of the KNN result.
- *
- * @param list Existing list
- * @param k k
- * @return Subset
- */
- public static <D extends Distance<D>> KNNResult<D> subList(KNNResult<D> list, int k) {
- if(k >= list.size()) {
- return list;
- }
- return new KNNSubList<D>(list, k);
- }
-} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/database/query/knn/LinearScanKNNQuery.java b/src/de/lmu/ifi/dbs/elki/database/query/knn/LinearScanKNNQuery.java
index b51dad4c..29fa9931 100644
--- a/src/de/lmu/ifi/dbs/elki/database/query/knn/LinearScanKNNQuery.java
+++ b/src/de/lmu/ifi/dbs/elki/database/query/knn/LinearScanKNNQuery.java
@@ -25,20 +25,17 @@ package de.lmu.ifi.dbs.elki.database.query.knn;
import java.util.ArrayList;
import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
import de.lmu.ifi.dbs.elki.database.ids.ArrayDBIDs;
-import de.lmu.ifi.dbs.elki.database.ids.ArrayModifiableDBIDs;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
-import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.query.LinearScanQuery;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.database.query.distance.PrimitiveDistanceQuery;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNHeap;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNUtil;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
-import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.KNNHeap;
/**
* Instance of this query for a particular database.
@@ -67,11 +64,10 @@ public class LinearScanKNNQuery<O, D extends Distance<D>> extends AbstractDistan
private void linearScanBatchKNN(ArrayDBIDs ids, List<KNNHeap<D>> heaps) {
// The distance is computed on database IDs
for(DBIDIter iter = relation.getDBIDs().iter(); iter.valid(); iter.advance()) {
- DBID candidateID = iter.getDBID();
int index = 0;
- for (DBIDIter iter2 = ids.iter(); iter2.valid(); iter2.advance()) {
+ for(DBIDIter iter2 = ids.iter(); iter2.valid(); iter2.advance()) {
KNNHeap<D> heap = heaps.get(index);
- heap.add(distanceQuery.distance(iter2, candidateID), candidateID);
+ heap.add(distanceQuery.distance(iter2, iter), iter);
index++;
}
}
@@ -79,16 +75,29 @@ public class LinearScanKNNQuery<O, D extends Distance<D>> extends AbstractDistan
@Override
public KNNResult<D> getKNNForDBID(DBIDRef id, int k) {
- KNNHeap<D> heap = new KNNHeap<D>(k);
+ KNNHeap<D> heap = KNNUtil.newHeap(distanceQuery.getDistanceFactory(), k);
+ D max = distanceQuery.getDistanceFactory().infiniteDistance();
if(PrimitiveDistanceQuery.class.isInstance(distanceQuery)) {
O obj = relation.get(id);
for(DBIDIter iter = relation.getDBIDs().iter(); iter.valid(); iter.advance()) {
- heap.add(distanceQuery.distance(obj, relation.get(iter)), iter);
+ final D dist = distanceQuery.distance(obj, relation.get(iter));
+ if(max.compareTo(dist) > 0) {
+ heap.add(dist, iter);
+ if(heap.size() >= k) {
+ max = heap.getKNNDistance();
+ }
+ }
}
}
else {
for(DBIDIter iter = relation.getDBIDs().iter(); iter.valid(); iter.advance()) {
- heap.add(distanceQuery.distance(id, iter), iter);
+ final D dist = distanceQuery.distance(id, iter);
+ if(max.compareTo(dist) > 0) {
+ heap.add(dist, iter);
+ if(heap.size() >= k) {
+ max = heap.getKNNDistance();
+ }
+ }
}
}
return heap.toKNNList();
@@ -99,7 +108,7 @@ public class LinearScanKNNQuery<O, D extends Distance<D>> extends AbstractDistan
final int size = ids.size();
final List<KNNHeap<D>> heaps = new ArrayList<KNNHeap<D>>(size);
for(int i = 0; i < size; i++) {
- heaps.add(new KNNHeap<D>(k));
+ heaps.add(KNNUtil.newHeap(distanceQuery.getDistanceFactory(), k));
}
linearScanBatchKNN(ids, heaps);
// Serialize heaps
@@ -111,20 +120,8 @@ public class LinearScanKNNQuery<O, D extends Distance<D>> extends AbstractDistan
}
@Override
- public void getKNNForBulkHeaps(Map<DBID, KNNHeap<D>> heaps) {
- final int size = heaps.size();
- ArrayModifiableDBIDs ids = DBIDUtil.newArray(size);
- List<KNNHeap<D>> kheaps = new ArrayList<KNNHeap<D>>(size);
- for(Entry<DBID, KNNHeap<D>> ent : heaps.entrySet()) {
- ids.add(ent.getKey());
- kheaps.add(ent.getValue());
- }
- linearScanBatchKNN(ids, kheaps);
- }
-
- @Override
public KNNResult<D> getKNNForObject(O obj, int k) {
- KNNHeap<D> heap = new KNNHeap<D>(k);
+ KNNHeap<D> heap = KNNUtil.newHeap(distanceQuery.getDistanceFactory(), k);
for(DBIDIter iter = relation.getDBIDs().iter(); iter.valid(); iter.advance()) {
heap.add(distanceQuery.distance(obj, iter), iter);
}
diff --git a/src/de/lmu/ifi/dbs/elki/database/query/knn/LinearScanPrimitiveDistanceKNNQuery.java b/src/de/lmu/ifi/dbs/elki/database/query/knn/LinearScanPrimitiveDistanceKNNQuery.java
index 59987282..c4f09744 100644
--- a/src/de/lmu/ifi/dbs/elki/database/query/knn/LinearScanPrimitiveDistanceKNNQuery.java
+++ b/src/de/lmu/ifi/dbs/elki/database/query/knn/LinearScanPrimitiveDistanceKNNQuery.java
@@ -25,16 +25,15 @@ package de.lmu.ifi.dbs.elki.database.query.knn;
import java.util.ArrayList;
import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
import de.lmu.ifi.dbs.elki.database.ids.ArrayDBIDs;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.query.distance.PrimitiveDistanceQuery;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNHeap;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNUtil;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
-import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.KNNHeap;
/**
* Instance of this query for a particular database.
@@ -66,12 +65,10 @@ public class LinearScanPrimitiveDistanceKNNQuery<O, D extends Distance<D>> exten
final int size = objs.size();
// Linear scan style KNN.
for(DBIDIter iter = relation.getDBIDs().iter(); iter.valid(); iter.advance()) {
- DBID candidateID = iter.getDBID();
- O candidate = relation.get(candidateID);
+ O candidate = relation.get(iter);
for(int index = 0; index < size; index++) {
O object = objs.get(index);
- KNNHeap<D> heap = heaps.get(index);
- heap.add(distanceQuery.distance(object, candidate), candidateID);
+ heaps.get(index).add(distanceQuery.distance(object, candidate), iter);
}
}
}
@@ -87,7 +84,7 @@ public class LinearScanPrimitiveDistanceKNNQuery<O, D extends Distance<D>> exten
final List<KNNHeap<D>> heaps = new ArrayList<KNNHeap<D>>(size);
List<O> objs = new ArrayList<O>(size);
for(DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
- heaps.add(new KNNHeap<D>(k));
+ heaps.add(KNNUtil.newHeap(distanceQuery.getDistanceFactory(), k));
objs.add(relation.get(iter));
}
linearScanBatchKNN(objs, heaps);
@@ -98,15 +95,4 @@ public class LinearScanPrimitiveDistanceKNNQuery<O, D extends Distance<D>> exten
}
return result;
}
-
- @Override
- public void getKNNForBulkHeaps(Map<DBID, KNNHeap<D>> heaps) {
- List<O> objs = new ArrayList<O>(heaps.size());
- List<KNNHeap<D>> kheaps = new ArrayList<KNNHeap<D>>(heaps.size());
- for(Entry<DBID, KNNHeap<D>> ent : heaps.entrySet()) {
- objs.add(relation.get(ent.getKey()));
- kheaps.add(ent.getValue());
- }
- linearScanBatchKNN(objs, kheaps);
- }
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/database/query/knn/LinearScanRawDoubleDistanceKNNQuery.java b/src/de/lmu/ifi/dbs/elki/database/query/knn/LinearScanRawDoubleDistanceKNNQuery.java
index a10aaac5..5e5eae2f 100644
--- a/src/de/lmu/ifi/dbs/elki/database/query/knn/LinearScanRawDoubleDistanceKNNQuery.java
+++ b/src/de/lmu/ifi/dbs/elki/database/query/knn/LinearScanRawDoubleDistanceKNNQuery.java
@@ -23,16 +23,20 @@ package de.lmu.ifi.dbs.elki.database.query.knn;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.util.Arrays;
-import java.util.List;
-
+import de.lmu.ifi.dbs.elki.database.ids.DBIDFactory;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
-import de.lmu.ifi.dbs.elki.database.query.DoubleDistanceResultPair;
+import de.lmu.ifi.dbs.elki.database.ids.DistanceDBIDPair;
+import de.lmu.ifi.dbs.elki.database.ids.DoubleDistanceDBIDPair;
import de.lmu.ifi.dbs.elki.database.query.distance.PrimitiveDistanceQuery;
import de.lmu.ifi.dbs.elki.distance.distancefunction.PrimitiveDoubleDistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.AbstractKNNHeap;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DoubleDistanceKNNHeap;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DoubleDistanceKNNList;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.GenericKNNList;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
-import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.KNNHeap;
+import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.TiedTopBoundedHeap;
/**
* Optimized linear scan query for {@link PrimitiveDoubleDistanceFunction}s.
@@ -45,15 +49,22 @@ import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.KNNHeap;
*/
public class LinearScanRawDoubleDistanceKNNQuery<O> extends LinearScanPrimitiveDistanceKNNQuery<O, DoubleDistance> {
/**
+ * Raw distance function.
+ */
+ PrimitiveDoubleDistanceFunction<O> rawdist;
+
+ /**
* Constructor.
*
* @param distanceQuery Distance function to use
*/
+ @SuppressWarnings("unchecked")
public LinearScanRawDoubleDistanceKNNQuery(PrimitiveDistanceQuery<O, DoubleDistance> distanceQuery) {
super(distanceQuery);
- if(!(distanceQuery.getDistanceFunction() instanceof PrimitiveDoubleDistanceFunction)) {
+ if (!(distanceQuery.getDistanceFunction() instanceof PrimitiveDoubleDistanceFunction)) {
throw new UnsupportedOperationException("LinearScanRawDoubleDistance instantiated for non-RawDoubleDistance!");
}
+ rawdist = (PrimitiveDoubleDistanceFunction<O>) distanceQuery.getDistanceFunction();
}
@Override
@@ -63,47 +74,70 @@ public class LinearScanRawDoubleDistanceKNNQuery<O> extends LinearScanPrimitiveD
@Override
public KNNResult<DoubleDistance> getKNNForObject(O obj, int k) {
- @SuppressWarnings("unchecked")
- final PrimitiveDoubleDistanceFunction<O> rawdist = (PrimitiveDoubleDistanceFunction<O>) distanceQuery.getDistanceFunction();
+ return getKNNForObjectBenchmarked(obj, k);
+ }
+
+ /**
+ * This is the cleaner, supposedly faster implementation.
+ *
+ * @param obj Query object
+ * @param k Desired number of neighbors
+ * @return kNN result
+ */
+ KNNResult<DoubleDistance> getKNNForObjectClean(O obj, int k) {
// Optimization for double distances.
- final KNNHeap<DoubleDistance> heap = new KNNHeap<DoubleDistance>(k);
- double max = Double.POSITIVE_INFINITY;
- for(DBIDIter iter = relation.getDBIDs().iter(); iter.valid(); iter.advance()) {
+ final TiedTopBoundedHeap<DoubleDistanceDBIDPair> heap = new TiedTopBoundedHeap<DoubleDistanceDBIDPair>(k, DoubleDistanceKNNHeap.COMPARATOR);
+ final DBIDIter iter = relation.iterDBIDs();
+
+ // First k elements don't need checking.
+ double max = 0.;
+ for (int i = 0; i < k && iter.valid(); i++, iter.advance()) {
+ final double doubleDistance = rawdist.doubleDistance(obj, relation.get(iter));
+ heap.add(DBIDFactory.FACTORY.newDistancePair(doubleDistance, iter));
+ max = Math.max(max, doubleDistance);
+ }
+ // Remaining elements
+ for (; iter.valid(); iter.advance()) {
final double doubleDistance = rawdist.doubleDistance(obj, relation.get(iter));
- if(doubleDistance <= max) {
- heap.add(new DoubleDistanceResultPair(doubleDistance, iter.getDBID()));
- // Update cutoff
- if(heap.size() >= heap.getK()) {
- max = ((DoubleDistanceResultPair) heap.peek()).getDoubleDistance();
- }
+ if (doubleDistance <= max) {
+ heap.add(DBIDFactory.FACTORY.newDistancePair(doubleDistance, iter));
+ }
+ if (doubleDistance < max) {
+ max = heap.peek().doubleDistance();
}
}
- return heap.toKNNList();
+ return new DoubleDistanceKNNList(heap, k);
}
- @Override
- protected void linearScanBatchKNN(List<O> objs, List<KNNHeap<DoubleDistance>> heaps) {
- final int size = objs.size();
- @SuppressWarnings("unchecked")
- final PrimitiveDoubleDistanceFunction<O> rawdist = (PrimitiveDoubleDistanceFunction<O>) distanceQuery.getDistanceFunction();
- // Track the max ourselves to reduce object access for comparisons.
- final double[] max = new double[size];
- Arrays.fill(max, Double.POSITIVE_INFINITY);
-
- // The distance is computed on arbitrary vectors, we can reduce object
- // loading by working on the actual vectors.
- for(DBIDIter iter = relation.getDBIDs().iter(); iter.valid(); iter.advance()) {
- O candidate = relation.get(iter);
- for(int index = 0; index < size; index++) {
- final KNNHeap<DoubleDistance> heap = heaps.get(index);
- double doubleDistance = rawdist.doubleDistance(objs.get(index), candidate);
- if(doubleDistance <= max[index]) {
- heap.add(new DoubleDistanceResultPair(doubleDistance, iter.getDBID()));
- if(heap.size() >= heap.getK()) {
- max[index] = ((DoubleDistanceResultPair) heap.peek()).getDoubleDistance();
- }
- }
+ /**
+ * It does not make sense, but this version is faster in our larger
+ * benchmarks. Apparently, some JIT optimization kicks in better.
+ *
+ * @param obj Query object
+ * @param k Desired number of neighbors
+ * @return kNN result
+ */
+ KNNResult<DoubleDistance> getKNNForObjectBenchmarked(O obj, int k) {
+ // THIS SHOULD BE SLOWER THAN THE VERSION ABOVE, BUT ISN'T!
+ final TiedTopBoundedHeap<DistanceDBIDPair<DoubleDistance>> heap = new TiedTopBoundedHeap<DistanceDBIDPair<DoubleDistance>>(k, AbstractKNNHeap.COMPARATOR);
+ final DBIDIter iter = relation.iterDBIDs();
+ // First k elements don't need checking.
+ double max = 0.;
+ for (int i = 0; i < k && iter.valid(); i++, iter.advance()) {
+ final double doubleDistance = rawdist.doubleDistance(obj, relation.get(iter));
+ heap.add(DBIDFactory.FACTORY.newDistancePair(new DoubleDistance(doubleDistance), iter));
+ max = Math.max(max, doubleDistance);
+ }
+ // Remaining elements
+ for (; iter.valid(); iter.advance()) {
+ final double doubleDistance = rawdist.doubleDistance(obj, relation.get(iter));
+ if (doubleDistance <= max) {
+ heap.add(DBIDFactory.FACTORY.newDistancePair(new DoubleDistance(doubleDistance), iter));
+ }
+ if (doubleDistance < max) {
+ max = heap.peek().getDistance().doubleValue();
}
}
+ return new GenericKNNList<DoubleDistance>(heap, k);
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/database/query/knn/PreprocessorKNNQuery.java b/src/de/lmu/ifi/dbs/elki/database/query/knn/PreprocessorKNNQuery.java
index 09f93945..a1e12fc3 100644
--- a/src/de/lmu/ifi/dbs/elki/database/query/knn/PreprocessorKNNQuery.java
+++ b/src/de/lmu/ifi/dbs/elki/database/query/knn/PreprocessorKNNQuery.java
@@ -25,20 +25,17 @@ package de.lmu.ifi.dbs.elki.database.query.knn;
import java.util.ArrayList;
import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
import de.lmu.ifi.dbs.elki.database.ids.ArrayDBIDs;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.query.AbstractDataBasedQuery;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNUtil;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
import de.lmu.ifi.dbs.elki.index.preprocessed.knn.AbstractMaterializeKNNPreprocessor;
import de.lmu.ifi.dbs.elki.logging.LoggingUtil;
-import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.KNNHeap;
import de.lmu.ifi.dbs.elki.utilities.exceptions.AbortException;
/**
@@ -145,17 +142,6 @@ public class PreprocessorKNNQuery<O, D extends Distance<D>, T extends KNNResult<
}
@Override
- public void getKNNForBulkHeaps(Map<DBID, KNNHeap<D>> heaps) {
- for(Entry<DBID, KNNHeap<D>> ent : heaps.entrySet()) {
- DBID id = ent.getKey();
- KNNHeap<D> heap = ent.getValue();
- for(DistanceResultPair<D> dr : preprocessor.get(id)) {
- heap.add(dr);
- }
- }
- }
-
- @Override
public KNNResult<D> getKNNForObject(O obj, int k) {
throw new AbortException("Preprocessor KNN query only supports ID queries.");
}
diff --git a/src/de/lmu/ifi/dbs/elki/database/query/knn/package-info.java b/src/de/lmu/ifi/dbs/elki/database/query/knn/package-info.java
index 891ed296..63c3aa16 100644
--- a/src/de/lmu/ifi/dbs/elki/database/query/knn/package-info.java
+++ b/src/de/lmu/ifi/dbs/elki/database/query/knn/package-info.java
@@ -2,6 +2,7 @@
* <p>Prepared queries for k nearest neighbor (kNN) queries.</p>
*
* @apiviz.exclude de.lmu.ifi.dbs.elki.algorithm.*
+ * @apiviz.exclude java.util.*
*/
/*
This file is part of ELKI:
@@ -25,4 +26,4 @@ GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package de.lmu.ifi.dbs.elki.database.query.knn; \ No newline at end of file
+package de.lmu.ifi.dbs.elki.database.query.knn;
diff --git a/src/de/lmu/ifi/dbs/elki/database/query/package-info.java b/src/de/lmu/ifi/dbs/elki/database/query/package-info.java
index 05c635a0..386ce6f3 100644
--- a/src/de/lmu/ifi/dbs/elki/database/query/package-info.java
+++ b/src/de/lmu/ifi/dbs/elki/database/query/package-info.java
@@ -73,6 +73,9 @@
* knnQuery.getKNNForDBID(id, 10);
* }
* }</pre></blockquote>
+ *
+ * @apiviz.exclude java.util.*
+ * @apiviz.exclude de.lmu.ifi.dbs.elki.utilities.*
*/
/*
This file is part of ELKI:
@@ -96,4 +99,4 @@ GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package de.lmu.ifi.dbs.elki.database.query; \ No newline at end of file
+package de.lmu.ifi.dbs.elki.database.query;
diff --git a/src/de/lmu/ifi/dbs/elki/database/query/range/AbstractDistanceRangeQuery.java b/src/de/lmu/ifi/dbs/elki/database/query/range/AbstractDistanceRangeQuery.java
index 871c5e9e..9997f3d6 100644
--- a/src/de/lmu/ifi/dbs/elki/database/query/range/AbstractDistanceRangeQuery.java
+++ b/src/de/lmu/ifi/dbs/elki/database/query/range/AbstractDistanceRangeQuery.java
@@ -25,8 +25,8 @@ package de.lmu.ifi.dbs.elki.database.query.range;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.query.AbstractDataBasedQuery;
-import de.lmu.ifi.dbs.elki.database.query.DistanceDBIDResult;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
/**
diff --git a/src/de/lmu/ifi/dbs/elki/database/query/range/LinearScanPrimitiveDistanceRangeQuery.java b/src/de/lmu/ifi/dbs/elki/database/query/range/LinearScanPrimitiveDistanceRangeQuery.java
index 758d603e..57fa2338 100644
--- a/src/de/lmu/ifi/dbs/elki/database/query/range/LinearScanPrimitiveDistanceRangeQuery.java
+++ b/src/de/lmu/ifi/dbs/elki/database/query/range/LinearScanPrimitiveDistanceRangeQuery.java
@@ -24,8 +24,8 @@ package de.lmu.ifi.dbs.elki.database.query.range;
*/
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
-import de.lmu.ifi.dbs.elki.database.query.DistanceDBIDResult;
import de.lmu.ifi.dbs.elki.database.query.distance.PrimitiveDistanceQuery;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
/**
diff --git a/src/de/lmu/ifi/dbs/elki/database/query/range/LinearScanRangeQuery.java b/src/de/lmu/ifi/dbs/elki/database/query/range/LinearScanRangeQuery.java
index c8ddb1ec..13e13a5c 100644
--- a/src/de/lmu/ifi/dbs/elki/database/query/range/LinearScanRangeQuery.java
+++ b/src/de/lmu/ifi/dbs/elki/database/query/range/LinearScanRangeQuery.java
@@ -23,15 +23,12 @@ package de.lmu.ifi.dbs.elki.database.query.range;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.util.Collections;
-
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
-import de.lmu.ifi.dbs.elki.database.query.DistanceDBIDResult;
-import de.lmu.ifi.dbs.elki.database.query.GenericDistanceDBIDList;
-import de.lmu.ifi.dbs.elki.database.query.GenericDistanceResultPair;
import de.lmu.ifi.dbs.elki.database.query.LinearScanQuery;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.GenericDistanceDBIDList;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
/**
@@ -61,10 +58,10 @@ public class LinearScanRangeQuery<O, D extends Distance<D>> extends AbstractDist
for(DBIDIter iter = relation.getDBIDs().iter(); iter.valid(); iter.advance()) {
D currentDistance = distanceQuery.distance(id, iter);
if(currentDistance.compareTo(range) <= 0) {
- result.add(new GenericDistanceResultPair<D>(currentDistance, iter.getDBID()));
+ result.add(currentDistance, iter);
}
}
- Collections.sort(result);
+ result.sort();
return result;
}
@@ -74,10 +71,10 @@ public class LinearScanRangeQuery<O, D extends Distance<D>> extends AbstractDist
for(DBIDIter iter = relation.getDBIDs().iter(); iter.valid(); iter.advance()) {
D currentDistance = distanceQuery.distance(obj, iter);
if(currentDistance.compareTo(range) <= 0) {
- result.add(new GenericDistanceResultPair<D>(currentDistance, iter.getDBID()));
+ result.add(currentDistance, iter);
}
}
- Collections.sort(result);
+ result.sort();
return result;
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/database/query/range/LinearScanRawDoubleDistanceRangeQuery.java b/src/de/lmu/ifi/dbs/elki/database/query/range/LinearScanRawDoubleDistanceRangeQuery.java
index c5ce2f55..18863b2d 100644
--- a/src/de/lmu/ifi/dbs/elki/database/query/range/LinearScanRawDoubleDistanceRangeQuery.java
+++ b/src/de/lmu/ifi/dbs/elki/database/query/range/LinearScanRawDoubleDistanceRangeQuery.java
@@ -23,17 +23,14 @@ package de.lmu.ifi.dbs.elki.database.query.range;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.util.Collections;
-
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
-import de.lmu.ifi.dbs.elki.database.query.DistanceDBIDResult;
-import de.lmu.ifi.dbs.elki.database.query.DoubleDistanceResultPair;
-import de.lmu.ifi.dbs.elki.database.query.GenericDistanceDBIDList;
import de.lmu.ifi.dbs.elki.database.query.LinearScanQuery;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.database.query.distance.PrimitiveDistanceQuery;
import de.lmu.ifi.dbs.elki.distance.distancefunction.PrimitiveDoubleDistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DoubleDistanceDBIDList;
import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
/**
@@ -63,14 +60,14 @@ public class LinearScanRawDoubleDistanceRangeQuery<O> extends LinearScanRangeQue
double epsilon = range.doubleValue();
O qo = relation.get(id);
- GenericDistanceDBIDList<DoubleDistance> result = new GenericDistanceDBIDList<DoubleDistance>();
+ DoubleDistanceDBIDList result = new DoubleDistanceDBIDList();
for(DBIDIter iter = relation.getDBIDs().iter(); iter.valid(); iter.advance()) {
double doubleDistance = rawdist.doubleDistance(qo, relation.get(iter));
if(doubleDistance <= epsilon) {
- result.add(new DoubleDistanceResultPair(doubleDistance, iter.getDBID()));
+ result.add(doubleDistance, iter);
}
}
- Collections.sort(result);
+ result.sort();
return result;
}
else {
@@ -85,14 +82,14 @@ public class LinearScanRawDoubleDistanceRangeQuery<O> extends LinearScanRangeQue
PrimitiveDoubleDistanceFunction<O> rawdist = (PrimitiveDoubleDistanceFunction<O>) distanceQuery.getDistanceFunction();
double epsilon = range.doubleValue();
- GenericDistanceDBIDList<DoubleDistance> result = new GenericDistanceDBIDList<DoubleDistance>();
+ DoubleDistanceDBIDList result = new DoubleDistanceDBIDList();
for(DBIDIter iter = relation.getDBIDs().iter(); iter.valid(); iter.advance()) {
double doubleDistance = rawdist.doubleDistance(obj, relation.get(iter));
if(doubleDistance <= epsilon) {
- result.add(new DoubleDistanceResultPair(doubleDistance, iter.getDBID()));
+ result.add(doubleDistance, iter);
}
}
- Collections.sort(result);
+ result.sort();
return result;
}
else {
diff --git a/src/de/lmu/ifi/dbs/elki/database/query/range/RangeQuery.java b/src/de/lmu/ifi/dbs/elki/database/query/range/RangeQuery.java
index 2c6842bf..9574cda7 100644
--- a/src/de/lmu/ifi/dbs/elki/database/query/range/RangeQuery.java
+++ b/src/de/lmu/ifi/dbs/elki/database/query/range/RangeQuery.java
@@ -25,7 +25,7 @@ package de.lmu.ifi.dbs.elki.database.query.range;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.query.DatabaseQuery;
-import de.lmu.ifi.dbs.elki.database.query.DistanceDBIDResult;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
/**
diff --git a/src/de/lmu/ifi/dbs/elki/database/query/rknn/AbstractRKNNQuery.java b/src/de/lmu/ifi/dbs/elki/database/query/rknn/AbstractRKNNQuery.java
index fe4f49e1..a090b466 100644
--- a/src/de/lmu/ifi/dbs/elki/database/query/rknn/AbstractRKNNQuery.java
+++ b/src/de/lmu/ifi/dbs/elki/database/query/rknn/AbstractRKNNQuery.java
@@ -23,12 +23,10 @@ package de.lmu.ifi.dbs.elki.database.query.rknn;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.util.List;
-
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.query.AbstractDataBasedQuery;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
/**
@@ -53,5 +51,5 @@ public abstract class AbstractRKNNQuery<O, D extends Distance<D>> extends Abstra
}
@Override
- abstract public List<DistanceResultPair<D>> getRKNNForDBID(DBIDRef id, int k);
+ abstract public DistanceDBIDResult<D> getRKNNForDBID(DBIDRef id, int k);
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/database/query/rknn/LinearScanRKNNQuery.java b/src/de/lmu/ifi/dbs/elki/database/query/rknn/LinearScanRKNNQuery.java
index 1f901828..f0e33777 100644
--- a/src/de/lmu/ifi/dbs/elki/database/query/rknn/LinearScanRKNNQuery.java
+++ b/src/de/lmu/ifi/dbs/elki/database/query/rknn/LinearScanRKNNQuery.java
@@ -24,20 +24,19 @@ package de.lmu.ifi.dbs.elki.database.query.rknn;
*/
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import de.lmu.ifi.dbs.elki.database.ids.ArrayDBIDs;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
-import de.lmu.ifi.dbs.elki.database.query.GenericDistanceResultPair;
import de.lmu.ifi.dbs.elki.database.query.LinearScanQuery;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.database.query.knn.KNNQuery;
-import de.lmu.ifi.dbs.elki.database.query.knn.KNNResult;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResultIter;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.GenericDistanceDBIDList;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
/**
@@ -71,8 +70,8 @@ public class LinearScanRKNNQuery<O, D extends Distance<D>> extends AbstractRKNNQ
}
@Override
- public List<DistanceResultPair<D>> getRKNNForObject(O obj, int k) {
- ArrayList<DistanceResultPair<D>> rNNlist = new ArrayList<DistanceResultPair<D>>();
+ public DistanceDBIDResult<D> getRKNNForObject(O obj, int k) {
+ GenericDistanceDBIDList<D> rNNlist = new GenericDistanceDBIDList<D>();
ArrayDBIDs allIDs = DBIDUtil.ensureArray(relation.getDBIDs());
List<? extends KNNResult<D>> kNNLists = knnQuery.getKNNForBulkDBIDs(allIDs, k);
@@ -83,17 +82,17 @@ public class LinearScanRKNNQuery<O, D extends Distance<D>> extends AbstractRKNNQ
int last = Math.min(k - 1, knn.size() - 1);
D dist = distanceQuery.distance(obj, iter);
if(last < k - 1 || dist.compareTo(knn.get(last).getDistance()) < 1) {
- rNNlist.add(new GenericDistanceResultPair<D>(dist, iter.getDBID()));
+ rNNlist.add(dist, iter);
}
i++;
}
- Collections.sort(rNNlist);
+ rNNlist.sort();
return rNNlist;
}
@Override
- public List<DistanceResultPair<D>> getRKNNForDBID(DBIDRef id, int k) {
- ArrayList<DistanceResultPair<D>> rNNList = new ArrayList<DistanceResultPair<D>>();
+ public DistanceDBIDResult<D> getRKNNForDBID(DBIDRef id, int k) {
+ GenericDistanceDBIDList<D> rNNList = new GenericDistanceDBIDList<D>();
ArrayDBIDs allIDs = DBIDUtil.ensureArray(relation.getDBIDs());
List<? extends KNNResult<D>> kNNList = knnQuery.getKNNForBulkDBIDs(allIDs, k);
@@ -101,22 +100,22 @@ public class LinearScanRKNNQuery<O, D extends Distance<D>> extends AbstractRKNNQ
int i = 0;
for (DBIDIter iter = allIDs.iter(); iter.valid(); iter.advance()) {
KNNResult<D> knn = kNNList.get(i);
- for(DistanceResultPair<D> n : knn) {
- if(n.sameDBID(id)) {
- rNNList.add(new GenericDistanceResultPair<D>(n.getDistance(), iter.getDBID()));
+ for(DistanceDBIDResultIter<D> n = knn.iter(); n.valid(); n.advance()) {
+ if(DBIDUtil.equal(n, id)) {
+ rNNList.add(n.getDistance(), iter);
}
}
i++;
}
- Collections.sort(rNNList);
+ rNNList.sort();
return rNNList;
}
@Override
- public List<List<DistanceResultPair<D>>> getRKNNForBulkDBIDs(ArrayDBIDs ids, int k) {
- List<List<DistanceResultPair<D>>> rNNList = new ArrayList<List<DistanceResultPair<D>>>(ids.size());
+ public List<GenericDistanceDBIDList<D>> getRKNNForBulkDBIDs(ArrayDBIDs ids, int k) {
+ List<GenericDistanceDBIDList<D>> rNNList = new ArrayList<GenericDistanceDBIDList<D>>(ids.size());
for(int i = 0; i < ids.size(); i++) {
- rNNList.add(new ArrayList<DistanceResultPair<D>>());
+ rNNList.add(new GenericDistanceDBIDList<D>());
}
ArrayDBIDs allIDs = DBIDUtil.ensureArray(relation.getDBIDs());
@@ -124,14 +123,13 @@ public class LinearScanRKNNQuery<O, D extends Distance<D>> extends AbstractRKNNQ
int i = 0;
for (DBIDIter iter = allIDs.iter(); iter.valid(); iter.advance()) {
- DBID qid = iter.getDBID();
KNNResult<D> knn = kNNList.get(i);
- for(DistanceResultPair<D> n : knn) {
+ for(DistanceDBIDResultIter<D> n = knn.iter(); n.valid(); n.advance()) {
int j = 0;
for (DBIDIter iter2 = ids.iter(); iter2.valid(); iter2.advance()) {
- if(n.getDBID().sameDBID(iter2)) {
- List<DistanceResultPair<D>> rNN = rNNList.get(j);
- rNN.add(new GenericDistanceResultPair<D>(n.getDistance(), qid));
+ if(DBIDUtil.equal(n, iter2)) {
+ GenericDistanceDBIDList<D> rNN = rNNList.get(j);
+ rNN.add(n.getDistance(), iter);
}
j++;
}
@@ -139,8 +137,7 @@ public class LinearScanRKNNQuery<O, D extends Distance<D>> extends AbstractRKNNQ
i++;
}
for(int j = 0; j < ids.size(); j++) {
- List<DistanceResultPair<D>> rNN = rNNList.get(j);
- Collections.sort(rNN);
+ rNNList.get(j).sort();
}
return rNNList;
}
diff --git a/src/de/lmu/ifi/dbs/elki/database/query/rknn/PreprocessorRKNNQuery.java b/src/de/lmu/ifi/dbs/elki/database/query/rknn/PreprocessorRKNNQuery.java
index ab8c3b30..8256ffad 100644
--- a/src/de/lmu/ifi/dbs/elki/database/query/rknn/PreprocessorRKNNQuery.java
+++ b/src/de/lmu/ifi/dbs/elki/database/query/rknn/PreprocessorRKNNQuery.java
@@ -30,8 +30,8 @@ import de.lmu.ifi.dbs.elki.database.ids.ArrayDBIDs;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.query.AbstractDataBasedQuery;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
import de.lmu.ifi.dbs.elki.index.preprocessed.knn.MaterializeKNNAndRKNNPreprocessor;
import de.lmu.ifi.dbs.elki.logging.LoggingUtil;
@@ -75,7 +75,7 @@ public class PreprocessorRKNNQuery<O, D extends Distance<D>> extends AbstractDat
}
@Override
- public List<DistanceResultPair<D>> getRKNNForDBID(DBIDRef id, int k) {
+ public DistanceDBIDResult<D> getRKNNForDBID(DBIDRef id, int k) {
if(!warned && k != preprocessor.getK()) {
LoggingUtil.warning("Requested more neighbors than preprocessed!");
}
@@ -83,18 +83,18 @@ public class PreprocessorRKNNQuery<O, D extends Distance<D>> extends AbstractDat
}
@Override
- public List<DistanceResultPair<D>> getRKNNForObject(O obj, int k) {
+ public DistanceDBIDResult<D> getRKNNForObject(O obj, int k) {
throw new AbortException("Preprocessor KNN query only supports ID queries.");
}
@Override
- public List<List<DistanceResultPair<D>>> getRKNNForBulkDBIDs(ArrayDBIDs ids, int k) {
+ public List<? extends DistanceDBIDResult<D>> getRKNNForBulkDBIDs(ArrayDBIDs ids, int k) {
if(!warned && k != preprocessor.getK()) {
LoggingUtil.warning("Requested more neighbors than preprocessed!");
}
- List<List<DistanceResultPair<D>>> result = new ArrayList<List<DistanceResultPair<D>>>(ids.size());
+ List<DistanceDBIDResult<D>> result = new ArrayList<DistanceDBIDResult<D>>(ids.size());
for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
- result.add(preprocessor.getRKNN(iter.getDBID()));
+ result.add(preprocessor.getRKNN(iter));
}
return result;
}
diff --git a/src/de/lmu/ifi/dbs/elki/database/query/rknn/RKNNQuery.java b/src/de/lmu/ifi/dbs/elki/database/query/rknn/RKNNQuery.java
index 9ff6f790..dc21948e 100644
--- a/src/de/lmu/ifi/dbs/elki/database/query/rknn/RKNNQuery.java
+++ b/src/de/lmu/ifi/dbs/elki/database/query/rknn/RKNNQuery.java
@@ -28,7 +28,7 @@ import java.util.List;
import de.lmu.ifi.dbs.elki.database.ids.ArrayDBIDs;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.query.DatabaseQuery;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
/**
@@ -36,7 +36,7 @@ import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
*
* @author Erich Schubert
*
- * @apiviz.uses DistanceResultPair oneway - - «create»
+ * @apiviz.uses DistanceDBIDResult oneway - - «create»
*
* @param <O> Object type
* @param <D> Distance type
@@ -49,7 +49,7 @@ public interface RKNNQuery<O, D extends Distance<D>> extends DatabaseQuery {
* @param k number of neighbors requested
* @return reverse k nearest neighbors
*/
- public List<DistanceResultPair<D>> getRKNNForDBID(DBIDRef id, int k);
+ public DistanceDBIDResult<D> getRKNNForDBID(DBIDRef id, int k);
/**
* Get the reverse k nearest neighbors for a particular object.
@@ -58,7 +58,7 @@ public interface RKNNQuery<O, D extends Distance<D>> extends DatabaseQuery {
* @param k number of neighbors requested
* @return reverse k nearest neighbors
*/
- public List<DistanceResultPair<D>> getRKNNForObject(O obj, int k);
+ public DistanceDBIDResult<D> getRKNNForObject(O obj, int k);
/**
* Bulk query method for reverse k nearest neighbors for ids.
@@ -67,5 +67,5 @@ public interface RKNNQuery<O, D extends Distance<D>> extends DatabaseQuery {
* @param k number of neighbors requested
* @return reverse k nearest neighbors
*/
- public List<List<DistanceResultPair<D>>> getRKNNForBulkDBIDs(ArrayDBIDs ids, int k);
+ public List<? extends DistanceDBIDResult<D>> getRKNNForBulkDBIDs(ArrayDBIDs ids, int k);
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/database/relation/DBIDView.java b/src/de/lmu/ifi/dbs/elki/database/relation/DBIDView.java
index 4898c518..b8974ac2 100644
--- a/src/de/lmu/ifi/dbs/elki/database/relation/DBIDView.java
+++ b/src/de/lmu/ifi/dbs/elki/database/relation/DBIDView.java
@@ -70,7 +70,7 @@ public class DBIDView extends AbstractHierarchicalResult implements Relation<DBI
@Override
public DBID get(DBIDRef id) {
assert (ids.contains(id));
- return id.getDBID();
+ return DBIDUtil.deref(id);
}
@Override
@@ -81,8 +81,7 @@ public class DBIDView extends AbstractHierarchicalResult implements Relation<DBI
@Override
public void delete(DBIDRef id) {
if(database instanceof UpdatableDatabase) {
- // TODO: skip getDBID()
- ((UpdatableDatabase) database).delete(id.getDBID());
+ ((UpdatableDatabase) database).delete(id);
}
else {
throw new UnsupportedOperationException("Deletions are not supported.");
diff --git a/src/de/lmu/ifi/dbs/elki/database/relation/RelationUtil.java b/src/de/lmu/ifi/dbs/elki/database/relation/RelationUtil.java
new file mode 100644
index 00000000..77f98f60
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/database/relation/RelationUtil.java
@@ -0,0 +1,105 @@
+package de.lmu.ifi.dbs.elki.database.relation;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import de.lmu.ifi.dbs.elki.data.FeatureVector;
+import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.data.type.VectorFieldTypeInformation;
+
+/**
+ * Utility functions for handling database relation.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.uses Relation oneway
+ */
+public final class RelationUtil {
+ /**
+ * Fake constructor: do not instantiate.
+ */
+ private RelationUtil() {
+ // Do not instantiate!
+ }
+
+ /**
+ * Get the vector field type information from a relation.
+ *
+ * @param relation relation
+ * @param <V> Vector type
+ * @return Vector field type information
+ */
+ public static <V extends FeatureVector<?>> VectorFieldTypeInformation<V> assumeVectorField(Relation<V> relation) {
+ try {
+ return ((VectorFieldTypeInformation<V>) relation.getDataTypeInformation());
+ } catch (Exception e) {
+ throw new UnsupportedOperationException("Expected a vector field, got type information: " + relation.getDataTypeInformation().toString(), e);
+ }
+ }
+
+ /**
+ * Get the number vector factory of a database relation.
+ *
+ * @param relation relation
+ * @param <V> Vector type
+ * @param <N> Number type
+ * @return Vector field type information
+ */
+ public static <V extends NumberVector<? extends N>, N extends Number> NumberVector.Factory<V, N> getNumberVectorFactory(Relation<V> relation) {
+ final VectorFieldTypeInformation<V> type = assumeVectorField(relation);
+ @SuppressWarnings("unchecked")
+ final NumberVector.Factory<V, N> factory = (NumberVector.Factory<V, N>) type.getFactory();
+ return factory;
+ }
+
+ /**
+ * Get the dimensionality of a database relation.
+ *
+ * @param relation relation
+ * @return Database dimensionality
+ */
+ public static int dimensionality(Relation<? extends FeatureVector<?>> relation) {
+ try {
+ return ((VectorFieldTypeInformation<? extends FeatureVector<?>>) relation.getDataTypeInformation()).getDimensionality();
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+
+ /**
+ * Get the column name or produce a generic label "Column XY".
+ *
+ * @param rel Relation
+ * @param col Column
+ * @param <V> Vector type
+ * @return Label
+ */
+ public static <V extends FeatureVector<?>> String getColumnLabel(Relation<? extends V> rel, int col) {
+ String lbl = assumeVectorField(rel).getLabel(col);
+ if (lbl != null) {
+ return lbl;
+ } else {
+ return "Column " + col;
+ }
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/AbstractDatabaseConnection.java b/src/de/lmu/ifi/dbs/elki/datasource/AbstractDatabaseConnection.java
index dfd9f981..25afa6bc 100644
--- a/src/de/lmu/ifi/dbs/elki/datasource/AbstractDatabaseConnection.java
+++ b/src/de/lmu/ifi/dbs/elki/datasource/AbstractDatabaseConnection.java
@@ -58,7 +58,7 @@ public abstract class AbstractDatabaseConnection implements DatabaseConnection {
* Key: {@code -dbc.filter}
* </p>
*/
- public static final OptionID FILTERS_ID = OptionID.getOrCreateOptionID("dbc.filter", "The filters to apply to the input data.");
+ public static final OptionID FILTERS_ID = new OptionID("dbc.filter", "The filters to apply to the input data.");
/**
* Parameter to specify the parser to provide a database.
@@ -66,7 +66,7 @@ public abstract class AbstractDatabaseConnection implements DatabaseConnection {
* Key: {@code -dbc.parser}
* </p>
*/
- public static final OptionID PARSER_ID = OptionID.getOrCreateOptionID("dbc.parser", "Parser to provide the database.");
+ public static final OptionID PARSER_ID = new OptionID("dbc.parser", "Parser to provide the database.");
/**
* The filters to invoke
@@ -182,15 +182,22 @@ public abstract class AbstractDatabaseConnection implements DatabaseConnection {
*
* @apiviz.exclude
*/
- public static abstract class Parameterizer extends AbstractParameterizer {
+ public abstract static class Parameterizer extends AbstractParameterizer {
+ /**
+ * Filters
+ */
protected List<ObjectFilter> filters;
+
+ /**
+ * Parser to use
+ */
protected Parser parser = null;
- @Override
- protected void makeOptions(Parameterization config) {
- super.makeOptions(config);
- }
-
+ /**
+ * Get the filters parameter
+ *
+ * @param config Parameterization
+ */
protected void configFilters(Parameterization config) {
final ObjectListParameter<ObjectFilter> filterParam = new ObjectListParameter<ObjectFilter>(FILTERS_ID, ObjectFilter.class, true);
if(config.grab(filterParam)) {
@@ -198,6 +205,13 @@ public abstract class AbstractDatabaseConnection implements DatabaseConnection {
}
}
+ /**
+ * Get the parser parameter
+ *
+ * @param config Parameterization
+ * @param parserRestrictionClass Restriction class
+ * @param parserDefaultValueClass Default value
+ */
protected void configParser(Parameterization config, Class<?> parserRestrictionClass, Class<?> parserDefaultValueClass) {
ObjectParameter<Parser> parserParam = new ObjectParameter<Parser>(PARSER_ID, parserRestrictionClass, parserDefaultValueClass);
if(config.grab(parserParam)) {
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/ArrayAdapterDatabaseConnection.java b/src/de/lmu/ifi/dbs/elki/datasource/ArrayAdapterDatabaseConnection.java
index d632a3fb..05eda9cb 100644
--- a/src/de/lmu/ifi/dbs/elki/datasource/ArrayAdapterDatabaseConnection.java
+++ b/src/de/lmu/ifi/dbs/elki/datasource/ArrayAdapterDatabaseConnection.java
@@ -51,7 +51,7 @@ public class ArrayAdapterDatabaseConnection implements DatabaseConnection {
double[][] data;
/**
- * Object labels
+ * Object labels.
*/
String[] labels;
@@ -99,7 +99,7 @@ public class ArrayAdapterDatabaseConnection implements DatabaseConnection {
if(startid != null) {
List<DBID> ids = new ArrayList<DBID>(data.length);
for(int i = 0; i < data.length; i++) {
- ids.add(DBIDUtil.importInteger(startid + i));
+ ids.add(DBIDUtil.importInteger(startid.intValue() + i));
}
b.appendColumn(TypeUtil.DBID, Arrays.asList(labels));
}
@@ -114,7 +114,7 @@ public class ArrayAdapterDatabaseConnection implements DatabaseConnection {
}
SimpleTypeInformation<DoubleVector> type;
if(mind == maxd) {
- type = new VectorFieldTypeInformation<DoubleVector>(DoubleVector.class, mind, DoubleVector.STATIC);
+ type = new VectorFieldTypeInformation<DoubleVector>(DoubleVector.FACTORY, mind);
}
else {
type = new SimpleTypeInformation<DoubleVector>(DoubleVector.class);
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/BundleDatabaseConnection.java b/src/de/lmu/ifi/dbs/elki/datasource/BundleDatabaseConnection.java
new file mode 100644
index 00000000..735ed149
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/datasource/BundleDatabaseConnection.java
@@ -0,0 +1,127 @@
+package de.lmu.ifi.dbs.elki.datasource;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.channels.FileChannel;
+import java.util.List;
+
+import de.lmu.ifi.dbs.elki.datasource.bundle.BundleReader;
+import de.lmu.ifi.dbs.elki.datasource.bundle.BundleStreamSource;
+import de.lmu.ifi.dbs.elki.datasource.bundle.MultipleObjectsBundle;
+import de.lmu.ifi.dbs.elki.datasource.filter.ObjectFilter;
+import de.lmu.ifi.dbs.elki.logging.Logging;
+import de.lmu.ifi.dbs.elki.utilities.exceptions.AbortException;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.FileParameter;
+
+/**
+ * Class to load a database from a bundle file.
+ *
+ * Bundle files are stored in a compact binary format along with metadata, so
+ * that parsing should be simpler, albeit the focus was on using it in on-disk
+ * indexes.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.composedOf BundleStreamSource
+ */
+public class BundleDatabaseConnection extends AbstractDatabaseConnection {
+ /**
+ * Class logger.
+ */
+ private static final Logging LOG = Logging.getLogger(BundleDatabaseConnection.class);
+
+ /**
+ * File to load.
+ */
+ private File infile;
+
+ /**
+ * Constructor.
+ *
+ * @param filters Filters
+ * @param infile Input file
+ */
+ public BundleDatabaseConnection(List<ObjectFilter> filters, File infile) {
+ super(filters);
+ this.infile = infile;
+ }
+
+ @Override
+ public MultipleObjectsBundle loadData() {
+ try {
+ FileInputStream fis = new FileInputStream(infile);
+ FileChannel channel = fis.getChannel();
+ BundleStreamSource src = invokeFilters(new BundleReader(channel));
+ MultipleObjectsBundle bundle = MultipleObjectsBundle.fromStream(src);
+ channel.close();
+ fis.close();
+ return bundle;
+ } catch (IOException e) {
+ throw new AbortException("IO error loading bundle", e);
+ }
+ }
+
+ @Override
+ protected Logging getLogger() {
+ return LOG;
+ }
+
+ /**
+ * Parameterization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public static class Parameterizer extends AbstractDatabaseConnection.Parameterizer {
+ /**
+ * Option ID for the bundle parameter.
+ */
+ private static final OptionID BUNDLE_ID = new OptionID("bundle.input", "Bundle file to load the data from.");
+
+ /**
+ * File to load.
+ */
+ private File infile;
+
+ @Override
+ protected void makeOptions(Parameterization config) {
+ super.makeOptions(config);
+ configFilters(config);
+ FileParameter infileP = new FileParameter(BUNDLE_ID, FileParameter.FileType.INPUT_FILE);
+ if (config.grab(infileP)) {
+ infile = infileP.getValue();
+ }
+ }
+
+ @Override
+ protected BundleDatabaseConnection makeInstance() {
+ return new BundleDatabaseConnection(filters, infile);
+ }
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/ConcatenateFilesDatabaseConnection.java b/src/de/lmu/ifi/dbs/elki/datasource/ConcatenateFilesDatabaseConnection.java
index 1a377d7b..0caeadf7 100644
--- a/src/de/lmu/ifi/dbs/elki/datasource/ConcatenateFilesDatabaseConnection.java
+++ b/src/de/lmu/ifi/dbs/elki/datasource/ConcatenateFilesDatabaseConnection.java
@@ -23,6 +23,7 @@ package de.lmu.ifi.dbs.elki.datasource;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
@@ -54,9 +55,9 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.FileListParameter
*/
public class ConcatenateFilesDatabaseConnection extends AbstractDatabaseConnection {
/**
- * Class logger
+ * Class logger.
*/
- private static final Logging logger = Logging.getLogger(ConcatenateFilesDatabaseConnection.class);
+ private static final Logging LOG = Logging.getLogger(ConcatenateFilesDatabaseConnection.class);
/**
* Input file list.
@@ -64,7 +65,7 @@ public class ConcatenateFilesDatabaseConnection extends AbstractDatabaseConnecti
private List<File> files;
/**
- * The parser
+ * The parser.
*/
private Parser parser;
@@ -85,70 +86,68 @@ public class ConcatenateFilesDatabaseConnection extends AbstractDatabaseConnecti
public MultipleObjectsBundle loadData() {
MultipleObjectsBundle objects = new MultipleObjectsBundle();
objects.appendColumn(TypeUtil.STRING, new ArrayList<Object>());
- for(File file : files) {
+ for (File file : files) {
String filestr = file.getPath();
try {
- InputStream inputStream = new FileInputStream(file);
+ InputStream inputStream = new BufferedInputStream(new FileInputStream(file));
inputStream = FileUtil.tryGzipInput(inputStream);
final BundleStreamSource source;
- if(parser instanceof StreamingParser) {
+ if (parser instanceof StreamingParser) {
final StreamingParser streamParser = (StreamingParser) parser;
streamParser.initStream(inputStream);
source = streamParser;
- }
- else {
+ } else {
MultipleObjectsBundle parsingResult = parser.parse(inputStream);
// normalize objects and transform labels
source = new StreamFromBundle(parsingResult);
}
BundleMeta meta = null; // NullPointerException on invalid streams
- loop: for(Event e = source.nextEvent();; e = source.nextEvent()) {
- switch(e){
+ loop: for (Event e = source.nextEvent();; e = source.nextEvent()) {
+ switch(e) {
case END_OF_STREAM:
break loop;
case META_CHANGED:
meta = source.getMeta();
- for(int i = 0; i < meta.size(); i++) {
- if(i + 1 >= objects.metaLength()) {
+ for (int i = 0; i < meta.size(); i++) {
+ if (i + 1 >= objects.metaLength()) {
objects.appendColumn(meta.get(i), new ArrayList<Object>());
- }
- else {
+ } else {
// Ensure compatibility:
- if(!objects.meta(i + 1).isAssignableFromType(meta.get(i))) {
+ if (!objects.meta(i + 1).isAssignableFromType(meta.get(i))) {
throw new AbortException("Incompatible files loaded. Cannot concatenate with unaligned columns, please preprocess manually.");
}
}
}
- break;
+ break; // switch
case NEXT_OBJECT:
Object[] o = new Object[objects.metaLength()];
o[0] = filestr;
- for(int i = 0; i < meta.size(); i++) {
+ for (int i = 0; i < meta.size(); i++) {
o[i + 1] = source.data(i);
}
objects.appendSimple(o);
+ break; // switch
}
}
- }
- catch(IOException e) {
+ } catch (IOException e) {
throw new AbortException("Loading file " + filestr + " failed: " + e.toString(), e);
}
}
// Invoke filters
- if(logger.isDebugging()) {
- logger.debugFine("Invoking filters.");
+ if (LOG.isDebugging()) {
+ LOG.debugFine("Invoking filters.");
}
return invokeFilters(objects);
}
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
- * Parameterization class
+ * Parameterization class.
*
* @author Erich Schubert
*
@@ -156,18 +155,19 @@ public class ConcatenateFilesDatabaseConnection extends AbstractDatabaseConnecti
*/
public static class Parameterizer extends AbstractDatabaseConnection.Parameterizer {
/**
- * The input files
+ * The input files.
*/
private List<File> files;
@Override
protected void makeOptions(Parameterization config) {
+ super.makeOptions(config);
FileListParameter filesP = new FileListParameter(FileBasedDatabaseConnection.INPUT_ID, FilesType.INPUT_FILES);
- if(config.grab(filesP)) {
+ if (config.grab(filesP)) {
files = filesP.getValue();
}
+ configFilters(config);
configParser(config, Parser.class, NumberVectorLabelParser.class);
- super.makeOptions(config);
}
@Override
@@ -175,4 +175,4 @@ public class ConcatenateFilesDatabaseConnection extends AbstractDatabaseConnecti
return new ConcatenateFilesDatabaseConnection(files, parser, filters);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/DBIDRangeDatabaseConnection.java b/src/de/lmu/ifi/dbs/elki/datasource/DBIDRangeDatabaseConnection.java
index 05af3cad..b93b81eb 100644
--- a/src/de/lmu/ifi/dbs/elki/datasource/DBIDRangeDatabaseConnection.java
+++ b/src/de/lmu/ifi/dbs/elki/datasource/DBIDRangeDatabaseConnection.java
@@ -87,12 +87,12 @@ public class DBIDRangeDatabaseConnection implements DatabaseConnection {
/**
* Parameter for starting ID to generate
*/
- private static final OptionID START_ID = OptionID.getOrCreateOptionID("idgen.start", "First integer DBID to generate.");
+ private static final OptionID START_ID = new OptionID("idgen.start", "First integer DBID to generate.");
/**
* Parameter for number of IDs to generate
*/
- private static final OptionID COUNT_ID = OptionID.getOrCreateOptionID("idgen.count", "Number of DBID to generate.");
+ private static final OptionID COUNT_ID = new OptionID("idgen.count", "Number of DBID to generate.");
/**
* Begin of interval
@@ -107,13 +107,13 @@ public class DBIDRangeDatabaseConnection implements DatabaseConnection {
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- IntParameter startp = new IntParameter(START_ID, 0);
+ IntParameter startp = new IntParameter(START_ID, Integer.valueOf(0));
if(config.grab(startp)) {
- start = startp.getValue();
+ start = startp.getValue().intValue();
}
IntParameter countp = new IntParameter(COUNT_ID);
if(config.grab(countp)) {
- count = countp.getValue();
+ count = countp.getValue().intValue();
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/EmptyDatabaseConnection.java b/src/de/lmu/ifi/dbs/elki/datasource/EmptyDatabaseConnection.java
index 5a365119..43aab20a 100644
--- a/src/de/lmu/ifi/dbs/elki/datasource/EmptyDatabaseConnection.java
+++ b/src/de/lmu/ifi/dbs/elki/datasource/EmptyDatabaseConnection.java
@@ -39,7 +39,7 @@ public class EmptyDatabaseConnection extends AbstractDatabaseConnection {
/**
* Static logger
*/
- private static final Logging logger = Logging.getLogger(EmptyDatabaseConnection.class);
+ private static final Logging LOG = Logging.getLogger(EmptyDatabaseConnection.class);
/**
* Constructor.
@@ -57,6 +57,6 @@ public class EmptyDatabaseConnection extends AbstractDatabaseConnection {
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/ExternalIDJoinDatabaseConnection.java b/src/de/lmu/ifi/dbs/elki/datasource/ExternalIDJoinDatabaseConnection.java
index 0b81211b..53468c96 100644
--- a/src/de/lmu/ifi/dbs/elki/datasource/ExternalIDJoinDatabaseConnection.java
+++ b/src/de/lmu/ifi/dbs/elki/datasource/ExternalIDJoinDatabaseConnection.java
@@ -23,10 +23,11 @@ package de.lmu.ifi.dbs.elki.datasource;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+import gnu.trove.impl.Constants;
+import gnu.trove.map.hash.TObjectIntHashMap;
+
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
import de.lmu.ifi.dbs.elki.data.ExternalID;
import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
@@ -51,7 +52,7 @@ public class ExternalIDJoinDatabaseConnection extends AbstractDatabaseConnection
/**
* Logger
*/
- private static final Logging logger = Logging.getLogger(ExternalIDJoinDatabaseConnection.class);
+ private static final Logging LOG = Logging.getLogger(ExternalIDJoinDatabaseConnection.class);
/**
* The filters to invoke
@@ -72,60 +73,60 @@ public class ExternalIDJoinDatabaseConnection extends AbstractDatabaseConnection
@Override
public MultipleObjectsBundle loadData() {
List<MultipleObjectsBundle> bundles = new ArrayList<MultipleObjectsBundle>(sources.size());
- for(DatabaseConnection dbc : sources) {
+ for (DatabaseConnection dbc : sources) {
bundles.add(dbc.loadData());
}
MultipleObjectsBundle first = bundles.get(0);
- Map<ExternalID, Integer> labelmap = new HashMap<ExternalID, Integer>(first.dataLength());
+ TObjectIntHashMap<ExternalID> labelmap = new TObjectIntHashMap<ExternalID>(first.dataLength(), Constants.DEFAULT_LOAD_FACTOR, -1);
// Process first bundle
{
// Identify a label column
final int lblcol;
{
int lblc = -1;
- for(int i = 0; i < first.metaLength(); i++) {
- if(TypeUtil.EXTERNALID.isAssignableFromType(first.meta(i))) {
+ for (int i = 0; i < first.metaLength(); i++) {
+ if (TypeUtil.EXTERNALID.isAssignableFromType(first.meta(i))) {
lblc = i;
break;
}
}
lblcol = lblc; // make static
}
- if(lblcol == -1) {
+ if (lblcol == -1) {
throw new AbortException("No external ID column found in primary source.");
}
- for(int i = 0; i < first.dataLength(); i++) {
+ for (int i = 0; i < first.dataLength(); i++) {
ExternalID data = (ExternalID) first.data(i, lblcol);
- if(data == null) {
- logger.debug("Object without ID encountered.");
+ if (data == null) {
+ LOG.debug("Object without ID encountered.");
continue;
}
- Integer old = labelmap.put(data, i);
- if(old != null) {
- logger.debug("Duplicate id encountered: " + data + " in rows " + old + " and " + i);
+ int old = labelmap.put(data, i);
+ if (old != -1) {
+ LOG.debug("Duplicate id encountered: " + data + " in rows " + old + " and " + i);
}
}
}
// Process additional columns
- for(int c = 1; c < sources.size(); c++) {
+ for (int c = 1; c < sources.size(); c++) {
MultipleObjectsBundle cur = bundles.get(c);
final int lblcol;
{
int lblc = -1;
- for(int i = 0; i < cur.metaLength(); i++) {
- if(TypeUtil.EXTERNALID.isAssignableFromType(cur.meta(i))) {
+ for (int i = 0; i < cur.metaLength(); i++) {
+ if (TypeUtil.EXTERNALID.isAssignableFromType(cur.meta(i))) {
lblc = i;
break;
}
}
lblcol = lblc; // make static
}
- if(lblcol == -1) {
- StringBuffer buf = new StringBuffer();
- for(int i = 0; i < cur.metaLength(); i++) {
- if(buf.length() > 0) {
- buf.append(",");
+ if (lblcol == -1) {
+ StringBuilder buf = new StringBuilder();
+ for (int i = 0; i < cur.metaLength(); i++) {
+ if (buf.length() > 0) {
+ buf.append(',');
}
buf.append(cur.meta(i));
}
@@ -133,33 +134,33 @@ public class ExternalIDJoinDatabaseConnection extends AbstractDatabaseConnection
}
// Destination columns
List<ArrayList<Object>> dcol = new ArrayList<ArrayList<Object>>(cur.metaLength());
- for(int i = 0; i < cur.metaLength(); i++) {
+ for (int i = 0; i < cur.metaLength(); i++) {
// Skip the label columns
- if(i == lblcol) {
+ if (i == lblcol) {
dcol.add(null);
continue;
}
ArrayList<Object> newcol = new ArrayList<Object>(first.dataLength());
// Pre-fill with nulls.
- for(int j = 0; j < first.dataLength(); j++) {
+ for (int j = 0; j < first.dataLength(); j++) {
newcol.add(null);
}
first.appendColumn(cur.meta(i), newcol);
dcol.add(newcol);
}
- for(int i = 0; i < cur.dataLength(); i++) {
+ for (int i = 0; i < cur.dataLength(); i++) {
ExternalID data = (ExternalID) cur.data(i, lblcol);
- if(data == null) {
- logger.warning("Object without label encountered.");
+ if (data == null) {
+ LOG.warning("Object without label encountered.");
continue;
}
- Integer row = labelmap.get(data);
- if(row == null) {
- logger.debug("ID not found for join: " + data + " in row " + i);
+ int row = labelmap.get(data);
+ if (row == -1) {
+ LOG.debug("ID not found for join: " + data + " in row " + i);
continue;
}
- for(int d = 0; d < cur.metaLength(); d++) {
- if(d == lblcol) {
+ for (int d = 0; d < cur.metaLength(); d++) {
+ if (d == lblcol) {
continue;
}
List<Object> col = dcol.get(d);
@@ -168,22 +169,21 @@ public class ExternalIDJoinDatabaseConnection extends AbstractDatabaseConnection
}
}
}
- for(int i = 0; i < first.dataLength(); i++) {
- for(int d = 0; d < first.metaLength(); d++) {
- if(first.data(i, d) == null) {
- StringBuffer buf = new StringBuffer();
- for(int d2 = 0; d2 < first.metaLength(); d2++) {
- if(buf.length() > 0) {
+ for (int i = 0; i < first.dataLength(); i++) {
+ for (int d = 0; d < first.metaLength(); d++) {
+ if (first.data(i, d) == null) {
+ StringBuilder buf = new StringBuilder();
+ for (int d2 = 0; d2 < first.metaLength(); d2++) {
+ if (buf.length() > 0) {
buf.append(", ");
}
- if(first.data(i, d2) == null) {
+ if (first.data(i, d2) == null) {
buf.append("null");
- }
- else {
+ } else {
buf.append(first.data(i, d2));
}
}
- logger.warning("null value in joined data, row " + i + " column " + d + FormatUtil.NEWLINE + "[" + buf.toString() + "]");
+ LOG.warning("null value in joined data, row " + i + " column " + d + FormatUtil.NEWLINE + "[" + buf.toString() + "]");
break;
}
}
@@ -194,7 +194,7 @@ public class ExternalIDJoinDatabaseConnection extends AbstractDatabaseConnection
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -208,7 +208,7 @@ public class ExternalIDJoinDatabaseConnection extends AbstractDatabaseConnection
/**
* The static option ID
*/
- public static final OptionID SOURCES_ID = OptionID.getOrCreateOptionID("join.sources", "The data sources to join.");
+ public static final OptionID SOURCES_ID = new OptionID("join.sources", "The data sources to join.");
/**
* The data souces to use.
@@ -220,7 +220,7 @@ public class ExternalIDJoinDatabaseConnection extends AbstractDatabaseConnection
super.makeOptions(config);
super.configFilters(config);
final ObjectListParameter<DatabaseConnection> sourcesParam = new ObjectListParameter<DatabaseConnection>(SOURCES_ID, DatabaseConnection.class);
- if(config.grab(sourcesParam)) {
+ if (config.grab(sourcesParam)) {
sources = sourcesParam.instantiateClasses(config);
}
}
@@ -230,4 +230,4 @@ public class ExternalIDJoinDatabaseConnection extends AbstractDatabaseConnection
return new ExternalIDJoinDatabaseConnection(filters, sources);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/FileBasedDatabaseConnection.java b/src/de/lmu/ifi/dbs/elki/datasource/FileBasedDatabaseConnection.java
index 7973a6f0..42347841 100644
--- a/src/de/lmu/ifi/dbs/elki/datasource/FileBasedDatabaseConnection.java
+++ b/src/de/lmu/ifi/dbs/elki/datasource/FileBasedDatabaseConnection.java
@@ -23,6 +23,7 @@ package de.lmu.ifi.dbs.elki.datasource;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -50,7 +51,7 @@ public class FileBasedDatabaseConnection extends InputStreamDatabaseConnection {
* Key: {@code -dbc.in}
* </p>
*/
- public static final OptionID INPUT_ID = OptionID.getOrCreateOptionID("dbc.in", "The name of the input file to be parsed.");
+ public static final OptionID INPUT_ID = new OptionID("dbc.in", "The name of the input file to be parsed.");
/**
* Constructor.
@@ -80,8 +81,7 @@ public class FileBasedDatabaseConnection extends InputStreamDatabaseConnection {
final FileParameter inputParam = new FileParameter(INPUT_ID, FileParameter.FileType.INPUT_FILE);
if(config.grab(inputParam)) {
try {
- inputStream = new FileInputStream(inputParam.getValue());
- inputStream = FileUtil.tryGzipInput(inputStream);
+ inputStream = new BufferedInputStream(FileUtil.tryGzipInput(new FileInputStream(inputParam.getValue())));
}
catch(IOException e) {
config.reportError(new WrongParameterValueException(inputParam, inputParam.getValue().getPath(), e));
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/GeneratorXMLDatabaseConnection.java b/src/de/lmu/ifi/dbs/elki/datasource/GeneratorXMLDatabaseConnection.java
index faf557f0..9fe7febe 100644
--- a/src/de/lmu/ifi/dbs/elki/datasource/GeneratorXMLDatabaseConnection.java
+++ b/src/de/lmu/ifi/dbs/elki/datasource/GeneratorXMLDatabaseConnection.java
@@ -24,10 +24,8 @@ package de.lmu.ifi.dbs.elki.datasource;
*/
import java.io.File;
-import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
-import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
@@ -54,6 +52,7 @@ import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector;
import de.lmu.ifi.dbs.elki.math.statistics.distribution.DistributionWithRandom;
import de.lmu.ifi.dbs.elki.math.statistics.distribution.GammaDistribution;
+import de.lmu.ifi.dbs.elki.math.statistics.distribution.HaltonUniformDistribution;
import de.lmu.ifi.dbs.elki.math.statistics.distribution.NormalDistribution;
import de.lmu.ifi.dbs.elki.math.statistics.distribution.UniformDistribution;
import de.lmu.ifi.dbs.elki.utilities.exceptions.AbortException;
@@ -63,7 +62,7 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.DoubleParameter;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.FileParameter;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.RandomParameter;
import de.lmu.ifi.dbs.elki.utilities.xml.XMLNodeIterator;
/**
@@ -77,10 +76,90 @@ import de.lmu.ifi.dbs.elki.utilities.xml.XMLNodeIterator;
* @apiviz.composedOf GeneratorMain
*/
public class GeneratorXMLDatabaseConnection implements DatabaseConnection {
+ /** Dataset tag */
+ public static final String TAG_DATASET = "dataset";
+
+ /** Cluster tag */
+ public static final String TAG_CLUSTER = "cluster";
+
+ /** Uniform distribution */
+ public static final String TAG_UNIFORM = "uniform";
+
+ /** Normal distribution */
+ public static final String TAG_NORMAL = "normal";
+
+ /** Gamma distribution */
+ public static final String TAG_GAMMA = "gamma";
+
+ /**
+ * Halton pseudo uniform distribution.
+ */
+ public static final String TAG_HALTON = "halton";
+
+ /** Rotation */
+ public static final String TAG_ROTATE = "rotate";
+
+ /** Translation */
+ public static final String TAG_TRANSLATE = "translate";
+
+ /** Clipping */
+ public static final String TAG_CLIP = "clip";
+
+ /** Static cluster */
+ public static final String TAG_STATIC = "static";
+
+ /** Point in static cluster */
+ public static final String TAG_POINT = "point";
+
+ /** Attribute to control model testing */
+ public static final String ATTR_TEST = "test-model";
+
+ /** Random seed */
+ public static final String ATTR_SEED = "random-seed";
+
+ /** Density correction factor */
+ public static final String ATTR_DENSITY = "density-correction";
+
+ /** Cluster nane */
+ public static final String ATTR_NAME = "name";
+
+ /** Cluster size */
+ public static final String ATTR_SIZE = "size";
+
+ /** Minimum value */
+ public static final String ATTR_MIN = "min";
+
+ /** Maximum value */
+ public static final String ATTR_MAX = "max";
+
+ /** Mean */
+ public static final String ATTR_MEAN = "mean";
+
+ /** Standard deviation */
+ public static final String ATTR_STDDEV = "stddev";
+
+ /** Gamma k */
+ public static final String ATTR_K = "k";
+
+ /** Gamma theta */
+ public static final String ATTR_THETA = "theta";
+
+ /** Vector */
+ public static final String ATTR_VECTOR = "vector";
+
+ /** First axis for rotation plane */
+ public static final String ATTR_AXIS1 = "axis1";
+
+ /** Second axis for rotation plane */
+ public static final String ATTR_AXIS2 = "axis2";
+
+ /** Rotation angle */
+ public static final String ATTR_ANGLE = "angle";
+
/**
* Logger
*/
- private static Logging logger = Logging.getLogger(GeneratorXMLDatabaseConnection.class);
+ private static final Logging LOG = Logging.getLogger(GeneratorXMLDatabaseConnection.class);
/**
* A pattern defining whitespace.
@@ -90,22 +169,22 @@ public class GeneratorXMLDatabaseConnection implements DatabaseConnection {
/**
* Parameter to give the configuration file
*/
- public static final OptionID CONFIGFILE_ID = OptionID.getOrCreateOptionID("bymodel.spec", "The generator specification file.");
+ public static final OptionID CONFIGFILE_ID = new OptionID("bymodel.spec", "The generator specification file.");
/**
* Parameter to give the configuration file
*/
- public static final OptionID RANDOMSEED_ID = OptionID.getOrCreateOptionID("bymodel.randomseed", "The random generator seed.");
+ public static final OptionID RANDOMSEED_ID = new OptionID("bymodel.randomseed", "The random generator seed.");
/**
* Parameter to give the configuration file
*/
- public static final OptionID SIZE_SCALE_ID = OptionID.getOrCreateOptionID("bymodel.sizescale", "Factor for scaling the specified cluster sizes.");
+ public static final OptionID SIZE_SCALE_ID = new OptionID("bymodel.sizescale", "Factor for scaling the specified cluster sizes.");
/**
* File name of the generators XML Schema file.
*/
- private static final String GENERATOR_SCHEMA_FILE = GeneratorXMLSpec.class.getPackage().getName().replace('.', '/') + '/' + "GeneratorByModel.xsd";
+ public static final String GENERATOR_SCHEMA_FILE = GeneratorXMLSpec.class.getPackage().getName().replace('.', '/') + '/' + "GeneratorByModel.xsd";
/**
* The configuration file.
@@ -139,33 +218,31 @@ public class GeneratorXMLDatabaseConnection implements DatabaseConnection {
this.specfile = specfile;
this.sizescale = sizescale;
this.clusterRandom = clusterRandom;
- if(this.clusterRandom == null) {
+ if (this.clusterRandom == null) {
this.clusterRandom = new Random();
}
}
@Override
public MultipleObjectsBundle loadData() {
- if(logger.isVerbose()) {
- logger.verbose("Loading specification ...");
+ if (LOG.isVerbose()) {
+ LOG.verbose("Loading specification ...");
}
GeneratorMain gen;
try {
gen = loadXMLSpecification();
- }
- catch(UnableToComplyException e) {
+ } catch (UnableToComplyException e) {
throw new AbortException("Cannot load XML specification", e);
}
- if(logger.isVerbose()) {
- logger.verbose("Generating clusters ...");
+ if (LOG.isVerbose()) {
+ LOG.verbose("Generating clusters ...");
}
- if(testAgainstModel != null) {
- gen.setTestAgainstModel(testAgainstModel);
+ if (testAgainstModel != null) {
+ gen.setTestAgainstModel(testAgainstModel.booleanValue());
}
try {
return gen.generate();
- }
- catch(UnableToComplyException e) {
+ } catch (UnableToComplyException e) {
throw new AbortException("Data generation failed. ", e);
}
}
@@ -179,44 +256,36 @@ public class GeneratorXMLDatabaseConnection implements DatabaseConnection {
*/
private GeneratorMain loadXMLSpecification() throws UnableToComplyException {
try {
- InputStream in = new FileInputStream(specfile);
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
URL url = ClassLoader.getSystemResource(GENERATOR_SCHEMA_FILE);
- if(url != null) {
+ if (url != null) {
try {
Schema schema = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI).newSchema(url);
dbf.setSchema(schema);
dbf.setIgnoringElementContentWhitespace(true);
+ } catch (Exception e) {
+ LOG.warning("Could not set up XML Schema validation for speciciation file.", e);
}
- catch(Exception e) {
- logger.warning("Could not set up XML Schema validation for speciciation file.");
- }
- }
- else {
- logger.warning("Could not set up XML Schema validation for speciciation file.");
+ } else {
+ LOG.warning("Could not set up XML Schema validation for speciciation file.");
}
- Document doc = dbf.newDocumentBuilder().parse(in);
+ Document doc = dbf.newDocumentBuilder().parse(specfile);
Node root = doc.getDocumentElement();
- if(root.getNodeName() == "dataset") {
+ if (TAG_DATASET.equals(root.getNodeName())) {
GeneratorMain gen = new GeneratorMain();
processElementDataset(gen, root);
return gen;
- }
- else {
+ } else {
throw new UnableToComplyException("Experiment specification has incorrect document element: " + root.getNodeName());
}
- }
- catch(FileNotFoundException e) {
+ } catch (FileNotFoundException e) {
throw new UnableToComplyException("Can't open specification file.", e);
- }
- catch(SAXException e) {
+ } catch (SAXException e) {
throw new UnableToComplyException("Error parsing specification file.", e);
- }
- catch(IOException e) {
+ } catch (IOException e) {
throw new UnableToComplyException("IO Exception loading specification file.", e);
- }
- catch(ParserConfigurationException e) {
+ } catch (ParserConfigurationException e) {
throw new UnableToComplyException("Parser Configuration Error", e);
}
}
@@ -230,26 +299,24 @@ public class GeneratorXMLDatabaseConnection implements DatabaseConnection {
*/
private void processElementDataset(GeneratorMain gen, Node cur) throws UnableToComplyException {
// *** get parameters
- String seedstr = ((Element) cur).getAttribute("random-seed");
- if(seedstr != null && seedstr != "") {
- clusterRandom = new Random((int) (Integer.valueOf(seedstr) * sizescale));
+ String seedstr = ((Element) cur).getAttribute(ATTR_SEED);
+ if (seedstr != null && seedstr.length() > 0) {
+ clusterRandom = new Random((int) (Integer.parseInt(seedstr) * sizescale));
}
- String testmod = ((Element) cur).getAttribute("test-model");
- if(testmod != null && testmod != "") {
- testAgainstModel = (Integer.valueOf(testmod) != 0);
+ String testmod = ((Element) cur).getAttribute(ATTR_TEST);
+ if (testmod != null && testmod.length() > 0) {
+ testAgainstModel = Boolean.valueOf(Integer.parseInt(testmod) != 0);
}
// TODO: check for unknown attributes.
XMLNodeIterator iter = new XMLNodeIterator(cur.getFirstChild());
- while(iter.hasNext()) {
+ while (iter.hasNext()) {
Node child = iter.next();
- if(child.getNodeName() == "cluster") {
+ if (TAG_CLUSTER.equals(child.getNodeName())) {
processElementCluster(gen, child);
- }
- else if(child.getNodeName() == "static") {
+ } else if (TAG_STATIC.equals(child.getNodeName())) {
processElementStatic(gen, child);
- }
- else if(child.getNodeType() == Node.ELEMENT_NODE) {
- logger.warning("Unknown element in XML specification file: " + child.getNodeName());
+ } else if (child.getNodeType() == Node.ELEMENT_NODE) {
+ LOG.warning("Unknown element in XML specification file: " + child.getNodeName());
}
}
}
@@ -265,22 +332,22 @@ public class GeneratorXMLDatabaseConnection implements DatabaseConnection {
int size = -1;
double overweight = 1.0;
- String sizestr = ((Element) cur).getAttribute("size");
- if(sizestr != null && sizestr != "") {
- size = (int) (Integer.valueOf(sizestr) * sizescale);
+ String sizestr = ((Element) cur).getAttribute(ATTR_SIZE);
+ if (sizestr != null && sizestr.length() > 0) {
+ size = (int) (Integer.parseInt(sizestr) * sizescale);
}
- String name = ((Element) cur).getAttribute("name");
+ String name = ((Element) cur).getAttribute(ATTR_NAME);
- String dcostr = ((Element) cur).getAttribute("density-correction");
- if(dcostr != null && dcostr != "") {
+ String dcostr = ((Element) cur).getAttribute(ATTR_DENSITY);
+ if (dcostr != null && dcostr.length() > 0) {
overweight = Double.parseDouble(dcostr);
}
- if(size < 0) {
+ if (size < 0) {
throw new UnableToComplyException("No valid cluster size given in specification file.");
}
- if(name == null || name == "") {
+ if (name == null || name.length() == 0) {
throw new UnableToComplyException("No cluster name given in specification file.");
}
@@ -290,28 +357,24 @@ public class GeneratorXMLDatabaseConnection implements DatabaseConnection {
// TODO: check for unknown attributes.
XMLNodeIterator iter = new XMLNodeIterator(cur.getFirstChild());
- while(iter.hasNext()) {
+ while (iter.hasNext()) {
Node child = iter.next();
- if(child.getNodeName() == "uniform") {
+ if (TAG_UNIFORM.equals(child.getNodeName())) {
processElementUniform(cluster, child);
- }
- else if(child.getNodeName() == "normal") {
+ } else if (TAG_NORMAL.equals(child.getNodeName())) {
processElementNormal(cluster, child);
- }
- else if(child.getNodeName() == "gamma") {
+ } else if (TAG_GAMMA.equals(child.getNodeName())) {
processElementGamma(cluster, child);
- }
- else if(child.getNodeName() == "rotate") {
+ } else if (TAG_HALTON.equals(child.getNodeName())) {
+ processElementHalton(cluster, child);
+ } else if (TAG_ROTATE.equals(child.getNodeName())) {
processElementRotate(cluster, child);
- }
- else if(child.getNodeName() == "translate") {
+ } else if (TAG_TRANSLATE.equals(child.getNodeName())) {
processElementTranslate(cluster, child);
- }
- else if(child.getNodeName() == "clip") {
+ } else if (TAG_CLIP.equals(child.getNodeName())) {
processElementClipping(cluster, child);
- }
- else if(child.getNodeType() == Node.ELEMENT_NODE) {
- logger.warning("Unknown element in XML specification file: " + child.getNodeName());
+ } else if (child.getNodeType() == Node.ELEMENT_NODE) {
+ LOG.warning("Unknown element in XML specification file: " + child.getNodeName());
}
}
@@ -329,12 +392,12 @@ public class GeneratorXMLDatabaseConnection implements DatabaseConnection {
double min = 0.0;
double max = 1.0;
- String minstr = ((Element) cur).getAttribute("min");
- if(minstr != null && minstr != "") {
+ String minstr = ((Element) cur).getAttribute(ATTR_MIN);
+ if (minstr != null && minstr.length() > 0) {
min = Double.parseDouble(minstr);
}
- String maxstr = ((Element) cur).getAttribute("max");
- if(maxstr != null && maxstr != "") {
+ String maxstr = ((Element) cur).getAttribute(ATTR_MAX);
+ if (maxstr != null && maxstr.length() > 0) {
max = Double.parseDouble(maxstr);
}
@@ -345,10 +408,10 @@ public class GeneratorXMLDatabaseConnection implements DatabaseConnection {
// TODO: check for unknown attributes.
XMLNodeIterator iter = new XMLNodeIterator(cur.getFirstChild());
- while(iter.hasNext()) {
+ while (iter.hasNext()) {
Node child = iter.next();
- if(child.getNodeType() == Node.ELEMENT_NODE) {
- logger.warning("Unknown element in XML specification file: " + child.getNodeName());
+ if (child.getNodeType() == Node.ELEMENT_NODE) {
+ LOG.warning("Unknown element in XML specification file: " + child.getNodeName());
}
}
}
@@ -363,12 +426,12 @@ public class GeneratorXMLDatabaseConnection implements DatabaseConnection {
private void processElementNormal(GeneratorSingleCluster cluster, Node cur) throws UnableToComplyException {
double mean = 0.0;
double stddev = 1.0;
- String meanstr = ((Element) cur).getAttribute("mean");
- if(meanstr != null && meanstr != "") {
+ String meanstr = ((Element) cur).getAttribute(ATTR_MEAN);
+ if (meanstr != null && meanstr.length() > 0) {
mean = Double.parseDouble(meanstr);
}
- String stddevstr = ((Element) cur).getAttribute("stddev");
- if(stddevstr != null && stddevstr != "") {
+ String stddevstr = ((Element) cur).getAttribute(ATTR_STDDEV);
+ if (stddevstr != null && stddevstr.length() > 0) {
stddev = Double.parseDouble(stddevstr);
}
@@ -379,10 +442,10 @@ public class GeneratorXMLDatabaseConnection implements DatabaseConnection {
// TODO: check for unknown attributes.
XMLNodeIterator iter = new XMLNodeIterator(cur.getFirstChild());
- while(iter.hasNext()) {
+ while (iter.hasNext()) {
Node child = iter.next();
- if(child.getNodeType() == Node.ELEMENT_NODE) {
- logger.warning("Unknown element in XML specification file: " + child.getNodeName());
+ if (child.getNodeType() == Node.ELEMENT_NODE) {
+ LOG.warning("Unknown element in XML specification file: " + child.getNodeName());
}
}
}
@@ -397,12 +460,12 @@ public class GeneratorXMLDatabaseConnection implements DatabaseConnection {
private void processElementGamma(GeneratorSingleCluster cluster, Node cur) throws UnableToComplyException {
double k = 1.0;
double theta = 1.0;
- String kstr = ((Element) cur).getAttribute("k");
- if(kstr != null && kstr != "") {
+ String kstr = ((Element) cur).getAttribute(ATTR_K);
+ if (kstr != null && kstr.length() > 0) {
k = Double.parseDouble(kstr);
}
- String thetastr = ((Element) cur).getAttribute("theta");
- if(thetastr != null && thetastr != "") {
+ String thetastr = ((Element) cur).getAttribute(ATTR_THETA);
+ if (thetastr != null && thetastr.length() > 0) {
theta = Double.parseDouble(thetastr);
}
@@ -413,10 +476,45 @@ public class GeneratorXMLDatabaseConnection implements DatabaseConnection {
// TODO: check for unknown attributes.
XMLNodeIterator iter = new XMLNodeIterator(cur.getFirstChild());
- while(iter.hasNext()) {
+ while (iter.hasNext()) {
+ Node child = iter.next();
+ if (child.getNodeType() == Node.ELEMENT_NODE) {
+ LOG.warning("Unknown element in XML specification file: " + child.getNodeName());
+ }
+ }
+ }
+
+ /**
+ * Process a 'halton' Element in the XML stream.
+ *
+ * @param cluster
+ * @param cur Current document nod
+ * @throws UnableToComplyException
+ */
+ private void processElementHalton(GeneratorSingleCluster cluster, Node cur) throws UnableToComplyException {
+ double min = 0.0;
+ double max = 1.0;
+
+ String minstr = ((Element) cur).getAttribute(ATTR_MIN);
+ if (minstr != null && minstr.length() > 0) {
+ min = Double.parseDouble(minstr);
+ }
+ String maxstr = ((Element) cur).getAttribute(ATTR_MAX);
+ if (maxstr != null && maxstr.length() > 0) {
+ max = Double.parseDouble(maxstr);
+ }
+
+ // *** new uniform generator
+ Random random = cluster.getNewRandomGenerator();
+ DistributionWithRandom generator = new HaltonUniformDistribution(min, max, random);
+ cluster.addGenerator(generator);
+
+ // TODO: check for unknown attributes.
+ XMLNodeIterator iter = new XMLNodeIterator(cur.getFirstChild());
+ while (iter.hasNext()) {
Node child = iter.next();
- if(child.getNodeType() == Node.ELEMENT_NODE) {
- logger.warning("Unknown element in XML specification file: " + child.getNodeName());
+ if (child.getNodeType() == Node.ELEMENT_NODE) {
+ LOG.warning("Unknown element in XML specification file: " + child.getNodeName());
}
}
}
@@ -433,25 +531,25 @@ public class GeneratorXMLDatabaseConnection implements DatabaseConnection {
int axis2 = 0;
double angle = 0.0;
- String a1str = ((Element) cur).getAttribute("axis1");
- if(a1str != null && a1str != "") {
- axis1 = Integer.valueOf(a1str);
+ String a1str = ((Element) cur).getAttribute(ATTR_AXIS1);
+ if (a1str != null && a1str.length() > 0) {
+ axis1 = Integer.parseInt(a1str);
}
- String a2str = ((Element) cur).getAttribute("axis2");
- if(a2str != null && a2str != "") {
- axis2 = Integer.valueOf(a2str);
+ String a2str = ((Element) cur).getAttribute(ATTR_AXIS2);
+ if (a2str != null && a2str.length() > 0) {
+ axis2 = Integer.parseInt(a2str);
}
- String anstr = ((Element) cur).getAttribute("angle");
- if(anstr != null && anstr != "") {
+ String anstr = ((Element) cur).getAttribute(ATTR_ANGLE);
+ if (anstr != null && anstr.length() > 0) {
angle = Double.parseDouble(anstr);
}
- if(axis1 <= 0 || axis1 > cluster.getDim()) {
+ if (axis1 <= 0 || axis1 > cluster.getDim()) {
throw new UnableToComplyException("Invalid axis1 number given in specification file.");
}
- if(axis1 <= 0 || axis1 > cluster.getDim()) {
+ if (axis1 <= 0 || axis1 > cluster.getDim()) {
throw new UnableToComplyException("Invalid axis2 number given in specification file.");
}
- if(axis1 == axis2) {
+ if (axis1 == axis2) {
throw new UnableToComplyException("Invalid axis numbers given in specification file.");
}
@@ -460,10 +558,10 @@ public class GeneratorXMLDatabaseConnection implements DatabaseConnection {
// TODO: check for unknown attributes.
XMLNodeIterator iter = new XMLNodeIterator(cur.getFirstChild());
- while(iter.hasNext()) {
+ while (iter.hasNext()) {
Node child = iter.next();
- if(child.getNodeType() == Node.ELEMENT_NODE) {
- logger.warning("Unknown element in XML specification file: " + child.getNodeName());
+ if (child.getNodeType() == Node.ELEMENT_NODE) {
+ LOG.warning("Unknown element in XML specification file: " + child.getNodeName());
}
}
}
@@ -477,11 +575,11 @@ public class GeneratorXMLDatabaseConnection implements DatabaseConnection {
*/
private void processElementTranslate(GeneratorSingleCluster cluster, Node cur) throws UnableToComplyException {
Vector offset = null;
- String vstr = ((Element) cur).getAttribute("vector");
- if(vstr != null && vstr != "") {
+ String vstr = ((Element) cur).getAttribute(ATTR_VECTOR);
+ if (vstr != null && vstr.length() > 0) {
offset = parseVector(vstr);
}
- if(offset == null) {
+ if (offset == null) {
throw new UnableToComplyException("No translation vector given.");
}
@@ -490,10 +588,10 @@ public class GeneratorXMLDatabaseConnection implements DatabaseConnection {
// TODO: check for unknown attributes.
XMLNodeIterator iter = new XMLNodeIterator(cur.getFirstChild());
- while(iter.hasNext()) {
+ while (iter.hasNext()) {
Node child = iter.next();
- if(child.getNodeType() == Node.ELEMENT_NODE) {
- logger.warning("Unknown element in XML specification file: " + child.getNodeName());
+ if (child.getNodeType() == Node.ELEMENT_NODE) {
+ LOG.warning("Unknown element in XML specification file: " + child.getNodeName());
}
}
}
@@ -509,15 +607,15 @@ public class GeneratorXMLDatabaseConnection implements DatabaseConnection {
Vector cmin = null;
Vector cmax = null;
- String minstr = ((Element) cur).getAttribute("min");
- if(minstr != null && minstr != "") {
+ String minstr = ((Element) cur).getAttribute(ATTR_MIN);
+ if (minstr != null && minstr.length() > 0) {
cmin = parseVector(minstr);
}
- String maxstr = ((Element) cur).getAttribute("max");
- if(maxstr != null && maxstr != "") {
+ String maxstr = ((Element) cur).getAttribute(ATTR_MAX);
+ if (maxstr != null && maxstr.length() > 0) {
cmax = parseVector(maxstr);
}
- if(cmin == null || cmax == null) {
+ if (cmin == null || cmax == null) {
throw new UnableToComplyException("No or incomplete clipping vectors given.");
}
@@ -526,10 +624,10 @@ public class GeneratorXMLDatabaseConnection implements DatabaseConnection {
// TODO: check for unknown attributes.
XMLNodeIterator iter = new XMLNodeIterator(cur.getFirstChild());
- while(iter.hasNext()) {
+ while (iter.hasNext()) {
Node child = iter.next();
- if(child.getNodeType() == Node.ELEMENT_NODE) {
- logger.warning("Unknown element in XML specification file: " + child.getNodeName());
+ if (child.getNodeType() == Node.ELEMENT_NODE) {
+ LOG.warning("Unknown element in XML specification file: " + child.getNodeName());
}
}
}
@@ -542,29 +640,28 @@ public class GeneratorXMLDatabaseConnection implements DatabaseConnection {
* @throws UnableToComplyException
*/
private void processElementStatic(GeneratorMain gen, Node cur) throws UnableToComplyException {
- String name = ((Element) cur).getAttribute("name");
- if(name == null) {
+ String name = ((Element) cur).getAttribute(ATTR_NAME);
+ if (name == null) {
throw new UnableToComplyException("No cluster name given in specification file.");
}
ArrayList<Vector> points = new ArrayList<Vector>();
// TODO: check for unknown attributes.
XMLNodeIterator iter = new XMLNodeIterator(cur.getFirstChild());
- while(iter.hasNext()) {
+ while (iter.hasNext()) {
Node child = iter.next();
- if(child.getNodeName() == "point") {
+ if (TAG_POINT.equals(child.getNodeName())) {
processElementPoint(points, child);
- }
- else if(child.getNodeType() == Node.ELEMENT_NODE) {
- logger.warning("Unknown element in XML specification file: " + child.getNodeName());
+ } else if (child.getNodeType() == Node.ELEMENT_NODE) {
+ LOG.warning("Unknown element in XML specification file: " + child.getNodeName());
}
}
// *** add new cluster object
GeneratorStatic cluster = new GeneratorStatic(name, points);
gen.addCluster(cluster);
- if(logger.isVerbose()) {
- logger.verbose("Loaded cluster " + cluster.name + " from specification.");
+ if (LOG.isVerbose()) {
+ LOG.verbose("Loaded cluster " + cluster.name + " from specification.");
}
}
@@ -577,11 +674,11 @@ public class GeneratorXMLDatabaseConnection implements DatabaseConnection {
*/
private void processElementPoint(List<Vector> points, Node cur) throws UnableToComplyException {
Vector point = null;
- String vstr = ((Element) cur).getAttribute("vector");
- if(vstr != null && vstr != "") {
+ String vstr = ((Element) cur).getAttribute(ATTR_VECTOR);
+ if (vstr != null && vstr.length() > 0) {
point = parseVector(vstr);
}
- if(point == null) {
+ if (point == null) {
throw new UnableToComplyException("No translation vector given.");
}
@@ -590,10 +687,10 @@ public class GeneratorXMLDatabaseConnection implements DatabaseConnection {
// TODO: check for unknown attributes.
XMLNodeIterator iter = new XMLNodeIterator(cur.getFirstChild());
- while(iter.hasNext()) {
+ while (iter.hasNext()) {
Node child = iter.next();
- if(child.getNodeType() == Node.ELEMENT_NODE) {
- logger.warning("Unknown element in XML specification file: " + child.getNodeName());
+ if (child.getNodeType() == Node.ELEMENT_NODE) {
+ LOG.warning("Unknown element in XML specification file: " + child.getNodeName());
}
}
}
@@ -610,11 +707,10 @@ public class GeneratorXMLDatabaseConnection implements DatabaseConnection {
private Vector parseVector(String s) throws UnableToComplyException {
String[] entries = WHITESPACE_PATTERN.split(s);
double[] d = new double[entries.length];
- for(int i = 0; i < entries.length; i++) {
+ for (int i = 0; i < entries.length; i++) {
try {
d[i] = Double.parseDouble(entries[i]);
- }
- catch(NumberFormatException e) {
+ } catch (NumberFormatException e) {
throw new UnableToComplyException("Could not parse vector.");
}
}
@@ -649,18 +745,19 @@ public class GeneratorXMLDatabaseConnection implements DatabaseConnection {
super.makeOptions(config);
// Specification file
final FileParameter cfgparam = new FileParameter(CONFIGFILE_ID, FileParameter.FileType.INPUT_FILE);
- if(config.grab(cfgparam)) {
+ if (config.grab(cfgparam)) {
specfile = cfgparam.getValue();
}
// Cluster size scaling
- final DoubleParameter scalepar = new DoubleParameter(SIZE_SCALE_ID, 1.0);
- if(config.grab(scalepar)) {
- sizescale = scalepar.getValue();
+ final DoubleParameter scalepar = new DoubleParameter(SIZE_SCALE_ID, Double.valueOf(1.0));
+ if (config.grab(scalepar)) {
+ sizescale = scalepar.getValue().doubleValue();
}
// Random generator
- final IntParameter seedpar = new IntParameter(RANDOMSEED_ID, true);
- if(config.grab(seedpar)) {
- clusterRandom = new Random(seedpar.getValue());
+ final RandomParameter rndP = new RandomParameter(RANDOMSEED_ID);
+ if (config.grab(rndP)) {
+ // TODO: use RandomFactory in cluster
+ clusterRandom = rndP.getValue().getRandom();
}
}
@@ -669,4 +766,4 @@ public class GeneratorXMLDatabaseConnection implements DatabaseConnection {
return new GeneratorXMLDatabaseConnection(specfile, sizescale, clusterRandom);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/InputStreamDatabaseConnection.java b/src/de/lmu/ifi/dbs/elki/datasource/InputStreamDatabaseConnection.java
index 9decd34b..109ef0cd 100644
--- a/src/de/lmu/ifi/dbs/elki/datasource/InputStreamDatabaseConnection.java
+++ b/src/de/lmu/ifi/dbs/elki/datasource/InputStreamDatabaseConnection.java
@@ -50,7 +50,7 @@ public class InputStreamDatabaseConnection extends AbstractDatabaseConnection {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(InputStreamDatabaseConnection.class);
+ private static final Logging LOG = Logging.getLogger(InputStreamDatabaseConnection.class);
/**
* Holds the instance of the parser.
@@ -76,16 +76,16 @@ public class InputStreamDatabaseConnection extends AbstractDatabaseConnection {
@Override
public MultipleObjectsBundle loadData() {
// Run parser
- if(logger.isDebugging()) {
- logger.debugFine("Invoking parsers.");
+ if(LOG.isDebugging()) {
+ LOG.debugFine("Invoking parsers.");
}
if(parser instanceof StreamingParser) {
final StreamingParser streamParser = (StreamingParser)parser;
streamParser.initStream(in);
// normalize objects and transform labels
- if(logger.isDebugging()) {
- logger.debugFine("Invoking filters.");
+ if(LOG.isDebugging()) {
+ LOG.debugFine("Invoking filters.");
}
MultipleObjectsBundle objects = MultipleObjectsBundle.fromStream(invokeFilters(streamParser));
return objects;
@@ -94,8 +94,8 @@ public class InputStreamDatabaseConnection extends AbstractDatabaseConnection {
MultipleObjectsBundle parsingResult = parser.parse(in);
// normalize objects and transform labels
- if(logger.isDebugging()) {
- logger.debugFine("Invoking filters.");
+ if(LOG.isDebugging()) {
+ LOG.debugFine("Invoking filters.");
}
MultipleObjectsBundle objects = invokeFilters(parsingResult);
return objects;
@@ -104,7 +104,7 @@ public class InputStreamDatabaseConnection extends AbstractDatabaseConnection {
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/LabelJoinDatabaseConnection.java b/src/de/lmu/ifi/dbs/elki/datasource/LabelJoinDatabaseConnection.java
index 7015bda9..a41be640 100644
--- a/src/de/lmu/ifi/dbs/elki/datasource/LabelJoinDatabaseConnection.java
+++ b/src/de/lmu/ifi/dbs/elki/datasource/LabelJoinDatabaseConnection.java
@@ -23,10 +23,11 @@ package de.lmu.ifi.dbs.elki.datasource;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+import gnu.trove.impl.Constants;
+import gnu.trove.map.hash.TObjectIntHashMap;
+
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
import de.lmu.ifi.dbs.elki.data.LabelList;
import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
@@ -51,7 +52,7 @@ public class LabelJoinDatabaseConnection extends AbstractDatabaseConnection impl
/**
* Logger
*/
- private static final Logging logger = Logging.getLogger(LabelJoinDatabaseConnection.class);
+ private static final Logging LOG = Logging.getLogger(LabelJoinDatabaseConnection.class);
/**
* The filters to invoke
@@ -72,118 +73,114 @@ public class LabelJoinDatabaseConnection extends AbstractDatabaseConnection impl
@Override
public MultipleObjectsBundle loadData() {
List<MultipleObjectsBundle> bundles = new ArrayList<MultipleObjectsBundle>(sources.size());
- for(DatabaseConnection dbc : sources) {
+ for (DatabaseConnection dbc : sources) {
bundles.add(dbc.loadData());
}
MultipleObjectsBundle first = bundles.get(0);
- Map<String, Integer> labelmap = new HashMap<String, Integer>(first.dataLength());
+ TObjectIntHashMap<String> labelmap = new TObjectIntHashMap<String>(first.dataLength(), Constants.DEFAULT_LOAD_FACTOR, -1);
// Process first bundle
{
// Identify a label column
final int lblcol;
{
int lblc = -1;
- for(int i = 0; i < first.metaLength(); i++) {
- if(TypeUtil.GUESSED_LABEL.isAssignableFromType(first.meta(i))) {
+ for (int i = 0; i < first.metaLength(); i++) {
+ if (TypeUtil.GUESSED_LABEL.isAssignableFromType(first.meta(i))) {
lblc = i;
break;
}
}
lblcol = lblc; // make static
}
- if(lblcol == -1) {
+ if (lblcol == -1) {
throw new AbortException("No label column found in first source, cannot join (do you want to use " + ExternalIDJoinDatabaseConnection.class.getSimpleName() + " instead?)");
}
- for(int i = 0; i < first.dataLength(); i++) {
+ for (int i = 0; i < first.dataLength(); i++) {
Object data = first.data(i, lblcol);
- if(data == null) {
- logger.warning("Object without label encountered.");
+ if (data == null) {
+ LOG.warning("Object without label encountered.");
continue;
}
- if(data instanceof String) {
- Integer old = labelmap.put((String) data, i);
- if(old != null) {
- logger.warning("Duplicate label encountered: " + data + " in rows " + old + " and " + i);
+ if (data instanceof String) {
+ int old = labelmap.put((String) data, i);
+ if (old != -1) {
+ LOG.warning("Duplicate label encountered: " + data + " in rows " + old + " and " + i);
}
- }
- else if(data instanceof LabelList) {
- for(String lbl : (LabelList) data) {
- Integer old = labelmap.put(lbl, i);
- if(old != null) {
- logger.warning("Duplicate label encountered: " + lbl + " in rows " + old + " and " + i);
+ } else if (data instanceof LabelList) {
+ for (String lbl : (LabelList) data) {
+ int old = labelmap.put(lbl, i);
+ if (old != -1) {
+ LOG.warning("Duplicate label encountered: " + lbl + " in rows " + old + " and " + i);
}
}
- }
- else {
+ } else {
String lbl = data.toString();
- Integer old = labelmap.put(lbl, i);
- if(old != null) {
- logger.warning("Duplicate label encountered: " + lbl + " in rows " + old + " and " + i);
+ int old = labelmap.put(lbl, i);
+ if (old != -1) {
+ LOG.warning("Duplicate label encountered: " + lbl + " in rows " + old + " and " + i);
}
}
}
}
// Process additional columns
- for(int c = 1; c < sources.size(); c++) {
+ for (int c = 1; c < sources.size(); c++) {
MultipleObjectsBundle cur = bundles.get(c);
final int lblcol;
{
int lblc = -1;
- for(int i = 0; i < cur.metaLength(); i++) {
- if(TypeUtil.GUESSED_LABEL.isAssignableFromType(cur.meta(i))) {
+ for (int i = 0; i < cur.metaLength(); i++) {
+ if (TypeUtil.GUESSED_LABEL.isAssignableFromType(cur.meta(i))) {
lblc = i;
break;
}
}
lblcol = lblc; // make static
}
- if(lblcol == -1) {
+ if (lblcol == -1) {
throw new AbortException("No label column found in source " + (c + 1) + ", cannot join (do you want to use " + ExternalIDJoinDatabaseConnection.class.getSimpleName() + " instead?)");
}
// Destination columns
List<ArrayList<Object>> dcol = new ArrayList<ArrayList<Object>>(cur.metaLength());
- for(int i = 0; i < cur.metaLength(); i++) {
+ for (int i = 0; i < cur.metaLength(); i++) {
// Skip the label columns
- if(i == lblcol) {
+ if (i == lblcol) {
dcol.add(null);
continue;
}
ArrayList<Object> newcol = new ArrayList<Object>(first.dataLength());
// Pre-fill with nulls.
- for(int j = 0; j < first.dataLength(); j++) {
+ for (int j = 0; j < first.dataLength(); j++) {
newcol.add(null);
}
first.appendColumn(cur.meta(i), newcol);
dcol.add(newcol);
}
- for(int i = 0; i < cur.dataLength(); i++) {
+ for (int i = 0; i < cur.dataLength(); i++) {
Object data = cur.data(i, lblcol);
- if(data == null) {
- logger.warning("Object without label encountered.");
+ if (data == null) {
+ LOG.warning("Object without label encountered.");
continue;
}
- Integer row = null;
- if(data instanceof String) {
+ int row = -1;
+ if (data instanceof String) {
row = labelmap.get(data);
- }
- else if(data instanceof LabelList) {
- for(String lbl : (LabelList) data) {
+ } else if (data instanceof LabelList) {
+ for (String lbl : (LabelList) data) {
row = labelmap.get(lbl);
- if(row != null) {
+ if (row >= 0) {
break;
}
}
- }
- else {
+ } else {
row = labelmap.get(data.toString());
}
- if(row == null) {
- logger.warning("Label not found for join: " + data + " in row " + i);
+ if (row < 0) {
+ LOG.warning("Label not found for join: " + data + " in row " + i);
continue;
}
- for(int d = 0; d < cur.metaLength(); d++) {
- if(d == lblcol) {
+ for (int d = 0; d < cur.metaLength(); d++) {
+ if (d == lblcol) {
continue;
}
List<Object> col = dcol.get(d);
@@ -192,22 +189,21 @@ public class LabelJoinDatabaseConnection extends AbstractDatabaseConnection impl
}
}
}
- for(int i = 0; i < first.dataLength(); i++) {
- for(int d = 0; d < first.metaLength(); d++) {
- if(first.data(i, d) == null) {
- StringBuffer buf = new StringBuffer();
- for(int d2 = 0; d2 < first.metaLength(); d2++) {
- if(buf.length() > 0) {
+ for (int i = 0; i < first.dataLength(); i++) {
+ for (int d = 0; d < first.metaLength(); d++) {
+ if (first.data(i, d) == null) {
+ StringBuilder buf = new StringBuilder();
+ for (int d2 = 0; d2 < first.metaLength(); d2++) {
+ if (buf.length() > 0) {
buf.append(", ");
}
- if(first.data(i, d2) == null) {
+ if (first.data(i, d2) == null) {
buf.append("null");
- }
- else {
+ } else {
buf.append(first.data(i, d2));
}
}
- logger.warning("null value in joined data, row " + i + " column " + d + FormatUtil.NEWLINE + "[" + buf.toString() + "]");
+ LOG.warning("null value in joined data, row " + i + " column " + d + FormatUtil.NEWLINE + "[" + buf.toString() + "]");
break;
}
}
@@ -218,7 +214,7 @@ public class LabelJoinDatabaseConnection extends AbstractDatabaseConnection impl
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -232,7 +228,7 @@ public class LabelJoinDatabaseConnection extends AbstractDatabaseConnection impl
/**
* The static option ID
*/
- public static final OptionID SOURCES_ID = OptionID.getOrCreateOptionID("join.sources", "The data sources to join.");
+ public static final OptionID SOURCES_ID = new OptionID("join.sources", "The data sources to join.");
/**
* The data souces to use.
@@ -244,7 +240,7 @@ public class LabelJoinDatabaseConnection extends AbstractDatabaseConnection impl
super.makeOptions(config);
super.configFilters(config);
final ObjectListParameter<DatabaseConnection> sourcesParam = new ObjectListParameter<DatabaseConnection>(SOURCES_ID, DatabaseConnection.class);
- if(config.grab(sourcesParam)) {
+ if (config.grab(sourcesParam)) {
sources = sourcesParam.instantiateClasses(config);
}
}
@@ -254,4 +250,4 @@ public class LabelJoinDatabaseConnection extends AbstractDatabaseConnection impl
return new LabelJoinDatabaseConnection(filters, sources);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/PresortedBlindJoinDatabaseConnection.java b/src/de/lmu/ifi/dbs/elki/datasource/PresortedBlindJoinDatabaseConnection.java
index b5b59e40..75c48731 100644
--- a/src/de/lmu/ifi/dbs/elki/datasource/PresortedBlindJoinDatabaseConnection.java
+++ b/src/de/lmu/ifi/dbs/elki/datasource/PresortedBlindJoinDatabaseConnection.java
@@ -47,7 +47,7 @@ public class PresortedBlindJoinDatabaseConnection extends AbstractDatabaseConnec
/**
* Logger
*/
- private static final Logging logger = Logging.getLogger(PresortedBlindJoinDatabaseConnection.class);
+ private static final Logging LOG = Logging.getLogger(PresortedBlindJoinDatabaseConnection.class);
/**
* The filters to invoke
@@ -89,7 +89,7 @@ public class PresortedBlindJoinDatabaseConnection extends AbstractDatabaseConnec
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -103,7 +103,7 @@ public class PresortedBlindJoinDatabaseConnection extends AbstractDatabaseConnec
/**
* The static option ID
*/
- public static final OptionID SOURCES_ID = OptionID.getOrCreateOptionID("join.sources", "The data sources to join.");
+ public static final OptionID SOURCES_ID = new OptionID("join.sources", "The data sources to join.");
/**
* The data souces to use.
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/RandomDoubleVectorDatabaseConnection.java b/src/de/lmu/ifi/dbs/elki/datasource/RandomDoubleVectorDatabaseConnection.java
index 4cd626ee..bd157315 100644
--- a/src/de/lmu/ifi/dbs/elki/datasource/RandomDoubleVectorDatabaseConnection.java
+++ b/src/de/lmu/ifi/dbs/elki/datasource/RandomDoubleVectorDatabaseConnection.java
@@ -33,67 +33,64 @@ import de.lmu.ifi.dbs.elki.data.type.VectorFieldTypeInformation;
import de.lmu.ifi.dbs.elki.datasource.bundle.MultipleObjectsBundle;
import de.lmu.ifi.dbs.elki.datasource.filter.ObjectFilter;
import de.lmu.ifi.dbs.elki.logging.Logging;
+import de.lmu.ifi.dbs.elki.utilities.RandomFactory;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.LongParameter;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.RandomParameter;
/**
- * Produce a database of random double vectors with each dimension in [0:1]
+ * Produce a database of random double vectors with each dimension in [0:1].
*
* @author Erich Schubert
*/
public class RandomDoubleVectorDatabaseConnection extends AbstractDatabaseConnection {
/**
- * Dimensionality
+ * Class logger.
+ */
+ private static final Logging LOG = Logging.getLogger(RandomDoubleVectorDatabaseConnection.class);
+
+ /**
+ * Dimensionality.
*/
protected int dim = -1;
/**
- * Size of database to generate
+ * Size of database to generate.
*/
protected int size = -1;
/**
- * Seed to use
+ * Random generator
*/
- protected Long seed;
+ protected RandomFactory rnd;
/**
* Constructor.
*
* @param dim Dimensionality
* @param size Database size
- * @param seed Random seed
- * @param filters
+ * @param rnd Random generator
+ * @param filters Filters to use
*/
- public RandomDoubleVectorDatabaseConnection(int dim, int size, Long seed, List<ObjectFilter> filters) {
+ public RandomDoubleVectorDatabaseConnection(int dim, int size, RandomFactory rnd, List<ObjectFilter> filters) {
super(filters);
this.dim = dim;
this.size = size;
- this.seed = seed;
+ this.rnd = rnd;
}
- private static final Logging logger = Logging.getLogger(RandomDoubleVectorDatabaseConnection.class);
-
@Override
public MultipleObjectsBundle loadData() {
- VectorFieldTypeInformation<DoubleVector> type = new VectorFieldTypeInformation<DoubleVector>(DoubleVector.class, dim, DoubleVector.STATIC);
+ VectorFieldTypeInformation<DoubleVector> type = new VectorFieldTypeInformation<DoubleVector>(DoubleVector.FACTORY, dim);
List<DoubleVector> vectors = new ArrayList<DoubleVector>(size);
// Setup random generator
- final Random rand;
- if(seed == null) {
- rand = new Random();
- }
- else {
- rand = new Random(seed);
- }
+ final Random rand = rnd.getRandom();
// Produce random vectors
- DoubleVector factory = new DoubleVector(new double[dim]);
for(int i = 0; i < size; i++) {
- vectors.add(VectorUtil.randomVector(factory, rand));
+ vectors.add(VectorUtil.randomVector(DoubleVector.FACTORY, dim, rand));
}
return MultipleObjectsBundle.makeSimple(type, vectors);
@@ -101,7 +98,7 @@ public class RandomDoubleVectorDatabaseConnection extends AbstractDatabaseConnec
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -118,15 +115,15 @@ public class RandomDoubleVectorDatabaseConnection extends AbstractDatabaseConnec
* Key: {@code -dbc.seed}
* </p>
*/
- public static final OptionID SEED_ID = OptionID.getOrCreateOptionID("dbc.genseed", "Seed for randomly generating vectors");
+ public static final OptionID SEED_ID = new OptionID("dbc.genseed", "Seed for randomly generating vectors");
/**
- * Database to specify the random vector dimensionality
+ * Database to specify the random vector dimensionality.
* <p>
* Key: {@code -dbc.dim}
* </p>
*/
- public static final OptionID DIM_ID = OptionID.getOrCreateOptionID("dbc.dim", "Dimensionality of the vectors to generate.");
+ public static final OptionID DIM_ID = new OptionID("dbc.dim", "Dimensionality of the vectors to generate.");
/**
* Parameter to specify the database size to generate.
@@ -134,47 +131,44 @@ public class RandomDoubleVectorDatabaseConnection extends AbstractDatabaseConnec
* Key: {@code -dbc.size}
* </p>
*/
- public static final OptionID SIZE_ID = OptionID.getOrCreateOptionID("dbc.size", "Database size to generate.");
+ public static final OptionID SIZE_ID = new OptionID("dbc.size", "Database size to generate.");
+ /**
+ * Dimensionality.
+ */
int dim = -1;
+ /**
+ * Database size.
+ */
int size = -1;
- Long seed = null;
+ /**
+ * Random generator.
+ */
+ RandomFactory rnd;
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
configFilters(config);
- configDimensionality(config);
- configSize(config);
- configSeed(config);
- }
-
- protected void configSeed(Parameterization config) {
- LongParameter seedParam = new LongParameter(SEED_ID, true);
- if(config.grab(seedParam)) {
- seed = seedParam.getValue();
- }
- }
-
- protected void configDimensionality(Parameterization config) {
IntParameter dimParam = new IntParameter(DIM_ID);
if(config.grab(dimParam)) {
- dim = dimParam.getValue();
+ dim = dimParam.getValue().intValue();
}
- }
-
- protected void configSize(Parameterization config) {
IntParameter sizeParam = new IntParameter(SIZE_ID);
if(config.grab(sizeParam)) {
- size = sizeParam.getValue();
+ size = sizeParam.getValue().intValue();
+ }
+ RandomParameter rndP = new RandomParameter(SEED_ID);
+ if(config.grab(rndP)) {
+ rnd = rndP.getValue();
}
}
@Override
protected RandomDoubleVectorDatabaseConnection makeInstance() {
- return new RandomDoubleVectorDatabaseConnection(dim, size, seed, filters);
+ return new RandomDoubleVectorDatabaseConnection(dim, size, rnd, filters);
}
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/bundle/BundleReader.java b/src/de/lmu/ifi/dbs/elki/datasource/bundle/BundleReader.java
new file mode 100644
index 00000000..a2a3fb7e
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/datasource/bundle/BundleReader.java
@@ -0,0 +1,168 @@
+package de.lmu.ifi.dbs.elki.datasource.bundle;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+import java.io.IOException;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.FileChannel;
+import java.nio.channels.FileChannel.MapMode;
+import java.util.ArrayList;
+
+import de.lmu.ifi.dbs.elki.data.type.SimpleTypeInformation;
+import de.lmu.ifi.dbs.elki.data.type.TypeInformationSerializer;
+import de.lmu.ifi.dbs.elki.persistent.ByteArrayUtil;
+import de.lmu.ifi.dbs.elki.persistent.ByteBufferSerializer;
+import de.lmu.ifi.dbs.elki.utilities.exceptions.AbortException;
+
+/**
+ * Read an ELKI bundle file into a data stream.
+ *
+ * TODO: resize buffer when necessary?
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.uses FileChannel
+ */
+public class BundleReader implements BundleStreamSource {
+ /**
+ * Magic number, shared with {@link BundleReader}.
+ */
+ public static final int MAGIC = BundleWriter.MAGIC;
+
+ /**
+ * The stream buffer.
+ */
+ MappedByteBuffer buffer;
+
+ /**
+ * Bundle metadata.
+ */
+ BundleMeta meta = null;
+
+ /**
+ * Input channel.
+ */
+ FileChannel input;
+
+ /**
+ * Serializers to use.
+ */
+ ArrayList<ByteBufferSerializer<?>> sers;
+
+ /**
+ * Current object.
+ */
+ ArrayList<Object> data;
+
+ /**
+ * Constructor.
+ *
+ * @param input Input channel
+ */
+ public BundleReader(FileChannel input) {
+ super();
+ this.input = input;
+ }
+
+ @Override
+ public BundleMeta getMeta() {
+ if (meta == null) {
+ openBuffer();
+ readMeta();
+ }
+ return meta;
+ }
+
+ /**
+ * Map the input file.
+ */
+ void openBuffer() {
+ try {
+ buffer = input.map(MapMode.READ_ONLY, 0, input.size());
+ } catch (IOException e) {
+ throw new AbortException("Cannot map input bundle.", e);
+ }
+ }
+
+ /**
+ * Read the metadata.
+ */
+ void readMeta() {
+ final int check = buffer.getInt();
+ if (check != MAGIC) {
+ throw new AbortException("File does not start with expected magic.");
+ }
+ final int nummeta = buffer.getInt();
+ assert (nummeta > 0);
+ meta = new BundleMeta(nummeta);
+ sers = new ArrayList<ByteBufferSerializer<?>>(nummeta);
+ data = new ArrayList<Object>(nummeta);
+ for (int i = 0; i < nummeta; i++) {
+ try {
+ @SuppressWarnings("unchecked")
+ SimpleTypeInformation<? extends Object> type = (SimpleTypeInformation<? extends Object>) TypeInformationSerializer.STATIC.fromByteBuffer(buffer);
+ meta.add(type);
+ sers.add(type.getSerializer());
+ } catch (UnsupportedOperationException e) {
+ throw new AbortException("Deserialization failed: "+e.getMessage(), e);
+ } catch (IOException e) {
+ throw new AbortException("IO error", e);
+ }
+ }
+ }
+
+ /**
+ * Read an object.
+ */
+ void readObject() {
+ data.clear();
+ for (ByteBufferSerializer<?> ser : sers) {
+ try {
+ data.add(ser.fromByteBuffer(buffer));
+ } catch (UnsupportedOperationException e) {
+ throw new AbortException("Deserialization failed.", e);
+ } catch (IOException e) {
+ throw new AbortException("IO error", e);
+ }
+ }
+ }
+
+ @Override
+ public Event nextEvent() {
+ // Send initial meta
+ if (meta == null) {
+ return Event.META_CHANGED;
+ }
+ if (buffer.remaining() == 0) {
+ ByteArrayUtil.unmapByteBuffer(buffer);
+ return Event.END_OF_STREAM;
+ }
+ readObject();
+ return Event.NEXT_OBJECT;
+ }
+
+ @Override
+ public Object data(int rnum) {
+ return data.get(rnum);
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/bundle/BundleWriter.java b/src/de/lmu/ifi/dbs/elki/datasource/bundle/BundleWriter.java
new file mode 100644
index 00000000..7efbd592
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/datasource/bundle/BundleWriter.java
@@ -0,0 +1,169 @@
+package de.lmu.ifi.dbs.elki.datasource.bundle;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.WritableByteChannel;
+
+import de.lmu.ifi.dbs.elki.data.type.SimpleTypeInformation;
+import de.lmu.ifi.dbs.elki.data.type.TypeInformationSerializer;
+import de.lmu.ifi.dbs.elki.logging.Logging;
+import de.lmu.ifi.dbs.elki.persistent.ByteBufferSerializer;
+import de.lmu.ifi.dbs.elki.utilities.exceptions.AbortException;
+
+/**
+ * Write an object bundle stream to a file channel.
+ *
+ * Bundle streams that add new columns are not supported.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.uses BundleStreamSource
+ * @apiviz.uses WritableByteChannel
+ */
+public class BundleWriter {
+ /**
+ * Class logger for the bundle writer.
+ */
+ private static final Logging LOG = Logging.getLogger(BundleWriter.class);
+
+ /**
+ * Initial buffer size.
+ */
+ private static final int INITIAL_BUFFER = 4096;
+
+ /**
+ * Random magic number.
+ */
+ public static final int MAGIC = 0xa8123b12;
+
+ /**
+ * Write a bundle stream to a file output channel.
+ *
+ * @param source Data source
+ * @param output Output channel
+ * @throws IOException on IO errors
+ */
+ public void writeBundleStream(BundleStreamSource source, WritableByteChannel output) throws IOException {
+ ByteBuffer buffer = ByteBuffer.allocateDirect(INITIAL_BUFFER);
+
+ ByteBufferSerializer<Object>[] serializers = null;
+ loop: while (true) {
+ BundleStreamSource.Event ev = source.nextEvent();
+ switch(ev) {
+ case NEXT_OBJECT:
+ if (serializers == null) {
+ serializers = writeHeader(source, buffer, output);
+ }
+ for (int i = 0; i < serializers.length; i++) {
+ int size = serializers[i].getByteSize(source.data(i));
+ buffer = ensureBuffer(size, buffer, output);
+ serializers[i].toByteBuffer(buffer, source.data(i));
+ }
+ break; // switch
+ case META_CHANGED:
+ if (serializers != null) {
+ throw new AbortException("Meta changes are not supported, once the block header has been written.");
+ }
+ break; // switch
+ case END_OF_STREAM:
+ break loop;
+ default:
+ LOG.warning("Unknown bundle stream event. API inconsistent? " + ev);
+ break; // switch
+ }
+ }
+ if (buffer.position() > 0) {
+ flushBuffer(buffer, output);
+ }
+ }
+
+ /**
+ * Flush the current write buffer to disk.
+ *
+ * @param buffer Buffer to write
+ * @param output Output channel
+ * @throws IOException on IO errors
+ */
+ private void flushBuffer(ByteBuffer buffer, WritableByteChannel output) throws IOException {
+ buffer.flip();
+ output.write(buffer);
+ buffer.flip();
+ buffer.limit(buffer.capacity());
+ }
+
+ /**
+ * Ensure the buffer is large enough.
+ *
+ * @param size Required size to add
+ * @param buffer Existing buffer
+ * @param output Output channel
+ * @return Buffer, eventually resized
+ * @throws IOException on IO errors
+ */
+ private ByteBuffer ensureBuffer(int size, ByteBuffer buffer, WritableByteChannel output) throws IOException {
+ if (buffer.remaining() >= size) {
+ return buffer;
+ }
+ flushBuffer(buffer, output);
+ if (buffer.remaining() >= size) {
+ return buffer;
+ }
+ // Aggressively grow the buffer
+ return ByteBuffer.allocateDirect(Math.max(buffer.capacity() << 1, buffer.capacity() + size));
+ }
+
+ /**
+ * Write the header for the given stream to the stream.
+ *
+ * @param source Bundle stream
+ * @param buffer Buffer to use for writing
+ * @param output Output channel
+ * @return Array of serializers
+ * @throws IOException on IO errors
+ */
+ @SuppressWarnings("unchecked")
+ private ByteBufferSerializer<Object>[] writeHeader(BundleStreamSource source, ByteBuffer buffer, WritableByteChannel output) throws IOException {
+ final BundleMeta meta = source.getMeta();
+ final int nummeta = meta.size();
+ @SuppressWarnings("rawtypes")
+ final ByteBufferSerializer[] serializers = new ByteBufferSerializer[nummeta];
+ // Write our magic ID first.
+ assert (buffer.position() == 0) : "Buffer is supposed to be at 0.";
+ buffer.putInt(MAGIC);
+ // Write the number of metas next
+ buffer.putInt(nummeta);
+ for (int i = 0; i < nummeta; i++) {
+ SimpleTypeInformation<?> type = meta.get(i);
+ ByteBufferSerializer<Object> ser = (ByteBufferSerializer<Object>) type.getSerializer();
+ if (ser == null) {
+ throw new AbortException("Cannot serialize - no serializer found for type: " + type.toString());
+ }
+ TypeInformationSerializer.STATIC.toByteBuffer(buffer, type);
+ serializers[i] = ser;
+ }
+ return serializers;
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/bundle/MultipleObjectsBundle.java b/src/de/lmu/ifi/dbs/elki/datasource/bundle/MultipleObjectsBundle.java
index 51107098..c3e99a83 100644
--- a/src/de/lmu/ifi/dbs/elki/datasource/bundle/MultipleObjectsBundle.java
+++ b/src/de/lmu/ifi/dbs/elki/datasource/bundle/MultipleObjectsBundle.java
@@ -27,7 +27,6 @@ import java.util.ArrayList;
import java.util.List;
import de.lmu.ifi.dbs.elki.data.type.SimpleTypeInformation;
-import de.lmu.ifi.dbs.elki.datasource.bundle.BundleStreamSource.Event;
import de.lmu.ifi.dbs.elki.logging.LoggingUtil;
import de.lmu.ifi.dbs.elki.utilities.exceptions.AbortException;
@@ -37,6 +36,8 @@ import de.lmu.ifi.dbs.elki.utilities.exceptions.AbortException;
* with multiple representations outside of any index structure.
*
* @author Erich Schubert
+ *
+ * @apiviz.landmark
*/
public class MultipleObjectsBundle implements ObjectBundle {
/**
@@ -218,7 +219,7 @@ public class MultipleObjectsBundle implements ObjectBundle {
MultipleObjectsBundle bundle = new MultipleObjectsBundle();
boolean stop = false;
while(!stop) {
- Event ev = source.nextEvent();
+ BundleStreamSource.Event ev = source.nextEvent();
switch(ev) {
case END_OF_STREAM:
stop = true;
@@ -263,4 +264,4 @@ public class MultipleObjectsBundle implements ObjectBundle {
}
return ret;
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/bundle/package-info.java b/src/de/lmu/ifi/dbs/elki/datasource/bundle/package-info.java
index 4be6fa79..0b2cbad9 100644
--- a/src/de/lmu/ifi/dbs/elki/datasource/bundle/package-info.java
+++ b/src/de/lmu/ifi/dbs/elki/datasource/bundle/package-info.java
@@ -1,5 +1,7 @@
/**
* <p>Object bundles - exchange container for multi-represented objects.</p>
+ *
+ * @apiviz.exclude java.util.*
*/
/*
This file is part of ELKI:
@@ -23,4 +25,4 @@ GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package de.lmu.ifi.dbs.elki.datasource.bundle; \ No newline at end of file
+package de.lmu.ifi.dbs.elki.datasource.bundle;
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/filter/AbstractConversionFilter.java b/src/de/lmu/ifi/dbs/elki/datasource/filter/AbstractConversionFilter.java
index 34fb6bad..5948cd83 100644
--- a/src/de/lmu/ifi/dbs/elki/datasource/filter/AbstractConversionFilter.java
+++ b/src/de/lmu/ifi/dbs/elki/datasource/filter/AbstractConversionFilter.java
@@ -45,6 +45,9 @@ public abstract class AbstractConversionFilter<I, O> implements ObjectFilter {
*
* In the main pass, each object is then filtered using
* {@link #filterSingleObject}.
+ *
+ * @param objects Objects to filter
+ * @return Filtered bundle
*/
@Override
public MultipleObjectsBundle filter(MultipleObjectsBundle objects) {
@@ -100,14 +103,14 @@ public abstract class AbstractConversionFilter<I, O> implements ObjectFilter {
* @param obj Database object to normalize
* @return Normalized database object
*/
- abstract protected O filterSingleObject(I obj);
+ protected abstract O filterSingleObject(I obj);
/**
* Get the input type restriction used for negotiating the data query.
*
* @return Type restriction
*/
- abstract protected SimpleTypeInformation<? super I> getInputTypeRestriction();
+ protected abstract SimpleTypeInformation<? super I> getInputTypeRestriction();
/**
* Get the output type from the input type after conversion.
@@ -115,10 +118,10 @@ public abstract class AbstractConversionFilter<I, O> implements ObjectFilter {
* @param in input type restriction
* @return output type restriction
*/
- abstract protected SimpleTypeInformation<? super O> convertedType(SimpleTypeInformation<I> in);
+ protected abstract SimpleTypeInformation<? super O> convertedType(SimpleTypeInformation<I> in);
/**
- * Return "true" when the normalization needs initialization (two-pass filtering!)
+ * Return "true" when the normalization needs initialization (two-pass filtering!).
*
* @param in Input type information
* @return true or false
@@ -137,7 +140,7 @@ public abstract class AbstractConversionFilter<I, O> implements ObjectFilter {
}
/**
- * Complete the initialization phase
+ * Complete the initialization phase.
*/
protected void prepareComplete() {
// optional - default NOOP.
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/filter/AbstractRandomFeatureSelectionFilter.java b/src/de/lmu/ifi/dbs/elki/datasource/filter/AbstractRandomFeatureSelectionFilter.java
deleted file mode 100644
index 6c16abfc..00000000
--- a/src/de/lmu/ifi/dbs/elki/datasource/filter/AbstractRandomFeatureSelectionFilter.java
+++ /dev/null
@@ -1,158 +0,0 @@
-package de.lmu.ifi.dbs.elki.datasource.filter;
-
-/*
- This file is part of ELKI:
- Environment for Developing KDD-Applications Supported by Index-Structures
-
- Copyright (C) 2012
- Ludwig-Maximilians-Universität München
- Lehr- und Forschungseinheit für Datenbanksysteme
- ELKI Development Team
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-import java.util.BitSet;
-import java.util.Random;
-
-import de.lmu.ifi.dbs.elki.data.FeatureVector;
-import de.lmu.ifi.dbs.elki.data.NumberVector;
-import de.lmu.ifi.dbs.elki.data.type.SimpleTypeInformation;
-import de.lmu.ifi.dbs.elki.data.type.VectorFieldTypeInformation;
-import de.lmu.ifi.dbs.elki.utilities.Util;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterEqualConstraint;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.LongParameter;
-
-/**
- * <p>
- * A RandomProjectionParser selects a subset of attributes randomly for
- * projection of a ParsingResult.
- * </p>
- *
- * The cardinality of the subset of attributes is specified as a parameter.
- *
- * @author Arthur Zimek
- * @author Erich Schubert
- *
- * @param <V> the type of FeatureVector contained in both the original data of
- * the base parser and the projected data of this ProjectionParser
- */
-public abstract class AbstractRandomFeatureSelectionFilter<V extends FeatureVector<?, ?>> extends AbstractStreamConversionFilter<V, V> {
- /**
- * The selected attributes
- */
- protected BitSet selectedAttributes = null;
-
- /**
- * Parameter for the desired cardinality of the subset of attributes selected
- * for projection.
- *
- * <p>
- * Key: <code>-randomprojection.numberselected</code>
- * </p>
- * <p>
- * Default: <code>1</code>
- * </p>
- * <p>
- * Constraint: &ge;1
- * </p>
- */
- public static final OptionID NUMBER_SELECTED_ATTRIBUTES_ID = OptionID.getOrCreateOptionID("randomprojection.numberselected", "number of selected attributes");
-
- /**
- * Optional parameter to specify a seed for random projection.
- * If unused, system time is used as seed.
- * <p>
- * Key: {@code -randomprojection.seed}
- * </p>
- */
- public static final OptionID SEED_ID = OptionID.getOrCreateOptionID("randomprojection.seed", "Seed for random selection of projection attributes.");
-
-
- /**
- * Holds the desired cardinality of the subset of attributes selected for
- * projection.
- */
- protected int k;
-
- /**
- * Holds a random object.
- */
- protected final Random random;
-
- /**
- * Constructor.
- *
- * @param dim dimensionality
- */
- public AbstractRandomFeatureSelectionFilter(int dim) {
- super();
- this.k = dim;
- this.random = new Random();
- }
-
- /**
- * Constructor.
- *
- * @param dim dimensionality
- * @param seed seed for random
- */
- public AbstractRandomFeatureSelectionFilter(int dim, long seed) {
- super();
- this.k = dim;
- this.random = new Random(seed);
- }
-
- /**
- * Initialize random attributes.
- *
- * Invoke this from {@link #convertedType}!
- *
- * @param in Type information.
- */
- void initializeRandomAttributes(SimpleTypeInformation<V> in) {
- int d = ((VectorFieldTypeInformation<V>) in).dimensionality();
- selectedAttributes = Util.randomBitSet(k, d, random);
- }
-
- /**
- * Parameterization class.
- *
- * @author Erich Schubert
- *
- * @apiviz.exclude
- */
- public static abstract class Parameterizer<V extends NumberVector<V, ?>> extends AbstractParameterizer {
- protected int k = 0;
-
- protected long seed = System.currentTimeMillis();
-
- @Override
- protected void makeOptions(Parameterization config) {
- super.makeOptions(config);
- IntParameter kP = new IntParameter(NUMBER_SELECTED_ATTRIBUTES_ID, new GreaterEqualConstraint(1), 1);
- if(config.grab(kP)) {
- k = kP.getValue();
- }
- LongParameter seedP = new LongParameter(SEED_ID, true);
- if(config.grab(seedP)) {
- seed = seedP.getValue();
- }
- }
- }
-} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/filter/AbstractStreamConversionFilter.java b/src/de/lmu/ifi/dbs/elki/datasource/filter/AbstractStreamConversionFilter.java
index 1c8acb72..9b628f2d 100644
--- a/src/de/lmu/ifi/dbs/elki/datasource/filter/AbstractStreamConversionFilter.java
+++ b/src/de/lmu/ifi/dbs/elki/datasource/filter/AbstractStreamConversionFilter.java
@@ -37,15 +37,15 @@ import de.lmu.ifi.dbs.elki.datasource.bundle.BundleMeta;
*/
public abstract class AbstractStreamConversionFilter<I, O> extends AbstractStreamFilter {
/**
- * The filtered meta
+ * The filtered meta.
*/
BundleMeta meta;
/**
- * The column to filter
+ * The column to filter.
*/
int column = -1;
-
+
@Override
public BundleMeta getMeta() {
return meta;
@@ -98,14 +98,14 @@ public abstract class AbstractStreamConversionFilter<I, O> extends AbstractStrea
* @param obj Database object to normalize
* @return Normalized database object
*/
- abstract protected O filterSingleObject(I obj);
+ protected abstract O filterSingleObject(I obj);
/**
* Get the input type restriction used for negotiating the data query.
*
* @return Type restriction
*/
- abstract protected SimpleTypeInformation<? super I> getInputTypeRestriction();
+ protected abstract SimpleTypeInformation<? super I> getInputTypeRestriction();
/**
* Get the output type from the input type after conversion.
@@ -113,5 +113,5 @@ public abstract class AbstractStreamConversionFilter<I, O> extends AbstractStrea
* @param in input type restriction
* @return output type restriction
*/
- abstract protected SimpleTypeInformation<? super O> convertedType(SimpleTypeInformation<I> in);
+ protected abstract SimpleTypeInformation<? super O> convertedType(SimpleTypeInformation<I> in);
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/filter/AbstractVectorConversionFilter.java b/src/de/lmu/ifi/dbs/elki/datasource/filter/AbstractVectorConversionFilter.java
new file mode 100644
index 00000000..66d10967
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/datasource/filter/AbstractVectorConversionFilter.java
@@ -0,0 +1,51 @@
+package de.lmu.ifi.dbs.elki.datasource.filter;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.data.type.SimpleTypeInformation;
+
+/**
+ * Abstract class for filters that produce number vectors.
+ *
+ * @author Erich Schubert
+ *
+ * @param <I> Input vector type
+ * @param <O> Output vector type
+ */
+public abstract class AbstractVectorConversionFilter<I, O extends NumberVector<?>> extends AbstractConversionFilter<I, O> {
+ /**
+ * Number vector factory.
+ */
+ protected NumberVector.Factory<O, ?> factory;
+
+ /**
+ * Initialize factory from a data type.
+ *
+ * @param type Output data type information.
+ */
+ protected void initializeOutputType(SimpleTypeInformation<O> type) {
+ factory = FilterUtil.guessFactory(type);
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/filter/AbstractVectorStreamConversionFilter.java b/src/de/lmu/ifi/dbs/elki/datasource/filter/AbstractVectorStreamConversionFilter.java
new file mode 100644
index 00000000..695a54e0
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/datasource/filter/AbstractVectorStreamConversionFilter.java
@@ -0,0 +1,51 @@
+package de.lmu.ifi.dbs.elki.datasource.filter;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.data.type.SimpleTypeInformation;
+
+/**
+ * Abstract base class for streaming filters that produce vectors.
+ *
+ * @author Erich Schubert
+ *
+ * @param <I> Input type
+ * @param <O> Output vector type
+ */
+public abstract class AbstractVectorStreamConversionFilter<I, O extends NumberVector<?>> extends AbstractStreamConversionFilter<I, O> {
+ /**
+ * Number vector factory.
+ */
+ protected NumberVector.Factory<O, ?> factory;
+
+ /**
+ * Initialize factory from a data type.
+ *
+ * @param type Output data type information.
+ */
+ protected void initializeOutputType(SimpleTypeInformation<O> type) {
+ factory = FilterUtil.guessFactory(type);
+ }
+
+}
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/filter/ByLabelFilter.java b/src/de/lmu/ifi/dbs/elki/datasource/filter/ByLabelFilter.java
index ebf01cfd..21f05739 100644
--- a/src/de/lmu/ifi/dbs/elki/datasource/filter/ByLabelFilter.java
+++ b/src/de/lmu/ifi/dbs/elki/datasource/filter/ByLabelFilter.java
@@ -46,7 +46,7 @@ public class ByLabelFilter extends AbstractStreamFilter {
/**
* Class logger
*/
- private static final Logging logger = Logging.getLogger(ByLabelFilter.class);
+ private static final Logging LOG = Logging.getLogger(ByLabelFilter.class);
/**
* The filter pattern
@@ -92,7 +92,7 @@ public class ByLabelFilter extends AbstractStreamFilter {
switch(ev){
case END_OF_STREAM:
if (lblcol < 0) {
- logger.warning("By label filter was used, but never saw a label relation!");
+ LOG.warning("By label filter was used, but never saw a label relation!");
}
return Event.END_OF_STREAM;
case META_CHANGED:
@@ -136,7 +136,7 @@ public class ByLabelFilter extends AbstractStreamFilter {
}
return Event.NEXT_OBJECT;
default:
- logger.warning("Unknown event: " + ev);
+ LOG.warning("Unknown event: " + ev);
}
}
}
@@ -155,7 +155,7 @@ public class ByLabelFilter extends AbstractStreamFilter {
* Key: {@code -patternfilter.pattern}
* </p>
*/
- public static final OptionID LABELFILTER_PATTERN_ID = OptionID.getOrCreateOptionID("patternfilter.pattern", "The filter pattern to use.");
+ public static final OptionID LABELFILTER_PATTERN_ID = new OptionID("patternfilter.pattern", "The filter pattern to use.");
/**
* Flag to use the pattern in inverted mode
@@ -163,7 +163,7 @@ public class ByLabelFilter extends AbstractStreamFilter {
* Key: {@code -patternfilter.invert}
* </p>
*/
- public static final OptionID LABELFILTER_PATTERN_INVERT_ID = OptionID.getOrCreateOptionID("patternfilter.invert", "Flag to invert pattern.");
+ public static final OptionID LABELFILTER_PATTERN_INVERT_ID = new OptionID("patternfilter.invert", "Flag to invert pattern.");
/**
* The pattern configured.
@@ -184,7 +184,7 @@ public class ByLabelFilter extends AbstractStreamFilter {
}
final Flag invertedF = new Flag(LABELFILTER_PATTERN_INVERT_ID);
if(config.grab(invertedF)) {
- inverted = invertedF.getValue();
+ inverted = invertedF.getValue().booleanValue();
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/filter/ClassLabelFilter.java b/src/de/lmu/ifi/dbs/elki/datasource/filter/ClassLabelFilter.java
index 95596773..4a349d3d 100644
--- a/src/de/lmu/ifi/dbs/elki/datasource/filter/ClassLabelFilter.java
+++ b/src/de/lmu/ifi/dbs/elki/datasource/filter/ClassLabelFilter.java
@@ -30,7 +30,6 @@ import de.lmu.ifi.dbs.elki.data.ClassLabel;
import de.lmu.ifi.dbs.elki.data.LabelList;
import de.lmu.ifi.dbs.elki.data.SimpleClassLabel;
import de.lmu.ifi.dbs.elki.data.type.SimpleTypeInformation;
-import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
import de.lmu.ifi.dbs.elki.datasource.bundle.MultipleObjectsBundle;
import de.lmu.ifi.dbs.elki.utilities.exceptions.AbortException;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
@@ -56,7 +55,7 @@ public class ClassLabelFilter implements ObjectFilter {
* Key: {@code -dbc.classLabelIndex}
* </p>
*/
- public static final OptionID CLASS_LABEL_INDEX_ID = OptionID.getOrCreateOptionID("dbc.classLabelIndex", "The index of the label to be used as class label.");
+ public static final OptionID CLASS_LABEL_INDEX_ID = new OptionID("dbc.classLabelIndex", "The index of the label to be used as class label.");
/**
* Parameter to specify the class of occurring class labels.
@@ -64,7 +63,7 @@ public class ClassLabelFilter implements ObjectFilter {
* Key: {@code -dbc.classLabelClass}
* </p>
*/
- public static final OptionID CLASS_LABEL_CLASS_ID = OptionID.getOrCreateOptionID("dbc.classLabelClass", "Class label class to use.");
+ public static final OptionID CLASS_LABEL_CLASS_ID = new OptionID("dbc.classLabelClass", "Class label class to use.");
/**
* The index of the label to be used as class label, null if no class label is
@@ -95,10 +94,10 @@ public class ClassLabelFilter implements ObjectFilter {
// Find a labellist column
boolean done = false;
boolean keeplabelcol = false;
- for(int i = 0; i < objects.metaLength(); i++) {
+ for (int i = 0; i < objects.metaLength(); i++) {
SimpleTypeInformation<?> meta = objects.meta(i);
// Skip non-labellist columns - or if we already had a labellist
- if(done || meta.getRestrictionClass() != LabelList.class) {
+ if (done || !LabelList.class.equals(meta.getRestrictionClass())) {
bundle.appendColumn(meta, objects.getColumn(i));
continue;
}
@@ -109,29 +108,27 @@ public class ClassLabelFilter implements ObjectFilter {
List<LabelList> lblcol = new ArrayList<LabelList>(objects.dataLength());
// Split the column
- for(Object obj : objects.getColumn(i)) {
- if(obj != null) {
+ for (Object obj : objects.getColumn(i)) {
+ if (obj != null) {
LabelList ll = (LabelList) obj;
try {
ClassLabel lbl = classLabelFactory.makeFromString(ll.remove(classLabelIndex));
clscol.add(lbl);
- }
- catch(Exception e) {
- throw new AbortException("Cannot initialize class labels: "+e.getMessage(), e);
+ } catch (Exception e) {
+ throw new AbortException("Cannot initialize class labels: " + e.getMessage(), e);
}
lblcol.add(ll);
- if(ll.size() > 0) {
+ if (ll.size() > 0) {
keeplabelcol = true;
}
- }
- else {
+ } else {
clscol.add(null);
lblcol.add(null);
}
}
- bundle.appendColumn(TypeUtil.CLASSLABEL, clscol);
+ bundle.appendColumn(classLabelFactory.getTypeInformation(), clscol);
// Only add the label column when it's not empty.
- if(keeplabelcol) {
+ if (keeplabelcol) {
bundle.appendColumn(meta, lblcol);
}
}
@@ -150,7 +147,7 @@ public class ClassLabelFilter implements ObjectFilter {
* The index of the label to be used as class label, null if no class label
* is specified.
*/
- protected Integer classLabelIndex;
+ protected int classLabelIndex;
/**
* The class label factory to use.
@@ -161,13 +158,14 @@ public class ClassLabelFilter implements ObjectFilter {
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
// parameter class label index
- final IntParameter classLabelIndexParam = new IntParameter(CLASS_LABEL_INDEX_ID, new GreaterEqualConstraint(0));
+ final IntParameter classLabelIndexParam = new IntParameter(CLASS_LABEL_INDEX_ID);
+ classLabelIndexParam.addConstraint(new GreaterEqualConstraint(0));
final ObjectParameter<ClassLabel.Factory<?>> classlabelClassParam = new ObjectParameter<ClassLabel.Factory<?>>(CLASS_LABEL_CLASS_ID, ClassLabel.Factory.class, SimpleClassLabel.Factory.class);
config.grab(classLabelIndexParam);
config.grab(classlabelClassParam);
- if(classLabelIndexParam.isDefined() && classlabelClassParam.isDefined()) {
- classLabelIndex = classLabelIndexParam.getValue();
+ if (classLabelIndexParam.isDefined() && classlabelClassParam.isDefined()) {
+ classLabelIndex = classLabelIndexParam.intValue();
classLabelFactory = classlabelClassParam.instantiateClass(config);
}
}
@@ -177,4 +175,4 @@ public class ClassLabelFilter implements ObjectFilter {
return new ClassLabelFilter(classLabelIndex, classLabelFactory);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/filter/DoubleVectorProjectionFilter.java b/src/de/lmu/ifi/dbs/elki/datasource/filter/DoubleVectorProjectionFilter.java
deleted file mode 100644
index 4793b041..00000000
--- a/src/de/lmu/ifi/dbs/elki/datasource/filter/DoubleVectorProjectionFilter.java
+++ /dev/null
@@ -1,85 +0,0 @@
-package de.lmu.ifi.dbs.elki.datasource.filter;
-/*
-This file is part of ELKI:
-Environment for Developing KDD-Applications Supported by Index-Structures
-
-Copyright (C) 2012
-Ludwig-Maximilians-Universität München
-Lehr- und Forschungseinheit für Datenbanksysteme
-ELKI Development Team
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-import java.util.BitSet;
-
-import de.lmu.ifi.dbs.elki.data.DoubleVector;
-import de.lmu.ifi.dbs.elki.data.type.SimpleTypeInformation;
-import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
-import de.lmu.ifi.dbs.elki.data.type.VectorFieldTypeInformation;
-import de.lmu.ifi.dbs.elki.utilities.Util;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
-
-/**
- * <p>Parser to project the ParsingResult obtained by a suitable base parser
- * onto a selected subset of attributes.</p>
- *
- * @author Arthur Zimek
- *
- * @apiviz.uses DoubleVector
- */
-public class DoubleVectorProjectionFilter extends AbstractFeatureSelectionFilter<DoubleVector> {
- /**
- * Constructor.
- *
- * @param selectedAttributes
- */
- public DoubleVectorProjectionFilter(BitSet selectedAttributes) {
- super(selectedAttributes);
- }
-
- @Override
- protected DoubleVector filterSingleObject(DoubleVector obj) {
- return Util.project(obj, getSelectedAttributes());
- }
-
- @Override
- protected SimpleTypeInformation<? super DoubleVector> getInputTypeRestriction() {
- return TypeUtil.DOUBLE_VECTOR_FIELD;
- }
-
- @Override
- protected SimpleTypeInformation<? super DoubleVector> convertedType(SimpleTypeInformation<DoubleVector> in) {
- return new VectorFieldTypeInformation<DoubleVector>(DoubleVector.class, DoubleVector.STATIC, getDimensionality(), DoubleVector.STATIC);
- }
-
- /**
- * Parameterization class.
- *
- * @author Erich Schubert
- *
- * @apiviz.exclude
- */
- public static class Parameterizer extends AbstractFeatureSelectionFilter.Parameterizer<DoubleVector> {
- @Override
- protected void makeOptions(Parameterization config) {
- super.makeOptions(config);
- }
-
- @Override
- protected DoubleVectorProjectionFilter makeInstance() {
- return new DoubleVectorProjectionFilter(selectedAttributes);
- }
- }
-} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/filter/DoubleVectorRandomProjectionFilter.java b/src/de/lmu/ifi/dbs/elki/datasource/filter/DoubleVectorRandomProjectionFilter.java
deleted file mode 100644
index b21e7cea..00000000
--- a/src/de/lmu/ifi/dbs/elki/datasource/filter/DoubleVectorRandomProjectionFilter.java
+++ /dev/null
@@ -1,87 +0,0 @@
-package de.lmu.ifi.dbs.elki.datasource.filter;
-/*
-This file is part of ELKI:
-Environment for Developing KDD-Applications Supported by Index-Structures
-
-Copyright (C) 2012
-Ludwig-Maximilians-Universität München
-Lehr- und Forschungseinheit für Datenbanksysteme
-ELKI Development Team
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-import de.lmu.ifi.dbs.elki.data.DoubleVector;
-import de.lmu.ifi.dbs.elki.data.type.SimpleTypeInformation;
-import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
-import de.lmu.ifi.dbs.elki.data.type.VectorFieldTypeInformation;
-import de.lmu.ifi.dbs.elki.utilities.Util;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
-
-/**
- * <p>
- * Parser to project the ParsingResult obtained by a suitable base parser onto a
- * randomly selected subset of attributes.
- * </p>
- *
- * @author Arthur Zimek
- *
- * @apiviz.uses DoubleVector
- */
-public class DoubleVectorRandomProjectionFilter extends AbstractRandomFeatureSelectionFilter<DoubleVector> {
- /**
- * Constructor.
- *
- * @param dim
- * @param seed
- */
- public DoubleVectorRandomProjectionFilter(int dim, long seed) {
- super(dim, seed);
- }
-
- @Override
- protected DoubleVector filterSingleObject(DoubleVector obj) {
- return Util.project(obj, selectedAttributes);
- }
-
- @Override
- protected SimpleTypeInformation<? super DoubleVector> getInputTypeRestriction() {
- return TypeUtil.DOUBLE_VECTOR_FIELD;
- }
-
- @Override
- protected SimpleTypeInformation<? super DoubleVector> convertedType(SimpleTypeInformation<DoubleVector> in) {
- initializeRandomAttributes(in);
- return new VectorFieldTypeInformation<DoubleVector>(DoubleVector.class, DoubleVector.STATIC, k, DoubleVector.STATIC);
- }
-
- /**
- * Parameterization class.
- *
- * @author Erich Schubert
- *
- * @apiviz.exclude
- */
- public static class Parameterizer extends AbstractRandomFeatureSelectionFilter.Parameterizer<DoubleVector> {
- @Override
- protected void makeOptions(Parameterization config) {
- super.makeOptions(config);
- }
-
- @Override
- protected DoubleVectorRandomProjectionFilter makeInstance() {
- return new DoubleVectorRandomProjectionFilter(k,seed);
- }
- }
-} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/filter/ExternalIDFilter.java b/src/de/lmu/ifi/dbs/elki/datasource/filter/ExternalIDFilter.java
index f48810f5..2753534a 100644
--- a/src/de/lmu/ifi/dbs/elki/datasource/filter/ExternalIDFilter.java
+++ b/src/de/lmu/ifi/dbs/elki/datasource/filter/ExternalIDFilter.java
@@ -48,13 +48,13 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
// TODO: use a non-string class for external ids?
public class ExternalIDFilter implements ObjectFilter {
/**
- * Parameter that specifies the index of the label to be used as
- * external Id, must be an integer equal to or greater than 0.
+ * Parameter that specifies the index of the label to be used as external Id,
+ * must be an integer equal to or greater than 0.
* <p>
* Key: {@code -dbc.externalIdIndex}
* </p>
*/
- public static final OptionID EXTERNALID_INDEX_ID = OptionID.getOrCreateOptionID("dbc.externalIdIndex", "The index of the label to be used as external Id.");
+ public static final OptionID EXTERNALID_INDEX_ID = new OptionID("dbc.externalIdIndex", "The index of the label to be used as external Id.");
/**
* The index of the label to be used as external Id.
@@ -77,10 +77,10 @@ public class ExternalIDFilter implements ObjectFilter {
// Find a labellist column
boolean done = false;
boolean keeplabelcol = false;
- for(int i = 0; i < objects.metaLength(); i++) {
+ for (int i = 0; i < objects.metaLength(); i++) {
SimpleTypeInformation<?> meta = objects.meta(i);
// Skip non-labellist columns - or if we already had a labellist
- if(done || meta.getRestrictionClass() != LabelList.class) {
+ if (done || !LabelList.class.equals(meta.getRestrictionClass())) {
bundle.appendColumn(meta, objects.getColumn(i));
continue;
}
@@ -91,16 +91,15 @@ public class ExternalIDFilter implements ObjectFilter {
List<LabelList> lblcol = new ArrayList<LabelList>(objects.dataLength());
// Split the column
- for(Object obj : objects.getColumn(i)) {
- if(obj != null) {
+ for (Object obj : objects.getColumn(i)) {
+ if (obj != null) {
LabelList ll = (LabelList) obj;
eidcol.add(new ExternalID(ll.remove(externalIdIndex)));
lblcol.add(ll);
- if(ll.size() > 0) {
+ if (ll.size() > 0) {
keeplabelcol = true;
}
- }
- else {
+ } else {
eidcol.add(null);
lblcol.add(null);
}
@@ -108,7 +107,7 @@ public class ExternalIDFilter implements ObjectFilter {
bundle.appendColumn(TypeUtil.EXTERNALID, eidcol);
// Only add the label column when it's not empty.
- if(keeplabelcol) {
+ if (keeplabelcol) {
bundle.appendColumn(meta, lblcol);
}
}
@@ -128,9 +127,10 @@ public class ExternalIDFilter implements ObjectFilter {
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- final IntParameter externalIdIndexParam = new IntParameter(EXTERNALID_INDEX_ID, new GreaterEqualConstraint(0));
- if(config.grab(externalIdIndexParam)) {
- externalIdIndex = externalIdIndexParam.getValue();
+ final IntParameter externalIdIndexParam = new IntParameter(EXTERNALID_INDEX_ID);
+ externalIdIndexParam.addConstraint(new GreaterEqualConstraint(0));
+ if (config.grab(externalIdIndexParam)) {
+ externalIdIndex = externalIdIndexParam.intValue();
}
}
@@ -139,4 +139,4 @@ public class ExternalIDFilter implements ObjectFilter {
return new ExternalIDFilter(externalIdIndex);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/filter/FilterUtil.java b/src/de/lmu/ifi/dbs/elki/datasource/filter/FilterUtil.java
index 0015dce1..7b794066 100644
--- a/src/de/lmu/ifi/dbs/elki/datasource/filter/FilterUtil.java
+++ b/src/de/lmu/ifi/dbs/elki/datasource/filter/FilterUtil.java
@@ -37,25 +37,33 @@ import de.lmu.ifi.dbs.elki.logging.LoggingUtil;
*/
public final class FilterUtil {
/**
- * Try to guess the factory
+ * Fake constructor: do not instantiate.
+ */
+ private FilterUtil() {
+ // Do not instantiate.
+ }
+
+ /**
+ * Try to guess the appropriate factory.
*
* @param in Input type
+ * @param <V> Vector type
* @return Factory
*/
@SuppressWarnings("unchecked")
- protected static <V extends NumberVector<?, ?>> V guessFactory(SimpleTypeInformation<V> in) {
- V factory = null;
+ public static <V extends NumberVector<?>> NumberVector.Factory<V, ?> guessFactory(SimpleTypeInformation<V> in) {
+ NumberVector.Factory<V, ?> factory = null;
if(in instanceof VectorFieldTypeInformation) {
- factory = ((VectorFieldTypeInformation<V>) in).getFactory();
+ factory = (NumberVector.Factory<V, ?>) ((VectorFieldTypeInformation<V>) in).getFactory();
}
if(factory == null) {
// FIXME: hack. Add factories to simple type information, too?
try {
- Field f = in.getRestrictionClass().getField("STATIC");
- factory = (V) f.get(null);
+ Field f = in.getRestrictionClass().getField("FACTORY");
+ factory = (NumberVector.Factory<V, ?>) f.get(null);
}
catch(Exception e) {
- LoggingUtil.warning("Cannot determine factory for type " + in.getRestrictionClass());
+ LoggingUtil.warning("Cannot determine factory for type " + in.getRestrictionClass(), e);
}
}
return factory;
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/filter/FixedDBIDsFilter.java b/src/de/lmu/ifi/dbs/elki/datasource/filter/FixedDBIDsFilter.java
index c34ecbe7..2e5071a4 100644
--- a/src/de/lmu/ifi/dbs/elki/datasource/filter/FixedDBIDsFilter.java
+++ b/src/de/lmu/ifi/dbs/elki/datasource/filter/FixedDBIDsFilter.java
@@ -47,7 +47,7 @@ public class FixedDBIDsFilter extends AbstractStreamFilter {
* Key: {@code -dbc.startid}
* </p>
*/
- public static final OptionID IDSTART_ID = OptionID.getOrCreateOptionID("dbc.startid", "Object ID to start counting with");
+ public static final OptionID IDSTART_ID = new OptionID("dbc.startid", "Object ID to start counting with");
/**
* The filtered meta
@@ -116,7 +116,7 @@ public class FixedDBIDsFilter extends AbstractStreamFilter {
super.makeOptions(config);
IntParameter startidParam = new IntParameter(IDSTART_ID);
if(config.grab(startidParam)) {
- startid = startidParam.getValue();
+ startid = startidParam.getValue().intValue();
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/filter/HistogramJitterFilter.java b/src/de/lmu/ifi/dbs/elki/datasource/filter/HistogramJitterFilter.java
new file mode 100644
index 00000000..6723a12a
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/datasource/filter/HistogramJitterFilter.java
@@ -0,0 +1,164 @@
+package de.lmu.ifi.dbs.elki.datasource.filter;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+import de.lmu.ifi.dbs.elki.data.DoubleVector;
+import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.data.type.SimpleTypeInformation;
+import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
+import de.lmu.ifi.dbs.elki.math.statistics.distribution.ExponentialDistribution;
+import de.lmu.ifi.dbs.elki.utilities.RandomFactory;
+import de.lmu.ifi.dbs.elki.utilities.documentation.Description;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterEqualConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.DoubleParameter;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.RandomParameter;
+
+/**
+ * Add Jitter, preserving the histogram properties (same sum, nonnegative).
+ *
+ * For each vector, the total sum of all dimensions is computed.<br />
+ * Then a random vector of the average length <code>jitter * scale</code> is
+ * added and the result normalized to the original vectors sum. The individual
+ * dimensions are drawn from an exponential distribution with scale
+ * <code>jitter / dimensionality</code>, so it is expected that the error in
+ * most dimensions will be low, and higher in few.
+ *
+ * This is designed to degrade the quality of a histogram, while preserving the
+ * total sum (e.g. to keep the normalization). The factor "jitter" can be used
+ * to control the degradation amount.
+ *
+ * @author Erich Schubert
+ *
+ * @param <V> Vector type
+ */
+@Description("Add uniform Jitter to a dataset, while preserving the total vector sum.")
+public class HistogramJitterFilter<V extends NumberVector<?>> extends AbstractVectorStreamConversionFilter<V, V> {
+ /**
+ * Jitter amount.
+ */
+ double jitter;
+
+ /**
+ * Random generator.
+ */
+ ExponentialDistribution rnd;
+
+ /**
+ * Constructor.
+ *
+ * @param jitter Relative amount of jitter to add
+ * @param rnd Random generator
+ */
+ public HistogramJitterFilter(double jitter, RandomFactory rnd) {
+ super();
+ this.jitter = jitter;
+ this.rnd = new ExponentialDistribution(1, rnd.getRandom());
+ }
+
+ @Override
+ protected V filterSingleObject(V obj) {
+ final int dim = obj.getDimensionality();
+ // Compute the total sum.
+ double osum = 0;
+ for (int i = 0; i < dim; i++) {
+ osum += obj.doubleValue(i);
+ }
+ // Actual maximum jitter amount:
+ final double maxjitter = 2 * jitter / dim * osum;
+ // Generate jitter vector
+ double[] raw = new double[dim];
+ double jsum = 0; // Sum of jitter
+ for (int i = 0; i < raw.length; i++) {
+ raw[i] = rnd.nextRandom() * maxjitter;
+ jsum += raw[i];
+ }
+ final double mix = jsum / osum;
+ // Combine the two vector
+ for (int i = 0; i < raw.length; i++) {
+ raw[i] = raw[i] + (1 - mix) * obj.doubleValue(i);
+ }
+ return factory.newNumberVector(raw);
+ }
+
+ @Override
+ protected SimpleTypeInformation<? super V> getInputTypeRestriction() {
+ return TypeUtil.NUMBER_VECTOR_VARIABLE_LENGTH;
+ }
+
+ @Override
+ protected SimpleTypeInformation<V> convertedType(SimpleTypeInformation<V> in) {
+ initializeOutputType(in);
+ return in;
+ }
+
+ /**
+ * Parameterization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public static class Parameterizer extends AbstractParameterizer {
+ /**
+ * Option ID for the jitter strength.
+ */
+ public static final OptionID JITTER_ID = new OptionID("jitter.amount", "Jitter amount relative to data.");
+
+ /**
+ * Option ID for the jitter random seed.
+ */
+ public static final OptionID SEED_ID = new OptionID("jitter.seed", "Jitter random seed.");
+
+ /**
+ * Jitter amount.
+ */
+ double jitter = 0.1;
+
+ /**
+ * Random generator seed.
+ */
+ RandomFactory rnd;
+
+ @Override
+ protected void makeOptions(Parameterization config) {
+ super.makeOptions(config);
+ DoubleParameter jitterP = new DoubleParameter(JITTER_ID);
+ jitterP.addConstraint(new GreaterEqualConstraint(Double.valueOf(0.0)));
+ if (config.grab(jitterP)) {
+ jitter = jitterP.getValue().doubleValue();
+ }
+ RandomParameter rndP = new RandomParameter(SEED_ID);
+ if (config.grab(rndP)) {
+ rnd = rndP.getValue();
+ }
+ }
+
+ @Override
+ protected HistogramJitterFilter<DoubleVector> makeInstance() {
+ return new HistogramJitterFilter<DoubleVector>(jitter, rnd);
+ }
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/filter/NoMissingValuesFilter.java b/src/de/lmu/ifi/dbs/elki/datasource/filter/NoMissingValuesFilter.java
index 7d5b6c44..bfc6ad5c 100644
--- a/src/de/lmu/ifi/dbs/elki/datasource/filter/NoMissingValuesFilter.java
+++ b/src/de/lmu/ifi/dbs/elki/datasource/filter/NoMissingValuesFilter.java
@@ -39,7 +39,7 @@ public class NoMissingValuesFilter extends AbstractStreamFilter {
/**
* Class logger
*/
- private static final Logging logger = Logging.getLogger(NoMissingValuesFilter.class);
+ private static final Logging LOG = Logging.getLogger(NoMissingValuesFilter.class);
/**
* Number of columns
@@ -91,8 +91,8 @@ public class NoMissingValuesFilter extends AbstractStreamFilter {
@Override
public MultipleObjectsBundle filter(final MultipleObjectsBundle objects) {
- if(logger.isDebugging()) {
- logger.debug("Filtering the data set");
+ if(LOG.isDebugging()) {
+ LOG.debug("Filtering the data set");
}
MultipleObjectsBundle bundle = new MultipleObjectsBundle();
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/filter/RandomSamplingStreamFilter.java b/src/de/lmu/ifi/dbs/elki/datasource/filter/RandomSamplingStreamFilter.java
index 56509d8a..0fbec083 100644
--- a/src/de/lmu/ifi/dbs/elki/datasource/filter/RandomSamplingStreamFilter.java
+++ b/src/de/lmu/ifi/dbs/elki/datasource/filter/RandomSamplingStreamFilter.java
@@ -26,13 +26,14 @@ package de.lmu.ifi.dbs.elki.datasource.filter;
import java.util.Random;
import de.lmu.ifi.dbs.elki.datasource.bundle.BundleMeta;
+import de.lmu.ifi.dbs.elki.utilities.RandomFactory;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.IntervalConstraint;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.IntervalConstraint.IntervalBoundary;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterEqualConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.LessEqualConstraint;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.DoubleParameter;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.LongParameter;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.RandomParameter;
/**
* Subsampling stream filter.
@@ -54,12 +55,12 @@ public class RandomSamplingStreamFilter extends AbstractStreamFilter {
* Constructor.
*
* @param prob Probability
- * @param seed Random seed
+ * @param rnd Random generator
*/
- public RandomSamplingStreamFilter(double prob, Long seed) {
+ public RandomSamplingStreamFilter(double prob, RandomFactory rnd) {
super();
this.prob = prob;
- this.random = (seed != null) ? new Random(seed) : new Random();
+ this.random = rnd.getRandom();
}
@Override
@@ -74,15 +75,15 @@ public class RandomSamplingStreamFilter extends AbstractStreamFilter {
@Override
public Event nextEvent() {
- while(true) {
+ while (true) {
Event ev = source.nextEvent();
- switch(ev){
+ switch(ev) {
case END_OF_STREAM:
return ev;
case META_CHANGED:
return ev;
case NEXT_OBJECT:
- if(random.nextDouble() < prob) {
+ if (random.nextDouble() < prob) {
return ev;
}
continue;
@@ -101,12 +102,12 @@ public class RandomSamplingStreamFilter extends AbstractStreamFilter {
/**
* Option ID for sampling probability
*/
- private static final OptionID PROB_ID = OptionID.getOrCreateOptionID("sampling.p", "Sampling probability. Each object has a chance of being samples with this probability.");
+ private static final OptionID PROB_ID = new OptionID("sampling.p", "Sampling probability. Each object has a chance of being samples with this probability.");
/**
* Option ID for random seed
*/
- private static final OptionID SEED_ID = OptionID.getOrCreateOptionID("sampling.seed", "Random generator seed for sampling.");
+ private static final OptionID SEED_ID = new OptionID("sampling.seed", "Random generator seed for sampling.");
/**
* Probability
@@ -114,26 +115,28 @@ public class RandomSamplingStreamFilter extends AbstractStreamFilter {
protected double prob;
/**
- * Random seed
+ * Random generator
*/
- protected Long seed = null;
+ protected RandomFactory rnd;
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- DoubleParameter probP = new DoubleParameter(PROB_ID, new IntervalConstraint(0, IntervalBoundary.CLOSE, 1.0, IntervalBoundary.CLOSE));
- if(config.grab(probP)) {
- prob = probP.getValue();
+ DoubleParameter probP = new DoubleParameter(PROB_ID);
+ probP.addConstraint(new GreaterEqualConstraint(0.0));
+ probP.addConstraint(new LessEqualConstraint(1.0));
+ if (config.grab(probP)) {
+ prob = probP.getValue().doubleValue();
}
- LongParameter seedP = new LongParameter(SEED_ID, true);
- if(config.grab(seedP)) {
- seed = seedP.getValue();
+ RandomParameter rndP = new RandomParameter(SEED_ID);
+ if (config.grab(rndP)) {
+ rnd = rndP.getValue();
}
}
@Override
protected RandomSamplingStreamFilter makeInstance() {
- return new RandomSamplingStreamFilter(prob, seed);
+ return new RandomSamplingStreamFilter(prob, rnd);
}
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/filter/ShuffleObjectsFilter.java b/src/de/lmu/ifi/dbs/elki/datasource/filter/ShuffleObjectsFilter.java
index a8bf2cec..01a6da10 100644
--- a/src/de/lmu/ifi/dbs/elki/datasource/filter/ShuffleObjectsFilter.java
+++ b/src/de/lmu/ifi/dbs/elki/datasource/filter/ShuffleObjectsFilter.java
@@ -29,10 +29,11 @@ import java.util.Random;
import de.lmu.ifi.dbs.elki.datasource.bundle.MultipleObjectsBundle;
import de.lmu.ifi.dbs.elki.logging.Logging;
+import de.lmu.ifi.dbs.elki.utilities.RandomFactory;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.LongParameter;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.RandomParameter;
/**
* A filter to shuffle the dataset.
@@ -43,49 +44,47 @@ public class ShuffleObjectsFilter implements ObjectFilter {
/**
* Class logger
*/
- private static final Logging logger = Logging.getLogger(ShuffleObjectsFilter.class);
+ private static final Logging LOG = Logging.getLogger(ShuffleObjectsFilter.class);
/**
* Optional parameter to specify a seed for randomly shuffling the rows of the
- * database. If unused, no shuffling will be performed. Shuffling takes time
+ * database. If not set, a random seed will be used. Shuffling takes time
* linearly dependent from the size of the database.
* <p>
* Key: {@code -shuffle.seed}
* </p>
*/
- public static final OptionID SEED_ID = OptionID.getOrCreateOptionID("shuffle.seed", "Seed for randomly shuffling the rows for the database. If the parameter is not set, no shuffling will be performed.");
+ public static final OptionID SEED_ID = new OptionID("shuffle.seed", "Seed for randomly shuffling the rows for the database. If the parameter is not set, a random seed will be used.");
/**
- * Seed for randomly shuffling the rows of the database. If null, no shuffling
- * will be performed. Shuffling takes time linearly dependent from the size of
- * the database.
+ * Random generator.
*/
- final Long seed;
+ final RandomFactory rnd;
/**
* Constructor.
*
- * @param seed Seed value, may be {@code null} for a random seed.
+ * @param rnd Random generator
*/
- public ShuffleObjectsFilter(Long seed) {
+ public ShuffleObjectsFilter(RandomFactory rnd) {
super();
- this.seed = seed;
+ this.rnd = rnd;
}
@Override
public MultipleObjectsBundle filter(MultipleObjectsBundle objects) {
- if(logger.isDebugging()) {
- logger.debug("Shuffling the data set");
+ if (LOG.isDebugging()) {
+ LOG.debug("Shuffling the data set");
}
- final Random random = (seed == null) ? new Random() : new Random(seed);
+ final Random random = rnd.getRandom();
final int size = objects.dataLength();
final int[] offsets = new int[size];
- for(int i = 0; i < size; i++) {
+ for (int i = 0; i < size; i++) {
offsets[i] = i;
}
// Randomize the offset array
- for(int i = size; i > 1; i--) {
+ for (int i = size; i > 1; i--) {
final int j = random.nextInt(i);
// Swap the elements at positions j and i - 1:
final int temp = offsets[j];
@@ -94,11 +93,11 @@ public class ShuffleObjectsFilter implements ObjectFilter {
}
MultipleObjectsBundle bundle = new MultipleObjectsBundle();
- for(int j = 0; j < objects.metaLength(); j++) {
+ for (int j = 0; j < objects.metaLength(); j++) {
// Reorder column accordingly
List<?> in = objects.getColumn(j);
List<Object> data = new ArrayList<Object>(size);
- for(int i = 0; i < size; i++) {
+ for (int i = 0; i < size; i++) {
data.add(in.get(offsets[i]));
}
bundle.appendColumn(objects.meta(j), data);
@@ -114,20 +113,20 @@ public class ShuffleObjectsFilter implements ObjectFilter {
* @apiviz.exclude
*/
public static class Parameterizer extends AbstractParameterizer {
- Long seed = null;
+ RandomFactory rnd;
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- LongParameter seedParam = new LongParameter(SEED_ID, true);
- if(config.grab(seedParam)) {
- seed = seedParam.getValue();
+ RandomParameter rndP = new RandomParameter(SEED_ID);
+ if (config.grab(rndP)) {
+ rnd = rndP.getValue();
}
}
@Override
protected Object makeInstance() {
- return new ShuffleObjectsFilter(seed);
+ return new ShuffleObjectsFilter(rnd);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/filter/SortByLabelFilter.java b/src/de/lmu/ifi/dbs/elki/datasource/filter/SortByLabelFilter.java
index 5aedc79c..308a54b1 100644
--- a/src/de/lmu/ifi/dbs/elki/datasource/filter/SortByLabelFilter.java
+++ b/src/de/lmu/ifi/dbs/elki/datasource/filter/SortByLabelFilter.java
@@ -24,13 +24,13 @@ package de.lmu.ifi.dbs.elki.datasource.filter;
*/
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Comparator;
import java.util.List;
import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
import de.lmu.ifi.dbs.elki.datasource.bundle.MultipleObjectsBundle;
import de.lmu.ifi.dbs.elki.logging.Logging;
+import de.lmu.ifi.dbs.elki.utilities.datastructures.arrays.IntegerArrayQuickSort;
+import de.lmu.ifi.dbs.elki.utilities.datastructures.arrays.IntegerComparator;
/**
* A filter to sort the data set by some label.
@@ -43,7 +43,7 @@ public class SortByLabelFilter implements ObjectFilter {
/**
* Class logger
*/
- private static final Logging logger = Logging.getLogger(SortByLabelFilter.class);
+ private static final Logging LOG = Logging.getLogger(SortByLabelFilter.class);
/**
* Constructor.
@@ -54,31 +54,31 @@ public class SortByLabelFilter implements ObjectFilter {
@Override
public MultipleObjectsBundle filter(final MultipleObjectsBundle objects) {
- if(logger.isDebugging()) {
- logger.debug("Shuffling the data set");
+ if (LOG.isDebugging()) {
+ LOG.debug("Shuffling the data set");
}
// Prepare a reposition array for cheap resorting
final int size = objects.dataLength();
- final Integer[] offsets = new Integer[size];
- for(int i = 0; i < size; i++) {
+ final int[] offsets = new int[size];
+ for (int i = 0; i < size; i++) {
offsets[i] = i;
}
// Sort by labels - identify a label column
final int lblcol;
{
int lblc = -1;
- for(int i = 0; i < objects.metaLength(); i++) {
- if(TypeUtil.GUESSED_LABEL.isAssignableFromType(objects.meta(i))) {
+ for (int i = 0; i < objects.metaLength(); i++) {
+ if (TypeUtil.GUESSED_LABEL.isAssignableFromType(objects.meta(i))) {
lblc = i;
break;
}
}
lblcol = lblc; // make static
}
- Arrays.sort(offsets, new Comparator<Integer>() {
+ IntegerArrayQuickSort.sort(offsets, new IntegerComparator() {
@Override
- public int compare(Integer o1, Integer o2) {
+ public int compare(int o1, int o2) {
String l1 = objects.data(o1, lblcol).toString();
String l2 = objects.data(o2, lblcol).toString();
return l1.compareToIgnoreCase(l2);
@@ -86,15 +86,15 @@ public class SortByLabelFilter implements ObjectFilter {
});
MultipleObjectsBundle bundle = new MultipleObjectsBundle();
- for(int j = 0; j < objects.metaLength(); j++) {
+ for (int j = 0; j < objects.metaLength(); j++) {
// Reorder column accordingly
List<?> in = objects.getColumn(j);
List<Object> data = new ArrayList<Object>(size);
- for(int i = 0; i < size; i++) {
+ for (int i = 0; i < size; i++) {
data.add(in.get(offsets[i]));
}
bundle.appendColumn(objects.meta(j), data);
}
return bundle;
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/filter/SparseNumberVectorProjectionFilter.java b/src/de/lmu/ifi/dbs/elki/datasource/filter/SparseNumberVectorProjectionFilter.java
deleted file mode 100644
index 79a671c5..00000000
--- a/src/de/lmu/ifi/dbs/elki/datasource/filter/SparseNumberVectorProjectionFilter.java
+++ /dev/null
@@ -1,86 +0,0 @@
-package de.lmu.ifi.dbs.elki.datasource.filter;
-/*
-This file is part of ELKI:
-Environment for Developing KDD-Applications Supported by Index-Structures
-
-Copyright (C) 2012
-Ludwig-Maximilians-Universität München
-Lehr- und Forschungseinheit für Datenbanksysteme
-ELKI Development Team
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-import java.util.BitSet;
-
-import de.lmu.ifi.dbs.elki.data.SparseNumberVector;
-import de.lmu.ifi.dbs.elki.data.type.SimpleTypeInformation;
-import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
-import de.lmu.ifi.dbs.elki.data.type.VectorFieldTypeInformation;
-import de.lmu.ifi.dbs.elki.utilities.Util;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
-
-/**
- * <p>
- * Parser to project the ParsingResult obtained by a suitable base parser onto a
- * selected subset of attributes.
- * </p>
- *
- * @author Arthur Zimek
- */
-public class SparseNumberVectorProjectionFilter<V extends SparseNumberVector<V, ?>> extends AbstractFeatureSelectionFilter<V> {
- /**
- * Constructor.
- *
- * @param selectedAttributes
- */
- public SparseNumberVectorProjectionFilter(BitSet selectedAttributes) {
- super(selectedAttributes);
- }
-
- @Override
- protected V filterSingleObject(V obj) {
- return Util.project(obj, getSelectedAttributes());
- }
-
- @Override
- protected SimpleTypeInformation<? super V> getInputTypeRestriction() {
- return TypeUtil.SPARSE_VECTOR_FIELD;
- }
-
- @Override
- protected SimpleTypeInformation<? super V> convertedType(SimpleTypeInformation<V> in) {
- V factory = FilterUtil.guessFactory(in);
- return new VectorFieldTypeInformation<V>(in.getRestrictionClass(), getDimensionality(), factory);
- }
-
- /**
- * Parameterization class.
- *
- * @author Erich Schubert
- *
- * @apiviz.exclude
- */
- public static class Parameterizer<V extends SparseNumberVector<V, ?>> extends AbstractFeatureSelectionFilter.Parameterizer<V> {
- @Override
- protected void makeOptions(Parameterization config) {
- super.makeOptions(config);
- }
-
- @Override
- protected SparseNumberVectorProjectionFilter<V> makeInstance() {
- return new SparseNumberVectorProjectionFilter<V>(selectedAttributes);
- }
- }
-} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/filter/SparseNumberVectorRandomProjectionFilter.java b/src/de/lmu/ifi/dbs/elki/datasource/filter/SparseNumberVectorRandomProjectionFilter.java
deleted file mode 100644
index 8597d659..00000000
--- a/src/de/lmu/ifi/dbs/elki/datasource/filter/SparseNumberVectorRandomProjectionFilter.java
+++ /dev/null
@@ -1,83 +0,0 @@
-package de.lmu.ifi.dbs.elki.datasource.filter;
-/*
-This file is part of ELKI:
-Environment for Developing KDD-Applications Supported by Index-Structures
-
-Copyright (C) 2012
-Ludwig-Maximilians-Universität München
-Lehr- und Forschungseinheit für Datenbanksysteme
-ELKI Development Team
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-import de.lmu.ifi.dbs.elki.data.SparseNumberVector;
-import de.lmu.ifi.dbs.elki.data.type.SimpleTypeInformation;
-import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
-import de.lmu.ifi.dbs.elki.data.type.VectorFieldTypeInformation;
-import de.lmu.ifi.dbs.elki.utilities.Util;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
-
-/**
- * <p>Parser to project the ParsingResult obtained by a suitable base parser
- * onto a randomly selected subset of attributes.</p>
- *
- * @author Arthur Zimek
- */
-public class SparseNumberVectorRandomProjectionFilter<V extends SparseNumberVector<V, ?>> extends AbstractRandomFeatureSelectionFilter<V> {
- /**
- * Constructor.
- *
- * @param dim
- */
- public SparseNumberVectorRandomProjectionFilter(int dim) {
- super(dim);
- }
-
- @Override
- protected V filterSingleObject(V obj) {
- return Util.project(obj, selectedAttributes);
- }
-
- @Override
- protected SimpleTypeInformation<? super V> getInputTypeRestriction() {
- return TypeUtil.SPARSE_VECTOR_FIELD;
- }
-
- @Override
- protected SimpleTypeInformation<? super V> convertedType(SimpleTypeInformation<V> in) {
- initializeRandomAttributes(in);
- V factory = FilterUtil.guessFactory(in);
- return new VectorFieldTypeInformation<V>(in.getRestrictionClass(), k, factory);
- }
-
- /**
- * Parameterization class.
- *
- * @author Erich Schubert
- *
- * @apiviz.exclude
- */
- public static class Parameterizer<V extends SparseNumberVector<V, ?>> extends AbstractRandomFeatureSelectionFilter.Parameterizer<V> {
- @Override
- protected void makeOptions(Parameterization config) {
- super.makeOptions(config);
- }
-
- @Override
- protected SparseNumberVectorRandomProjectionFilter<V> makeInstance() {
- return new SparseNumberVectorRandomProjectionFilter<V>(k);
- }
- }
-} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/filter/SparseVectorFieldFilter.java b/src/de/lmu/ifi/dbs/elki/datasource/filter/SparseVectorFieldFilter.java
index 9d34057b..d3ef418d 100644
--- a/src/de/lmu/ifi/dbs/elki/datasource/filter/SparseVectorFieldFilter.java
+++ b/src/de/lmu/ifi/dbs/elki/datasource/filter/SparseVectorFieldFilter.java
@@ -33,10 +33,12 @@ import de.lmu.ifi.dbs.elki.data.type.VectorFieldTypeInformation;
* the maximum dimensionality for each vector.
*
* @author Erich Schubert
+ *
+ * @param <V> Vector type
*/
-public class SparseVectorFieldFilter<V extends SparseNumberVector<V, ?>> extends AbstractConversionFilter<V, V> {
+public class SparseVectorFieldFilter<V extends SparseNumberVector<?>> extends AbstractConversionFilter<V, V> {
/**
- * Maximum dimension
+ * Maximum dimension.
*/
int maxdim = -1;
@@ -71,7 +73,7 @@ public class SparseVectorFieldFilter<V extends SparseNumberVector<V, ?>> extends
@Override
protected SimpleTypeInformation<? super V> convertedType(SimpleTypeInformation<V> in) {
- V factory = FilterUtil.guessFactory(in);
- return new VectorFieldTypeInformation<V>(in.getRestrictionClass(), maxdim, factory);
+ SparseNumberVector.Factory<V, ?> factory = (SparseNumberVector.Factory<V, ?>) FilterUtil.guessFactory(in);
+ return new VectorFieldTypeInformation<V>(factory, maxdim);
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/filter/SplitNumberVectorFilter.java b/src/de/lmu/ifi/dbs/elki/datasource/filter/SplitNumberVectorFilter.java
index 827a5011..898eeff7 100644
--- a/src/de/lmu/ifi/dbs/elki/datasource/filter/SplitNumberVectorFilter.java
+++ b/src/de/lmu/ifi/dbs/elki/datasource/filter/SplitNumberVectorFilter.java
@@ -27,6 +27,7 @@ import java.util.ArrayList;
import java.util.List;
import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.data.NumberVector.Factory;
import de.lmu.ifi.dbs.elki.data.type.SimpleTypeInformation;
import de.lmu.ifi.dbs.elki.data.type.TypeInformation;
import de.lmu.ifi.dbs.elki.data.type.VectorFieldTypeInformation;
@@ -34,7 +35,8 @@ import de.lmu.ifi.dbs.elki.datasource.bundle.MultipleObjectsBundle;
import de.lmu.ifi.dbs.elki.utilities.exceptions.AbortException;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.ListGreaterEqualConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterEqualConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.ListEachConstraint;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntListParameter;
@@ -44,8 +46,10 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntListParameter;
* @author Erich Schubert
*
* @apiviz.uses NumberVector
+ *
+ * @param <V> Vector type
*/
-public class SplitNumberVectorFilter<V extends NumberVector<V, ?>> implements ObjectFilter {
+public class SplitNumberVectorFilter<V extends NumberVector<?>> implements ObjectFilter {
/**
* Selected dimensions.
*/
@@ -63,46 +67,47 @@ public class SplitNumberVectorFilter<V extends NumberVector<V, ?>> implements Ob
@Override
public MultipleObjectsBundle filter(MultipleObjectsBundle objects) {
- if(objects.dataLength() == 0) {
+ if (objects.dataLength() == 0) {
return objects;
}
MultipleObjectsBundle bundle = new MultipleObjectsBundle();
- for(int r = 0; r < objects.metaLength(); r++) {
+ for (int r = 0; r < objects.metaLength(); r++) {
@SuppressWarnings("unchecked")
SimpleTypeInformation<Object> type = (SimpleTypeInformation<Object>) objects.meta(r);
@SuppressWarnings("unchecked")
final List<Object> column = (List<Object>) objects.getColumn(r);
- if(!getInputTypeRestriction().isAssignableFromType(type)) {
+ if (!getInputTypeRestriction().isAssignableFromType(type)) {
bundle.appendColumn(type, column);
continue;
}
// Should be a vector type after above test.
@SuppressWarnings("unchecked")
final VectorFieldTypeInformation<V> vtype = VectorFieldTypeInformation.class.cast(type);
+ Factory<V, ?> factory = FilterUtil.guessFactory(vtype);
// Get the replacement type informations
- VectorFieldTypeInformation<V> type1 = new VectorFieldTypeInformation<V>(type.getRestrictionClass(), type.getSerializer(), dims.length, dims.length);
- VectorFieldTypeInformation<V> type2 = new VectorFieldTypeInformation<V>(type.getRestrictionClass(), type.getSerializer(), vtype.dimensionality() - dims.length, vtype.dimensionality() - dims.length);
+ VectorFieldTypeInformation<V> type1 = new VectorFieldTypeInformation<V>(factory, dims.length);
+ VectorFieldTypeInformation<V> type2 = new VectorFieldTypeInformation<V>(factory, vtype.getDimensionality() - dims.length);
final List<V> col1 = new ArrayList<V>(column.size());
final List<V> col2 = new ArrayList<V>(column.size());
bundle.appendColumn(type1, col1);
bundle.appendColumn(type2, col2);
// Build other dimensions array.
- int[] odims = new int[vtype.dimensionality() - dims.length];
+ int[] odims = new int[vtype.getDimensionality() - dims.length];
{
int i = 0;
- for(int d = 1; d <= vtype.dimensionality(); d++) {
+ for (int d = 0; d < vtype.getDimensionality(); d++) {
boolean found = false;
- for(int j = 0; j < dims.length; j++) {
- if(dims[j] == d) {
+ for (int j = 0; j < dims.length; j++) {
+ if (dims[j] == d) {
found = true;
break;
}
}
- if(!found) {
- if(i >= odims.length) {
+ if (!found) {
+ if (i >= odims.length) {
throw new AbortException("Dimensionalities not proper!");
}
odims[i] = d;
@@ -110,20 +115,20 @@ public class SplitNumberVectorFilter<V extends NumberVector<V, ?>> implements Ob
}
}
}
- // Normalization scan
- for(int i = 0; i < objects.dataLength(); i++) {
+ // Splitting scan.
+ for (int i = 0; i < objects.dataLength(); i++) {
@SuppressWarnings("unchecked")
final V obj = (V) column.get(i);
double[] part1 = new double[dims.length];
double[] part2 = new double[obj.getDimensionality() - dims.length];
- for(int d = 0; d < dims.length; d++) {
+ for (int d = 0; d < dims.length; d++) {
part1[d] = obj.doubleValue(dims[d]);
}
- for(int d = 0; d < odims.length; d++) {
+ for (int d = 0; d < odims.length; d++) {
part2[d] = obj.doubleValue(odims[d]);
}
- col1.add(obj.newNumberVector(part1));
- col2.add(obj.newNumberVector(part2));
+ col1.add(factory.newNumberVector(part1));
+ col2.add(factory.newNumberVector(part2));
}
}
return bundle;
@@ -137,10 +142,10 @@ public class SplitNumberVectorFilter<V extends NumberVector<V, ?>> implements Ob
private TypeInformation getInputTypeRestriction() {
// Find maximum dimension requested
int m = dims[0];
- for(int i = 1; i < dims.length; i++) {
+ for (int i = 1; i < dims.length; i++) {
m = Math.max(dims[i], m);
}
- return new VectorFieldTypeInformation<NumberVector<?, ?>>(NumberVector.class, m, Integer.MAX_VALUE);
+ return new VectorFieldTypeInformation<NumberVector<?>>(NumberVector.class, m, Integer.MAX_VALUE);
}
/**
@@ -150,11 +155,11 @@ public class SplitNumberVectorFilter<V extends NumberVector<V, ?>> implements Ob
*
* @apiviz.exclude
*/
- public static class Parameterizer<V extends NumberVector<V, ?>> extends AbstractParameterizer {
+ public static class Parameterizer<V extends NumberVector<?>> extends AbstractParameterizer {
/**
* The parameter listing the split dimensions.
*/
- public static final OptionID SELECTED_ATTRIBUTES_ID = OptionID.getOrCreateOptionID("split.dims", "Dimensions to split into the first relation.");
+ public static final OptionID SELECTED_ATTRIBUTES_ID = new OptionID("split.dims", "Dimensions to split into the first relation.");
/**
* Dimensions to use.
@@ -164,12 +169,13 @@ public class SplitNumberVectorFilter<V extends NumberVector<V, ?>> implements Ob
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- IntListParameter selectedAttributesP = new IntListParameter(SELECTED_ATTRIBUTES_ID, new ListGreaterEqualConstraint<Integer>(1));
- if(config.grab(selectedAttributesP)) {
+ IntListParameter selectedAttributesP = new IntListParameter(SELECTED_ATTRIBUTES_ID);
+ selectedAttributesP.addConstraint(new ListEachConstraint<Integer>(new GreaterEqualConstraint(0)));
+ if (config.grab(selectedAttributesP)) {
List<Integer> dimensionList = selectedAttributesP.getValue();
dims = new int[dimensionList.size()];
- for(int i = 0; i < dimensionList.size(); i++) {
- dims[i] = dimensionList.get(i);
+ for (int i = 0; i < dimensionList.size(); i++) {
+ dims[i] = dimensionList.get(i).intValue();
}
}
}
@@ -179,4 +185,4 @@ public class SplitNumberVectorFilter<V extends NumberVector<V, ?>> implements Ob
return new SplitNumberVectorFilter<V>(dims);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/filter/StreamFilter.java b/src/de/lmu/ifi/dbs/elki/datasource/filter/StreamFilter.java
index e40565f9..5d121659 100644
--- a/src/de/lmu/ifi/dbs/elki/datasource/filter/StreamFilter.java
+++ b/src/de/lmu/ifi/dbs/elki/datasource/filter/StreamFilter.java
@@ -31,6 +31,8 @@ import de.lmu.ifi.dbs.elki.datasource.bundle.BundleStreamSource;
*
* @author Erich Schubert
*
+ * @apiviz.landmark
+ *
* @apiviz.uses BundleStreamSource - - «filters»
*/
public interface StreamFilter extends ObjectFilter, BundleStreamSource {
@@ -40,4 +42,4 @@ public interface StreamFilter extends ObjectFilter, BundleStreamSource {
* @param source Stream source
*/
public void init(BundleStreamSource source);
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/filter/normalization/AbstractNormalization.java b/src/de/lmu/ifi/dbs/elki/datasource/filter/normalization/AbstractNormalization.java
index 5b6c02e3..2dcf09f8 100644
--- a/src/de/lmu/ifi/dbs/elki/datasource/filter/normalization/AbstractNormalization.java
+++ b/src/de/lmu/ifi/dbs/elki/datasource/filter/normalization/AbstractNormalization.java
@@ -23,9 +23,10 @@ package de.lmu.ifi.dbs.elki.datasource.filter.normalization;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.data.type.SimpleTypeInformation;
import de.lmu.ifi.dbs.elki.datasource.bundle.MultipleObjectsBundle;
-import de.lmu.ifi.dbs.elki.datasource.filter.AbstractConversionFilter;
+import de.lmu.ifi.dbs.elki.datasource.filter.AbstractVectorConversionFilter;
import de.lmu.ifi.dbs.elki.math.linearalgebra.LinearEquationSystem;
/**
@@ -35,7 +36,7 @@ import de.lmu.ifi.dbs.elki.math.linearalgebra.LinearEquationSystem;
*
* @param <O> Object type processed
*/
-public abstract class AbstractNormalization<O> extends AbstractConversionFilter<O, O> implements Normalization<O> {
+public abstract class AbstractNormalization<O extends NumberVector<?>> extends AbstractVectorConversionFilter<O, O> implements Normalization<O> {
/**
* Initializes the option handler and the parameter map.
*/
@@ -45,11 +46,12 @@ public abstract class AbstractNormalization<O> extends AbstractConversionFilter<
@Override
protected SimpleTypeInformation<? super O> convertedType(SimpleTypeInformation<O> in) {
+ initializeOutputType(in);
return in;
}
@Override
- public MultipleObjectsBundle normalizeObjects(MultipleObjectsBundle objects) throws NonNumericFeaturesException {
+ public MultipleObjectsBundle normalizeObjects(MultipleObjectsBundle objects) {
return super.filter(objects);
}
@@ -61,8 +63,6 @@ public abstract class AbstractNormalization<O> extends AbstractConversionFilter<
@Override
public String toString() {
- StringBuffer result = new StringBuffer();
- result.append("normalization class: ").append(getClass().getName());
- return result.toString();
+ return getClass().getName();
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/filter/normalization/AbstractStreamNormalization.java b/src/de/lmu/ifi/dbs/elki/datasource/filter/normalization/AbstractStreamNormalization.java
index c1524788..a1e2c55e 100644
--- a/src/de/lmu/ifi/dbs/elki/datasource/filter/normalization/AbstractStreamNormalization.java
+++ b/src/de/lmu/ifi/dbs/elki/datasource/filter/normalization/AbstractStreamNormalization.java
@@ -23,9 +23,10 @@ package de.lmu.ifi.dbs.elki.datasource.filter.normalization;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.data.type.SimpleTypeInformation;
import de.lmu.ifi.dbs.elki.datasource.bundle.MultipleObjectsBundle;
-import de.lmu.ifi.dbs.elki.datasource.filter.AbstractStreamConversionFilter;
+import de.lmu.ifi.dbs.elki.datasource.filter.AbstractVectorStreamConversionFilter;
import de.lmu.ifi.dbs.elki.math.linearalgebra.LinearEquationSystem;
/**
@@ -35,7 +36,7 @@ import de.lmu.ifi.dbs.elki.math.linearalgebra.LinearEquationSystem;
*
* @param <O> Object type processed
*/
-public abstract class AbstractStreamNormalization<O> extends AbstractStreamConversionFilter<O, O> implements Normalization<O> {
+public abstract class AbstractStreamNormalization<O extends NumberVector<?>> extends AbstractVectorStreamConversionFilter<O, O> implements Normalization<O> {
/**
* Initializes the option handler and the parameter map.
*/
@@ -45,11 +46,12 @@ public abstract class AbstractStreamNormalization<O> extends AbstractStreamConve
@Override
protected SimpleTypeInformation<? super O> convertedType(SimpleTypeInformation<O> in) {
+ initializeOutputType(in);
return in;
}
@Override
- public MultipleObjectsBundle normalizeObjects(MultipleObjectsBundle objects) throws NonNumericFeaturesException {
+ public MultipleObjectsBundle normalizeObjects(MultipleObjectsBundle objects) {
return super.filter(objects);
}
@@ -61,7 +63,7 @@ public abstract class AbstractStreamNormalization<O> extends AbstractStreamConve
@Override
public String toString() {
- StringBuffer result = new StringBuffer();
+ StringBuilder result = new StringBuilder();
result.append("normalization class: ").append(getClass().getName());
return result.toString();
}
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/filter/normalization/AttributeWiseErfNormalization.java b/src/de/lmu/ifi/dbs/elki/datasource/filter/normalization/AttributeWiseErfNormalization.java
index c0f2a955..f5e24bca 100644
--- a/src/de/lmu/ifi/dbs/elki/datasource/filter/normalization/AttributeWiseErfNormalization.java
+++ b/src/de/lmu/ifi/dbs/elki/datasource/filter/normalization/AttributeWiseErfNormalization.java
@@ -38,7 +38,7 @@ import de.lmu.ifi.dbs.elki.math.statistics.distribution.NormalDistribution;
*
* @apiviz.uses NumberVector
*/
-public class AttributeWiseErfNormalization<O extends NumberVector<O, ?>> extends AbstractNormalization<O> {
+public class AttributeWiseErfNormalization<O extends NumberVector<?>> extends AbstractNormalization<O> {
/**
* Constructor.
*/
@@ -47,7 +47,7 @@ public class AttributeWiseErfNormalization<O extends NumberVector<O, ?>> extends
}
@Override
- public O restore(O featureVector) throws NonNumericFeaturesException {
+ public O restore(O featureVector) {
throw new UnsupportedOperationException("Not implemented yet.");
}
@@ -55,9 +55,9 @@ public class AttributeWiseErfNormalization<O extends NumberVector<O, ?>> extends
protected O filterSingleObject(O obj) {
double[] val = new double[obj.getDimensionality()];
for(int i = 0; i < val.length; i++) {
- val[i] = NormalDistribution.erf(obj.doubleValue(i + 1));
+ val[i] = NormalDistribution.erf(obj.doubleValue(i));
}
- return obj.newNumberVector(val);
+ return factory.newNumberVector(val);
}
@Override
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/filter/normalization/AttributeWiseMinMaxNormalization.java b/src/de/lmu/ifi/dbs/elki/datasource/filter/normalization/AttributeWiseMinMaxNormalization.java
index 4cf3c606..62c0bf12 100644
--- a/src/de/lmu/ifi/dbs/elki/datasource/filter/normalization/AttributeWiseMinMaxNormalization.java
+++ b/src/de/lmu/ifi/dbs/elki/datasource/filter/normalization/AttributeWiseMinMaxNormalization.java
@@ -50,16 +50,16 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.Parameter;
* @apiviz.uses NumberVector
*/
// TODO: extract superclass AbstractAttributeWiseNormalization
-public class AttributeWiseMinMaxNormalization<V extends NumberVector<V, ?>> extends AbstractNormalization<V> {
+public class AttributeWiseMinMaxNormalization<V extends NumberVector<?>> extends AbstractNormalization<V> {
/**
* Parameter for minimum.
*/
- public static final OptionID MINIMA_ID = OptionID.getOrCreateOptionID("normalize.min", "a comma separated concatenation of the minimum values in each dimension that are mapped to 0. If no value is specified, the minimum value of the attribute range in this dimension will be taken.");
+ public static final OptionID MINIMA_ID = new OptionID("normalize.min", "a comma separated concatenation of the minimum values in each dimension that are mapped to 0. If no value is specified, the minimum value of the attribute range in this dimension will be taken.");
/**
* Parameter for maximum.
*/
- public static final OptionID MAXIMA_ID = OptionID.getOrCreateOptionID("normalize.max", "a comma separated concatenation of the maximum values in each dimension that are mapped to 1. If no value is specified, the maximum value of the attribute range in this dimension will be taken.");
+ public static final OptionID MAXIMA_ID = new OptionID("normalize.max", "a comma separated concatenation of the maximum values in each dimension that are mapped to 1. If no value is specified, the maximum value of the attribute range in this dimension will be taken.");
/**
* Stores the maximum in each dimension.
@@ -103,13 +103,13 @@ public class AttributeWiseMinMaxNormalization<V extends NumberVector<V, ?>> exte
if(minima.length != featureVector.getDimensionality()) {
throw new IllegalArgumentException("FeatureVectors differ in length.");
}
- for(int d = 1; d <= featureVector.getDimensionality(); d++) {
+ for(int d = 0; d < featureVector.getDimensionality(); d++) {
final double val = featureVector.doubleValue(d);
- if(val > maxima[d - 1]) {
- maxima[d - 1] = val;
+ if(val > maxima[d]) {
+ maxima[d] = val;
}
- if(val < minima[d - 1]) {
- minima[d - 1] = val;
+ if(val < minima[d]) {
+ minima[d] = val;
}
}
}
@@ -120,20 +120,20 @@ public class AttributeWiseMinMaxNormalization<V extends NumberVector<V, ?>> exte
if(minima.length != featureVector.getDimensionality()) {
throw new IllegalArgumentException("FeatureVectors and given Minima/Maxima differ in length.");
}
- for(int d = 1; d <= featureVector.getDimensionality(); d++) {
- values[d - 1] = (featureVector.doubleValue(d) - minima[d - 1]) / factor(d);
+ for(int d = 0; d < featureVector.getDimensionality(); d++) {
+ values[d] = (featureVector.doubleValue(d) - minima[d]) / factor(d);
}
- return featureVector.newNumberVector(values);
+ return factory.newNumberVector(values);
}
@Override
public V restore(V featureVector) throws NonNumericFeaturesException {
if(featureVector.getDimensionality() == maxima.length && featureVector.getDimensionality() == minima.length) {
double[] values = new double[featureVector.getDimensionality()];
- for(int d = 1; d <= featureVector.getDimensionality(); d++) {
- values[d - 1] = (featureVector.doubleValue(d) * (factor(d)) + minima[d - 1]);
+ for(int d = 0; d < featureVector.getDimensionality(); d++) {
+ values[d] = (featureVector.doubleValue(d) * (factor(d)) + minima[d]);
}
- return featureVector.newNumberVector(values);
+ return factory.newNumberVector(values);
}
else {
throw new NonNumericFeaturesException("Attributes cannot be resized: current dimensionality: " + featureVector.getDimensionality() + " former dimensionality: " + maxima.length);
@@ -151,7 +151,7 @@ public class AttributeWiseMinMaxNormalization<V extends NumberVector<V, ?>> exte
* @return a factor for normalization in a certain dimension
*/
private double factor(int dimension) {
- return maxima[dimension - 1] != minima[dimension - 1] ? maxima[dimension - 1] - minima[dimension - 1] : maxima[dimension - 1] != 0 ? maxima[dimension - 1] : 1;
+ return maxima[dimension] > minima[dimension] ? maxima[dimension] - minima[dimension] : maxima[dimension] > 0 ? maxima[dimension] : 1;
}
@Override
@@ -161,13 +161,12 @@ public class AttributeWiseMinMaxNormalization<V extends NumberVector<V, ?>> exte
int[] row = linearEquationSystem.getRowPermutations();
int[] col = linearEquationSystem.getColumnPermutations();
- // noinspection ForLoopReplaceableByForEach
for(int i = 0; i < coeff.length; i++) {
for(int r = 0; r < coeff.length; r++) {
double sum = 0.0;
for(int c = 0; c < coeff[0].length; c++) {
- sum += minima[c] * coeff[row[r]][col[c]] / factor(c + 1);
- coeff[row[r]][col[c]] = coeff[row[r]][col[c]] / factor(c + 1);
+ sum += minima[c] * coeff[row[r]][col[c]] / factor(c);
+ coeff[row[r]][col[c]] = coeff[row[r]][col[c]] / factor(c);
}
rhs[row[r]] = rhs[row[r]] + sum;
}
@@ -179,11 +178,11 @@ public class AttributeWiseMinMaxNormalization<V extends NumberVector<V, ?>> exte
@Override
public String toString() {
- StringBuffer result = new StringBuffer();
+ StringBuilder result = new StringBuilder();
result.append("normalization class: ").append(getClass().getName());
- result.append("\n");
+ result.append('\n');
result.append("normalization minima: ").append(FormatUtil.format(minima));
- result.append("\n");
+ result.append('\n');
result.append("normalization maxima: ").append(FormatUtil.format(maxima));
return result.toString();
}
@@ -200,7 +199,7 @@ public class AttributeWiseMinMaxNormalization<V extends NumberVector<V, ?>> exte
*
* @apiviz.exclude
*/
- public static class Parameterizer<V extends NumberVector<V, ?>> extends AbstractParameterizer {
+ public static class Parameterizer<V extends NumberVector<?>> extends AbstractParameterizer {
/**
* Stores the maximum in each dimension.
*/
@@ -223,7 +222,7 @@ public class AttributeWiseMinMaxNormalization<V extends NumberVector<V, ?>> exte
maxima = ArrayLikeUtil.toPrimitiveDoubleArray(maximaP.getValue());
}
- ArrayList<Parameter<?, ?>> global_1 = new ArrayList<Parameter<?, ?>>();
+ ArrayList<Parameter<?>> global_1 = new ArrayList<Parameter<?>>();
global_1.add(minimaP);
global_1.add(maximaP);
config.checkConstraint(new AllOrNoneMustBeSetGlobalConstraint(global_1));
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/filter/normalization/AttributeWiseVarianceNormalization.java b/src/de/lmu/ifi/dbs/elki/datasource/filter/normalization/AttributeWiseVarianceNormalization.java
index 52a0499f..0671231d 100644
--- a/src/de/lmu/ifi/dbs/elki/datasource/filter/normalization/AttributeWiseVarianceNormalization.java
+++ b/src/de/lmu/ifi/dbs/elki/datasource/filter/normalization/AttributeWiseVarianceNormalization.java
@@ -53,21 +53,21 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.Parameter;
* @apiviz.uses NumberVector
*/
// TODO: extract superclass AbstractAttributeWiseNormalization
-public class AttributeWiseVarianceNormalization<V extends NumberVector<V, ?>> extends AbstractNormalization<V> {
+public class AttributeWiseVarianceNormalization<V extends NumberVector<?>> extends AbstractNormalization<V> {
/**
* Class logger.
*/
- public static final Logging logger = Logging.getLogger(AttributeWiseVarianceNormalization.class);
+ private static final Logging LOG = Logging.getLogger(AttributeWiseVarianceNormalization.class);
/**
* Parameter for means.
*/
- public static final OptionID MEAN_ID = OptionID.getOrCreateOptionID("normalize.mean", "a comma separated concatenation of the mean values in each dimension that are mapped to 0. If no value is specified, the mean value of the attribute range in this dimension will be taken.");
+ public static final OptionID MEAN_ID = new OptionID("normalize.mean", "a comma separated concatenation of the mean values in each dimension that are mapped to 0. If no value is specified, the mean value of the attribute range in this dimension will be taken.");
/**
* Parameter for stddevs.
*/
- public static final OptionID STDDEV_ID = OptionID.getOrCreateOptionID("normalize.stddev", "a comma separated concatenation of the standard deviations in each dimension that are scaled to 1. If no value is specified, the standard deviation of the attribute range in this dimension will be taken.");
+ public static final OptionID STDDEV_ID = new OptionID("normalize.stddev", "a comma separated concatenation of the standard deviations in each dimension that are scaled to 1. If no value is specified, the standard deviation of the attribute range in this dimension will be taken.");
/**
* Stores the mean in each dimension.
@@ -108,14 +108,14 @@ public class AttributeWiseVarianceNormalization<V extends NumberVector<V, ?>> ex
int dimensionality = featureVector.getDimensionality();
mvs = MeanVariance.newArray(dimensionality);
}
- for(int d = 1; d <= featureVector.getDimensionality(); d++) {
- mvs[d - 1].put(featureVector.doubleValue(d));
+ for(int d = 0; d < featureVector.getDimensionality(); d++) {
+ mvs[d].put(featureVector.doubleValue(d));
}
}
@Override
protected void prepareComplete() {
- StringBuffer buf = logger.isVerbose() ? new StringBuffer() : null;
+ StringBuilder buf = LOG.isVerbose() ? new StringBuilder() : null;
final int dimensionality = mvs.length;
mean = new double[dimensionality];
stddev = new double[dimensionality];
@@ -134,33 +134,40 @@ public class AttributeWiseVarianceNormalization<V extends NumberVector<V, ?>> ex
}
mvs = null;
if(buf != null) {
- logger.debugFine(buf.toString());
+ LOG.debugFine(buf.toString());
}
}
@Override
protected V filterSingleObject(V featureVector) {
double[] values = new double[featureVector.getDimensionality()];
- for(int d = 1; d <= featureVector.getDimensionality(); d++) {
- values[d - 1] = normalize(d - 1, featureVector.doubleValue(d));
+ for(int d = 0; d < featureVector.getDimensionality(); d++) {
+ values[d] = normalize(d, featureVector.doubleValue(d));
}
- return featureVector.newNumberVector(values);
+ return factory.newNumberVector(values);
}
@Override
public V restore(V featureVector) throws NonNumericFeaturesException {
if(featureVector.getDimensionality() == mean.length) {
double[] values = new double[featureVector.getDimensionality()];
- for(int d = 1; d <= featureVector.getDimensionality(); d++) {
- values[d - 1] = restore(d - 1, featureVector.doubleValue(d));
+ for(int d = 0; d < featureVector.getDimensionality(); d++) {
+ values[d] = restore(d, featureVector.doubleValue(d));
}
- return featureVector.newNumberVector(values);
+ return factory.newNumberVector(values);
}
else {
throw new NonNumericFeaturesException("Attributes cannot be resized: current dimensionality: " + featureVector.getDimensionality() + " former dimensionality: " + mean.length);
}
}
+ /**
+ * Normalize a single dimension.
+ *
+ * @param d Dimension
+ * @param val Value
+ * @return Normalized value
+ */
private double normalize(int d, double val) {
if(mean.length == 1) {
return (val - mean[0]) / stddev[0];
@@ -170,6 +177,12 @@ public class AttributeWiseVarianceNormalization<V extends NumberVector<V, ?>> ex
}
}
+ /**
+ * Restore a single dimension.
+ * @param d Dimension
+ * @param val Value
+ * @return Normalized value
+ */
private double restore(int d, double val) {
if(mean.length == 1) {
return (val * stddev[0]) + mean[0];
@@ -208,11 +221,11 @@ public class AttributeWiseVarianceNormalization<V extends NumberVector<V, ?>> ex
@Override
public String toString() {
- StringBuffer result = new StringBuffer();
+ StringBuilder result = new StringBuilder();
result.append("normalization class: ").append(getClass().getName());
- result.append("\n");
+ result.append('\n');
result.append("normalization means: ").append(FormatUtil.format(mean));
- result.append("\n");
+ result.append('\n');
result.append("normalization stddevs: ").append(FormatUtil.format(stddev));
return result.toString();
@@ -225,7 +238,7 @@ public class AttributeWiseVarianceNormalization<V extends NumberVector<V, ?>> ex
*
* @apiviz.exclude
*/
- public static class Parameterizer<V extends NumberVector<V, ?>> extends AbstractParameterizer {
+ public static class Parameterizer<V extends NumberVector<?>> extends AbstractParameterizer {
/**
* Stores the mean in each dimension.
*/
@@ -255,7 +268,7 @@ public class AttributeWiseVarianceNormalization<V extends NumberVector<V, ?>> ex
}
}
- ArrayList<Parameter<?, ?>> global_1 = new ArrayList<Parameter<?, ?>>();
+ ArrayList<Parameter<?>> global_1 = new ArrayList<Parameter<?>>();
global_1.add(meanP);
global_1.add(stddevP);
config.checkConstraint(new AllOrNoneMustBeSetGlobalConstraint(global_1));
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/filter/normalization/InverseDocumentFrequencyNormalization.java b/src/de/lmu/ifi/dbs/elki/datasource/filter/normalization/InverseDocumentFrequencyNormalization.java
index 9350426b..24f3a850 100644
--- a/src/de/lmu/ifi/dbs/elki/datasource/filter/normalization/InverseDocumentFrequencyNormalization.java
+++ b/src/de/lmu/ifi/dbs/elki/datasource/filter/normalization/InverseDocumentFrequencyNormalization.java
@@ -40,15 +40,17 @@ import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
* @author Erich Schubert
*
* @apiviz.uses SparseNumberVector
+ *
+ * @param <V> Vector type
*/
-public class InverseDocumentFrequencyNormalization<V extends SparseNumberVector<V, ?>> extends AbstractNormalization<V> {
+public class InverseDocumentFrequencyNormalization<V extends SparseNumberVector<?>> extends AbstractNormalization<V> {
/**
- * The IDF storage
+ * The IDF storage.
*/
TIntDoubleMap idf = new TIntDoubleHashMap();
/**
- * The number of objects in the dataset
+ * The number of objects in the dataset.
*/
int objcnt = 0;
@@ -73,13 +75,7 @@ public class InverseDocumentFrequencyNormalization<V extends SparseNumberVector<
BitSet b = featureVector.getNotNullMask();
for(int i = b.nextSetBit(0); i >= 0; i = b.nextSetBit(i + 1)) {
if(featureVector.doubleValue(i) >= 0.0) {
- Number c = idf.get(i);
- if(c == null) {
- idf.put(i, 1);
- }
- else {
- idf.put(i, c.intValue() + 1);
- }
+ idf.put(i, idf.get(i) + 1);
}
}
objcnt += 1;
@@ -103,7 +99,7 @@ public class InverseDocumentFrequencyNormalization<V extends SparseNumberVector<
for(int i = b.nextSetBit(0); i >= 0; i = b.nextSetBit(i + 1)) {
vals.put(i, (float) (featureVector.doubleValue(i) * idf.get(i)));
}
- return featureVector.newNumberVector(vals, featureVector.getDimensionality());
+ return ((SparseNumberVector.Factory<V, ?>) factory).newNumberVector(vals, featureVector.getDimensionality());
}
@Override
@@ -113,7 +109,7 @@ public class InverseDocumentFrequencyNormalization<V extends SparseNumberVector<
for(int i = b.nextSetBit(0); i >= 0; i = b.nextSetBit(i + 1)) {
vals.put(i, (float) (featureVector.doubleValue(i) / idf.get(i)));
}
- return featureVector.newNumberVector(vals, featureVector.getDimensionality());
+ return ((SparseNumberVector.Factory<V, ?>) factory).newNumberVector(vals, featureVector.getDimensionality());
}
@Override
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/filter/normalization/LengthNormalization.java b/src/de/lmu/ifi/dbs/elki/datasource/filter/normalization/LengthNormalization.java
index 2edeebf9..457cc6eb 100644
--- a/src/de/lmu/ifi/dbs/elki/datasource/filter/normalization/LengthNormalization.java
+++ b/src/de/lmu/ifi/dbs/elki/datasource/filter/normalization/LengthNormalization.java
@@ -42,14 +42,14 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ObjectParameter;
*
* @param <V> vector type
*/
-public class LengthNormalization<V extends NumberVector<V, ?>> extends AbstractStreamNormalization<V> {
+public class LengthNormalization<V extends NumberVector<?>> extends AbstractStreamNormalization<V> {
/**
- * Norm to use
+ * Norm to use.
*/
DoubleNorm<? super V> norm;
/**
- * Constructor
+ * Constructor.
*
* @param norm Norm to use
*/
@@ -61,11 +61,11 @@ public class LengthNormalization<V extends NumberVector<V, ?>> extends AbstractS
@Override
protected V filterSingleObject(V featureVector) {
final double d = norm.doubleNorm(featureVector);
- return featureVector.newNumberVector(featureVector.getColumnVector().timesEquals(1 / d).getArrayRef());
+ return factory.newNumberVector(featureVector.getColumnVector().timesEquals(1 / d).getArrayRef());
}
@Override
- public V restore(V featureVector) throws NonNumericFeaturesException {
+ public V restore(V featureVector) {
throw new UnsupportedOperationException();
}
@@ -87,14 +87,14 @@ public class LengthNormalization<V extends NumberVector<V, ?>> extends AbstractS
*
* @apiviz.exclude
*/
- public static class Parameterizer<V extends NumberVector<V, ?>> extends AbstractParameterizer {
+ public static class Parameterizer<V extends NumberVector<?>> extends AbstractParameterizer {
/**
- * Option ID for normalization norm
+ * Option ID for normalization norm.
*/
- public static final OptionID NORM_ID = OptionID.getOrCreateOptionID("normalization.norm", "Norm (length function) to use for computing the vector length.");
+ public static final OptionID NORM_ID = new OptionID("normalization.norm", "Norm (length function) to use for computing the vector length.");
/**
- * Norm to use
+ * Norm to use.
*/
DoubleNorm<? super V> norm;
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/filter/normalization/RankTieNormalization.java b/src/de/lmu/ifi/dbs/elki/datasource/filter/normalization/RankTieNormalization.java
index be8c1166..519a3743 100644
--- a/src/de/lmu/ifi/dbs/elki/datasource/filter/normalization/RankTieNormalization.java
+++ b/src/de/lmu/ifi/dbs/elki/datasource/filter/normalization/RankTieNormalization.java
@@ -65,10 +65,10 @@ public class RankTieNormalization implements ObjectFilter {
continue;
}
@SuppressWarnings("unchecked")
- final List<? extends NumberVector<?, ?>> castColumn = (List<? extends NumberVector<?, ?>>) column;
+ final List<? extends NumberVector<?>> castColumn = (List<? extends NumberVector<?>>) column;
// Get the replacement type information
- final int dim = ((VectorFieldTypeInformation<?>) type).dimensionality();
- final VectorFieldTypeInformation<IntegerVector> outType = new VectorFieldTypeInformation<IntegerVector>(IntegerVector.class, dim, IntegerVector.STATIC);
+ final int dim = ((VectorFieldTypeInformation<?>) type).getDimensionality();
+ final VectorFieldTypeInformation<IntegerVector> outType = new VectorFieldTypeInformation<IntegerVector>(IntegerVector.STATIC, dim);
// Output vectors
int[][] posvecs = new int[len][dim];
@@ -78,7 +78,7 @@ public class RankTieNormalization implements ObjectFilter {
for(int i = 0; i < sorter.length; i++) {
sorter[i] = new DoubleIntPair(Double.NaN, -1);
}
- for(int d = 1; d <= dim; d++) {
+ for(int d = 0; d < dim; d++) {
// fill array
for(int i = 0; i < sorter.length; i++) {
sorter[i].first = castColumn.get(i).doubleValue(d);
@@ -90,12 +90,12 @@ public class RankTieNormalization implements ObjectFilter {
for(int sta = 0; sta < sorter.length;) {
// Compute ties
int end = sta + 1;
- while(end < sorter.length && sorter[sta].first == sorter[end].first) {
+ while(end < sorter.length && !(sorter[sta].first < sorter[end].first)) {
end++;
}
final int pos = (sta + end - 1);
for(int i = sta; i < end; i++) {
- posvecs[sorter[i].second][d - 1] = pos;
+ posvecs[sorter[i].second][d] = pos;
}
sta = end;
}
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/filter/normalization/TFIDFNormalization.java b/src/de/lmu/ifi/dbs/elki/datasource/filter/normalization/TFIDFNormalization.java
index 031cfb4c..5d203c6b 100644
--- a/src/de/lmu/ifi/dbs/elki/datasource/filter/normalization/TFIDFNormalization.java
+++ b/src/de/lmu/ifi/dbs/elki/datasource/filter/normalization/TFIDFNormalization.java
@@ -39,8 +39,10 @@ import de.lmu.ifi.dbs.elki.data.SparseNumberVector;
* Restore will only undo the IDF part of the normalization!
*
* @author Erich Schubert
+ *
+ * @param <V> Vector type
*/
-public class TFIDFNormalization<V extends SparseNumberVector<V, ?>> extends InverseDocumentFrequencyNormalization<V> {
+public class TFIDFNormalization<V extends SparseNumberVector<?>> extends InverseDocumentFrequencyNormalization<V> {
/**
* Constructor.
*/
@@ -62,6 +64,6 @@ public class TFIDFNormalization<V extends SparseNumberVector<V, ?>> extends Inve
for(int i = b.nextSetBit(0); i >= 0; i = b.nextSetBit(i + 1)) {
vals.put(i, (float) (featureVector.doubleValue(i) / sum * idf.get(i)));
}
- return featureVector.newNumberVector(vals, featureVector.getDimensionality());
+ return ((SparseNumberVector.Factory<V, ?>) factory).newNumberVector(vals, featureVector.getDimensionality());
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/filter/package-info.java b/src/de/lmu/ifi/dbs/elki/datasource/filter/package-info.java
index ca52f814..82302cd3 100644
--- a/src/de/lmu/ifi/dbs/elki/datasource/filter/package-info.java
+++ b/src/de/lmu/ifi/dbs/elki/datasource/filter/package-info.java
@@ -1,5 +1,7 @@
/**
* <p>Data filtering, in particular for normalization and projection.</p>
+ *
+ * @apiviz.exclude de.lmu.ifi.dbs.elki.utilities.*
*/
/*
This file is part of ELKI:
@@ -23,4 +25,4 @@ GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package de.lmu.ifi.dbs.elki.datasource.filter; \ No newline at end of file
+package de.lmu.ifi.dbs.elki.datasource.filter;
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/filter/transform/GlobalPrincipalComponentAnalysisTransform.java b/src/de/lmu/ifi/dbs/elki/datasource/filter/transform/GlobalPrincipalComponentAnalysisTransform.java
index afa21fa6..18537a8d 100644
--- a/src/de/lmu/ifi/dbs/elki/datasource/filter/transform/GlobalPrincipalComponentAnalysisTransform.java
+++ b/src/de/lmu/ifi/dbs/elki/datasource/filter/transform/GlobalPrincipalComponentAnalysisTransform.java
@@ -23,19 +23,26 @@ package de.lmu.ifi.dbs.elki.datasource.filter.transform;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+import java.util.List;
+
import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.data.type.SimpleTypeInformation;
import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
import de.lmu.ifi.dbs.elki.data.type.VectorFieldTypeInformation;
-import de.lmu.ifi.dbs.elki.datasource.filter.AbstractConversionFilter;
+import de.lmu.ifi.dbs.elki.datasource.filter.AbstractVectorConversionFilter;
+import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.math.linearalgebra.CovarianceMatrix;
import de.lmu.ifi.dbs.elki.math.linearalgebra.EigenPair;
import de.lmu.ifi.dbs.elki.math.linearalgebra.SortedEigenPairs;
import de.lmu.ifi.dbs.elki.math.linearalgebra.VMath;
+import de.lmu.ifi.dbs.elki.math.linearalgebra.pca.EigenPairFilter;
import de.lmu.ifi.dbs.elki.math.linearalgebra.pca.PCAResult;
import de.lmu.ifi.dbs.elki.math.linearalgebra.pca.PCARunner;
import de.lmu.ifi.dbs.elki.utilities.exceptions.AbortException;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ObjectParameter;
/**
* Apply principal component analysis to the data set.
@@ -46,22 +53,50 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
*
* @param <O> Vector type
*/
-public class GlobalPrincipalComponentAnalysisTransform<O extends NumberVector<O, ?>> extends AbstractConversionFilter<O, O> {
+public class GlobalPrincipalComponentAnalysisTransform<O extends NumberVector<?>> extends AbstractVectorConversionFilter<O, O> {
+ /**
+ * Class logger.
+ */
+ private static final Logging LOG = Logging.getLogger(GlobalPrincipalComponentAnalysisTransform.class);
+
+ /**
+ * Filter to use for dimensionality reduction.
+ */
+ EigenPairFilter filter = null;
+
+ /**
+ * Actual dataset dimensionality.
+ */
int dim = -1;
+ /**
+ * Covariance matrix builder.
+ */
CovarianceMatrix covmat = null;
+ /**
+ * Final projection after analysis run.
+ */
double[][] proj = null;
+ /**
+ * Projection buffer.
+ */
double[] buf = null;
+ /**
+ * Vector for data set centering.
+ */
double[] mean = null;
/**
* Constructor.
+ *
+ * @param filter Filter to use for dimensionality reduction.
*/
- public GlobalPrincipalComponentAnalysisTransform() {
+ public GlobalPrincipalComponentAnalysisTransform(EigenPairFilter filter) {
super();
+ this.filter = filter;
}
@Override
@@ -69,7 +104,7 @@ public class GlobalPrincipalComponentAnalysisTransform<O extends NumberVector<O,
if(!(in instanceof VectorFieldTypeInformation)) {
throw new AbortException("PCA can only applied to fixed dimensionality vectors");
}
- dim = ((VectorFieldTypeInformation<?>) in).dimensionality();
+ dim = ((VectorFieldTypeInformation<?>) in).getDimensionality();
covmat = new CovarianceMatrix(dim);
return true;
}
@@ -81,19 +116,38 @@ public class GlobalPrincipalComponentAnalysisTransform<O extends NumberVector<O,
@Override
protected void prepareComplete() {
- mean = covmat.getMeanVector().getArrayRef();
+ mean = covmat.getMeanVector().getArrayRef();
PCAResult pcares = (new PCARunner<O>(null)).processCovarMatrix(covmat.destroyToSampleMatrix());
SortedEigenPairs eps = pcares.getEigenPairs();
covmat = null;
- proj = new double[dim][dim];
- for(int d = 0; d < dim; d++) {
- EigenPair ep = eps.getEigenPair(d);
- double[] ev = ep.getEigenvector().getArrayRef();
- double eval = Math.sqrt(ep.getEigenvalue());
- // Fill weighted and transposed:
- for(int i = 0; i < dim; i++) {
- proj[d][i] = ev[i] / eval;
+ if(filter == null) {
+ proj = new double[dim][dim];
+ for(int d = 0; d < dim; d++) {
+ EigenPair ep = eps.getEigenPair(d);
+ double[] ev = ep.getEigenvector().getArrayRef();
+ double eval = Math.sqrt(ep.getEigenvalue());
+ // Fill weighted and transposed:
+ for(int i = 0; i < dim; i++) {
+ proj[d][i] = ev[i] / eval;
+ }
+ }
+ }
+ else {
+ List<EigenPair> axes = filter.filter(eps).getStrongEigenPairs();
+ final int pdim = axes.size(); // Projection dimensionality
+ if (LOG.isVerbose()) {
+ LOG.verbose("Reducing dimensionality from "+dim+" to "+pdim+" via PCA.");
+ }
+ proj = new double[pdim][dim];
+ for(int d = 0; d < pdim; d++) {
+ EigenPair ep = axes.get(d);
+ double[] ev = ep.getEigenvector().getArrayRef();
+ double eval = Math.sqrt(ep.getEigenvalue());
+ // Fill weighted and transposed:
+ for(int i = 0; i < dim; i++) {
+ proj[d][i] = ev[i] / eval;
+ }
}
}
buf = new double[dim];
@@ -103,10 +157,10 @@ public class GlobalPrincipalComponentAnalysisTransform<O extends NumberVector<O,
protected O filterSingleObject(O obj) {
// Shift by mean and copy
for(int i = 0; i < dim; i++) {
- buf[i] = obj.doubleValue(i + 1) - mean[i];
+ buf[i] = obj.doubleValue(i) - mean[i];
}
double[] p = VMath.times(proj, buf);
- return obj.newNumberVector(p);
+ return factory.newNumberVector(p);
}
@Override
@@ -116,7 +170,13 @@ public class GlobalPrincipalComponentAnalysisTransform<O extends NumberVector<O,
@Override
protected SimpleTypeInformation<? super O> convertedType(SimpleTypeInformation<O> in) {
- return in;
+ initializeOutputType(in);
+ if(proj.length == dim) {
+ return in;
+ }
+ else {
+ return new VectorFieldTypeInformation<O>(factory, proj.length);
+ }
}
/**
@@ -126,10 +186,30 @@ public class GlobalPrincipalComponentAnalysisTransform<O extends NumberVector<O,
*
* @apiviz.exclude
*/
- public static class Parameterizer<O extends NumberVector<O, ?>> extends AbstractParameterizer {
+ public static class Parameterizer<O extends NumberVector<?>> extends AbstractParameterizer {
+ /**
+ * To specify the eigenvectors to keep.
+ */
+ public static final OptionID FILTER_ID = new OptionID("globalpca.filter", "Filter to use for dimensionality reduction.");
+
+ /**
+ * Filter to use for dimensionality reduction.
+ */
+ EigenPairFilter filter = null;
+
+ @Override
+ protected void makeOptions(Parameterization config) {
+ super.makeOptions(config);
+
+ ObjectParameter<EigenPairFilter> filterP = new ObjectParameter<EigenPairFilter>(FILTER_ID, EigenPairFilter.class, true);
+ if(config.grab(filterP)) {
+ filter = filterP.instantiateClass(config);
+ }
+ }
+
@Override
protected Object makeInstance() {
- return new GlobalPrincipalComponentAnalysisTransform<O>();
+ return new GlobalPrincipalComponentAnalysisTransform<O>(filter);
}
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/filter/AbstractFeatureSelectionFilter.java b/src/de/lmu/ifi/dbs/elki/datasource/filter/transform/NumberVectorFeatureSelectionFilter.java
index 009296b1..82e7a1b6 100644
--- a/src/de/lmu/ifi/dbs/elki/datasource/filter/AbstractFeatureSelectionFilter.java
+++ b/src/de/lmu/ifi/dbs/elki/datasource/filter/transform/NumberVectorFeatureSelectionFilter.java
@@ -1,136 +1,168 @@
-package de.lmu.ifi.dbs.elki.datasource.filter;
-
-/*
- This file is part of ELKI:
+package de.lmu.ifi.dbs.elki.datasource.filter.transform;
+
+/*
+ This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
-
- Copyright (C) 2012
- Ludwig-Maximilians-Universität München
- Lehr- und Forschungseinheit für Datenbanksysteme
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-import java.util.BitSet;
-import java.util.List;
-
-import de.lmu.ifi.dbs.elki.data.FeatureVector;
-import de.lmu.ifi.dbs.elki.data.NumberVector;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.ListGreaterEqualConstraint;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntListParameter;
-
-/**
- * <p>
- * A ProjectionParser projects the objects of its base parser onto a subspace
- * specified by a BitSet.
- * </p>
- *
- * @author Arthur Zimek
- * @author Erich Schubert
- * @param <V> the type of FeatureVector contained in both the original and
- * projected data.
- */
-public abstract class AbstractFeatureSelectionFilter<V extends FeatureVector<?, ?>> extends AbstractStreamConversionFilter<V, V> {
- /**
- * <p>
- * Selected attributes parameter.
- * </p>
- * <p>
- * Key: <code>-projectionfilter.selectedattributes</code>
- * </p>
- */
- public static final OptionID SELECTED_ATTRIBUTES_ID = OptionID.getOrCreateOptionID("projectionfilter.selectedattributes", "a comma separated array of integer values d_i, where 1 <= d_i <= the " + "dimensionality of the feature space " + "specifying the dimensions to be considered " + "for projection. If this parameter is not set, " + "no dimensions will be considered, i.e. the projection is a zero-dimensional feature space");
-
- /**
- * Keeps the selection of the subspace to project onto.
- */
- private BitSet selectedAttributes;
-
- /**
- * Constructor.
- *
- * @param selectedAttributes
- */
- public AbstractFeatureSelectionFilter(BitSet selectedAttributes) {
- super();
- this.selectedAttributes = selectedAttributes;
- }
-
- /**
- * <p>
- * Sets the bits set to true in the given BitSet as selected attributes in
- * {@link #SELECTED_ATTRIBUTES_ID}.
- * </p>
- *
- * The index in the BitSet is expected to be shifted to the left by one, i.e.,
- * index 0 in the BitSet relates to the first attribute.
- *
- * @param selectedAttributes the new selected attributes
- */
- public void setSelectedAttributes(BitSet selectedAttributes) {
- this.selectedAttributes.or(selectedAttributes);
- }
-
- /**
- * <p>
- * Provides a BitSet with the bits set to true corresponding to the selected
- * attributes in {@link #SELECTED_ATTRIBUTES_ID}.
- * </p>
- *
- * The index in the BitSet is shifted to the left by one, i.e., index 0 in the
- * BitSet relates to the first attribute.
- *
- * @return the selected attributes
- */
- public BitSet getSelectedAttributes() {
- return selectedAttributes;
- }
-
- /**
- * Get the resulting dimensionality.
- *
- * @return dimensionality
- */
- public int getDimensionality() {
- return selectedAttributes.cardinality();
- }
-
- /**
- * Parameterization class.
- *
- * @author Erich Schubert
- *
- * @apiviz.exclude
- */
- public static abstract class Parameterizer<V extends NumberVector<V, ?>> extends AbstractParameterizer {
- protected BitSet selectedAttributes = null;
-
- @Override
- protected void makeOptions(Parameterization config) {
- super.makeOptions(config);
- IntListParameter selectedAttributesP = new IntListParameter(SELECTED_ATTRIBUTES_ID, new ListGreaterEqualConstraint<Integer>(1));
- if(config.grab(selectedAttributesP)) {
- selectedAttributes = new BitSet();
- List<Integer> dimensionList = selectedAttributesP.getValue();
- for(int d : dimensionList) {
- selectedAttributes.set(d - 1);
- }
- }
- }
- }
-} \ No newline at end of file
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.BitSet;
+import java.util.List;
+
+import de.lmu.ifi.dbs.elki.data.DoubleVector;
+import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.data.VectorUtil;
+import de.lmu.ifi.dbs.elki.data.type.SimpleTypeInformation;
+import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
+import de.lmu.ifi.dbs.elki.data.type.VectorFieldTypeInformation;
+import de.lmu.ifi.dbs.elki.datasource.filter.AbstractVectorStreamConversionFilter;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterEqualConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.ListEachConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntListParameter;
+
+/**
+ * <p>
+ * Parser to project the ParsingResult obtained by a suitable base parser onto a
+ * selected subset of attributes.
+ * </p>
+ *
+ * @author Arthur Zimek
+ *
+ * @apiviz.uses NumberVector
+ *
+ * @param <V> Vector type
+ */
+public class NumberVectorFeatureSelectionFilter<V extends NumberVector<?>> extends AbstractVectorStreamConversionFilter<V, V> {
+ /**
+ * Keeps the selection of the subspace to project onto.
+ */
+ private BitSet selectedAttributes;
+
+ /**
+ * Constructor.
+ *
+ * @param selectedAttributes Selected attributes
+ */
+ public NumberVectorFeatureSelectionFilter(BitSet selectedAttributes) {
+ super();
+ this.selectedAttributes = selectedAttributes;
+ }
+
+ @Override
+ protected V filterSingleObject(V obj) {
+ return VectorUtil.project(obj, getSelectedAttributes(), factory);
+ }
+
+ @Override
+ protected SimpleTypeInformation<? super V> getInputTypeRestriction() {
+ return TypeUtil.NUMBER_VECTOR_FIELD;
+ }
+
+ @Override
+ protected SimpleTypeInformation<? super V> convertedType(SimpleTypeInformation<V> in) {
+ initializeOutputType(in);
+ return new VectorFieldTypeInformation<V>(factory, getDimensionality());
+ }
+
+ /**
+ * <p>
+ * Sets the bits set to true in the given BitSet as selected attributes in
+ * {@link Parameterizer#SELECTED_ATTRIBUTES_ID}.
+ * </p>
+ *
+ * The index in the BitSet is expected to be shifted to the left by one, i.e.,
+ * index 0 in the BitSet relates to the first attribute.
+ *
+ * @param selectedAttributes the new selected attributes
+ */
+ public void setSelectedAttributes(BitSet selectedAttributes) {
+ this.selectedAttributes.or(selectedAttributes);
+ }
+
+ /**
+ * <p>
+ * Provides a BitSet with the bits set to true corresponding to the selected
+ * attributes in {@link Parameterizer#SELECTED_ATTRIBUTES_ID}.
+ * </p>
+ *
+ * The index in the BitSet is shifted to the left by one, i.e., index 0 in the
+ * BitSet relates to the first attribute.
+ *
+ * @return the selected attributes
+ */
+ public BitSet getSelectedAttributes() {
+ return selectedAttributes;
+ }
+
+ /**
+ * Get the resulting dimensionality.
+ *
+ * @return dimensionality
+ */
+ public int getDimensionality() {
+ return selectedAttributes.cardinality();
+ }
+
+ /**
+ * Parameterization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public static class Parameterizer extends AbstractParameterizer {
+ /**
+ * <p>
+ * Selected attributes parameter.
+ * </p>
+ * <p>
+ * Key: <code>-projectionfilter.selectedattributes</code>
+ * </p>
+ */
+ public static final OptionID SELECTED_ATTRIBUTES_ID = new OptionID("projectionfilter.selectedattributes", "a comma separated array of integer values d_i, where 0 <= d_i < the " + "dimensionality of the feature space " + "specifying the dimensions to be considered " + "for projection. If this parameter is not set, " + "no dimensions will be considered, i.e. the projection is a zero-dimensional feature space");
+
+ /**
+ * Selected attributes.
+ */
+ protected BitSet selectedAttributes = null;
+
+ @Override
+ protected void makeOptions(Parameterization config) {
+ super.makeOptions(config);
+ IntListParameter selectedAttributesP = new IntListParameter(SELECTED_ATTRIBUTES_ID);
+ selectedAttributesP.addConstraint(new ListEachConstraint<Integer>(new GreaterEqualConstraint(0)));
+ if (config.grab(selectedAttributesP)) {
+ selectedAttributes = new BitSet();
+ List<Integer> dimensionList = selectedAttributesP.getValue();
+ for (int d : dimensionList) {
+ selectedAttributes.set(d);
+ }
+ }
+ }
+
+ @Override
+ protected NumberVectorFeatureSelectionFilter<DoubleVector> makeInstance() {
+ return new NumberVectorFeatureSelectionFilter<DoubleVector>(selectedAttributes);
+ }
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/filter/transform/NumberVectorRandomFeatureSelectionFilter.java b/src/de/lmu/ifi/dbs/elki/datasource/filter/transform/NumberVectorRandomFeatureSelectionFilter.java
new file mode 100644
index 00000000..7d799a1e
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/datasource/filter/transform/NumberVectorRandomFeatureSelectionFilter.java
@@ -0,0 +1,174 @@
+package de.lmu.ifi.dbs.elki.datasource.filter.transform;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.BitSet;
+
+import de.lmu.ifi.dbs.elki.data.DoubleVector;
+import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.data.VectorUtil;
+import de.lmu.ifi.dbs.elki.data.type.SimpleTypeInformation;
+import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
+import de.lmu.ifi.dbs.elki.data.type.VectorFieldTypeInformation;
+import de.lmu.ifi.dbs.elki.datasource.filter.AbstractVectorStreamConversionFilter;
+import de.lmu.ifi.dbs.elki.utilities.RandomFactory;
+import de.lmu.ifi.dbs.elki.utilities.Util;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterEqualConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.RandomParameter;
+
+/**
+ * Parser to project the ParsingResult obtained by a suitable base parser onto a
+ * randomly selected subset of attributes.
+ *
+ * @author Arthur Zimek
+ *
+ * @apiviz.uses NumberVector
+ *
+ * @param <V> vector type
+ */
+public class NumberVectorRandomFeatureSelectionFilter<V extends NumberVector<?>> extends AbstractVectorStreamConversionFilter<V, V> {
+ /**
+ * The selected attributes.
+ */
+ protected BitSet selectedAttributes = null;
+
+ /**
+ * Holds the desired cardinality of the subset of attributes selected for
+ * projection.
+ */
+ protected int k;
+
+ /**
+ * Holds a random generator.
+ */
+ protected RandomFactory rnd;
+
+ /**
+ * Constructor.
+ *
+ * @param dim Dimensionality
+ * @param rnd Random generator
+ */
+ public NumberVectorRandomFeatureSelectionFilter(int dim, RandomFactory rnd) {
+ super();
+ this.k = dim;
+ this.rnd = rnd;
+ }
+
+ @Override
+ protected V filterSingleObject(V obj) {
+ return VectorUtil.project(obj, selectedAttributes, factory);
+ }
+
+ @Override
+ protected SimpleTypeInformation<? super V> getInputTypeRestriction() {
+ return TypeUtil.NUMBER_VECTOR_FIELD;
+ }
+
+ @Override
+ protected SimpleTypeInformation<? super V> convertedType(SimpleTypeInformation<V> in) {
+ initializeRandomAttributes(in);
+ initializeOutputType(in);
+ return new VectorFieldTypeInformation<V>(factory, k);
+ }
+
+ /**
+ * Initialize random attributes.
+ *
+ * Invoke this from {@link #convertedType}!
+ *
+ * @param in Type information.
+ */
+ void initializeRandomAttributes(SimpleTypeInformation<V> in) {
+ int d = ((VectorFieldTypeInformation<V>) in).getDimensionality();
+ selectedAttributes = Util.randomBitSet(k, d, rnd.getRandom());
+ }
+
+ /**
+ * Parameterization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public static class Parameterizer extends AbstractParameterizer {
+ /**
+ * Parameter for the desired cardinality of the subset of attributes
+ * selected for projection.
+ *
+ * <p>
+ * Key: <code>-randomprojection.numberselected</code>
+ * </p>
+ * <p>
+ * Default: <code>1</code>
+ * </p>
+ * <p>
+ * Constraint: &ge;1
+ * </p>
+ */
+ public static final OptionID NUMBER_SELECTED_ATTRIBUTES_ID = new OptionID("randomprojection.numberselected", "number of selected attributes");
+
+ /**
+ * Optional parameter to specify a seed for random projection. If unused,
+ * system time is used as seed.
+ * <p>
+ * Key: {@code -randomprojection.seed}
+ * </p>
+ */
+ public static final OptionID SEED_ID = new OptionID("randomprojection.seed", "Seed for random selection of projection attributes.");
+
+ /**
+ * Number of attributes to select.
+ */
+ protected int k = 0;
+
+ /**
+ * Random generator.
+ */
+ protected RandomFactory rnd;
+
+ @Override
+ protected void makeOptions(Parameterization config) {
+ super.makeOptions(config);
+ IntParameter kP = new IntParameter(NUMBER_SELECTED_ATTRIBUTES_ID, 1);
+ kP.addConstraint(new GreaterEqualConstraint(1));
+ if (config.grab(kP)) {
+ k = kP.getValue().intValue();
+ }
+ RandomParameter rndP = new RandomParameter(SEED_ID);
+ if (config.grab(rndP)) {
+ rnd = rndP.getValue();
+ }
+ }
+
+ @Override
+ protected NumberVectorRandomFeatureSelectionFilter<DoubleVector> makeInstance() {
+ return new NumberVectorRandomFeatureSelectionFilter<DoubleVector>(k, rnd);
+ }
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/parser/AbstractParser.java b/src/de/lmu/ifi/dbs/elki/datasource/parser/AbstractParser.java
index 3c294ca4..1f414055 100644
--- a/src/de/lmu/ifi/dbs/elki/datasource/parser/AbstractParser.java
+++ b/src/de/lmu/ifi/dbs/elki/datasource/parser/AbstractParser.java
@@ -65,13 +65,13 @@ public abstract class AbstractParser {
* OptionID for the column separator parameter (defaults to whitespace as in
* {@link #DEFAULT_SEPARATOR}.
*/
- public static final OptionID COLUMN_SEPARATOR_ID = OptionID.getOrCreateOptionID("parser.colsep", "Column separator pattern. The default assumes whitespace separated data.");
+ public static final OptionID COLUMN_SEPARATOR_ID = new OptionID("parser.colsep", "Column separator pattern. The default assumes whitespace separated data.");
/**
* OptionID for the quote character parameter (defaults to a double quotation
* mark as in {@link #QUOTE_CHAR}.
*/
- public static final OptionID QUOTE_ID = OptionID.getOrCreateOptionID("parser.quote", "Quotation character. The default is to use a double quote.");
+ public static final OptionID QUOTE_ID = new OptionID("parser.quote", "Quotation character. The default is to use a double quote.");
/**
* Stores the column separator pattern
@@ -196,7 +196,7 @@ public abstract class AbstractParser {
*
* @apiviz.exclude
*/
- public static abstract class Parameterizer extends AbstractParameterizer {
+ public abstract static class Parameterizer extends AbstractParameterizer {
/**
* Stores the column separator pattern
*/
@@ -214,7 +214,8 @@ public abstract class AbstractParser {
if(config.grab(colParam)) {
colSep = colParam.getValue();
}
- StringParameter quoteParam = new StringParameter(QUOTE_ID, new StringLengthConstraint(1, 1), ""+QUOTE_CHAR);
+ StringParameter quoteParam = new StringParameter(QUOTE_ID, String.valueOf(QUOTE_CHAR));
+ quoteParam.addConstraint(new StringLengthConstraint(1, 1));
if(config.grab(quoteParam)) {
quoteChar = quoteParam.getValue().charAt(0);
}
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/parser/ArffParser.java b/src/de/lmu/ifi/dbs/elki/datasource/parser/ArffParser.java
index 9d77b921..d1280fbe 100644
--- a/src/de/lmu/ifi/dbs/elki/datasource/parser/ArffParser.java
+++ b/src/de/lmu/ifi/dbs/elki/datasource/parser/ArffParser.java
@@ -66,22 +66,22 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.PatternParameter;
*/
public class ArffParser implements Parser {
/**
- * Logger
+ * Logger.
*/
- private static final Logging logger = Logging.getLogger(ArffParser.class);
+ private static final Logging LOG = Logging.getLogger(ArffParser.class);
/**
- * Arff file marker
+ * Arff file marker.
*/
public static final Pattern ARFF_HEADER_RELATION = Pattern.compile("@relation\\s+(.*)", Pattern.CASE_INSENSITIVE);
/**
- * Arff attribute declaration marker
+ * Arff attribute declaration marker.
*/
public static final Pattern ARFF_HEADER_ATTRIBUTE = Pattern.compile("@attribute\\s+([^ ]+|['\"].*?['\"])\\s+(numeric|real|integer|string|double|date(\\s.*)|\\{.*\\})\\s*", Pattern.CASE_INSENSITIVE);
/**
- * Arff data marker
+ * Arff data marker.
*/
public static final Pattern ARFF_HEADER_DATA = Pattern.compile("@data\\s*", Pattern.CASE_INSENSITIVE);
@@ -101,7 +101,7 @@ public class ArffParser implements Parser {
public static final String DEFAULT_ARFF_MAGIC_CLASS = "(Class|Class-?Label)";
/**
- * Pattern for numeric columns
+ * Pattern for numeric columns.
*/
public static final Pattern ARFF_NUMERIC = Pattern.compile("(numeric|real|integer|double)", Pattern.CASE_INSENSITIVE);
@@ -111,12 +111,12 @@ public class ArffParser implements Parser {
public static final Pattern EMPTY = Pattern.compile("^\\s*$");
/**
- * Pattern to recognize external ids
+ * Pattern to recognize external ids.
*/
Pattern magic_eid;
/**
- * Pattern to recognize class label columns
+ * Pattern to recognize class label columns.
*/
Pattern magic_class;
@@ -225,7 +225,7 @@ public class ArffParser implements Parser {
}
nextToken(tokenizer);
if(tokenizer.ttype == StreamTokenizer.TT_NUMBER) {
- map.put(dim, tokenizer.nval);
+ map.put(dim, Double.valueOf(tokenizer.nval));
}
else if(tokenizer.ttype == StreamTokenizer.TT_WORD) {
map.put(dim, tokenizer.sval);
@@ -246,7 +246,7 @@ public class ArffParser implements Parser {
}
}
assert (s >= 0);
- if(elkitypes[out] == TypeUtil.NUMBER_VECTOR_FIELD) {
+ if(TypeUtil.NUMBER_VECTOR_FIELD.equals(elkitypes[out])) {
TIntFloatHashMap f = new TIntFloatHashMap(dimsize[out]);
for(TIntObjectIterator<Object> iter = map.iterator(); iter.hasNext();) {
iter.advance();
@@ -257,12 +257,12 @@ public class ArffParser implements Parser {
if(i >= s + dimsize[out]) {
break;
}
- double v = (Double) iter.value();
+ double v = ((Double) iter.value()).doubleValue();
f.put(i - s + 1, (float) v);
}
data[out] = new SparseFloatVector(f, dimsize[out]);
}
- else if(elkitypes[out] == TypeUtil.LABELLIST) {
+ else if(TypeUtil.LABELLIST.equals(elkitypes[out])) {
// Build a label list out of successive labels
LabelList ll = new LabelList(1);
for(TIntObjectIterator<Object> iter = map.iterator(); iter.hasNext();) {
@@ -276,13 +276,13 @@ public class ArffParser implements Parser {
}
String v = (String) iter.value();
if(ll.size() < i - s) {
- logger.warning("Sparse consecutive labels are currently not correctly supported.");
+ LOG.warning("Sparse consecutive labels are currently not correctly supported.");
}
ll.add(v);
}
data[out] = ll;
}
- else if(elkitypes[out] == TypeUtil.EXTERNALID) {
+ else if(TypeUtil.EXTERNALID.equals(elkitypes[out])) {
String val = (String) map.get(s);
if(val != null) {
data[out] = new ExternalID(val);
@@ -291,7 +291,7 @@ public class ArffParser implements Parser {
throw new AbortException("External ID column not set in sparse instance." + tokenizer.toString());
}
}
- else if(elkitypes[out] == TypeUtil.CLASSLABEL) {
+ else if(TypeUtil.CLASSLABEL.equals(elkitypes[out])) {
String val = (String) map.get(s);
if(val != null) {
// TODO: support other class label types.
@@ -312,7 +312,7 @@ public class ArffParser implements Parser {
private Object[] loadDenseInstance(StreamTokenizer tokenizer, int[] dimsize, TypeInformation[] etyp, int outdim) throws IOException {
Object[] data = new Object[outdim];
for(int out = 0; out < outdim; out++) {
- if(etyp[out] == TypeUtil.NUMBER_VECTOR_FIELD) {
+ if(TypeUtil.NUMBER_VECTOR_FIELD.equals(etyp[out])) {
// For multi-column vectors, read successive columns
double[] cur = new double[dimsize[out]];
for(int k = 0; k < dimsize[out]; k++) {
@@ -334,7 +334,7 @@ public class ArffParser implements Parser {
}
data[out] = new DoubleVector(cur);
}
- else if(etyp[out] == TypeUtil.LABELLIST) {
+ else if(TypeUtil.LABELLIST.equals(etyp[out])) {
// Build a label list out of successive labels
LabelList ll = new LabelList(dimsize[out]);
for(int k = 0; k < dimsize[out]; k++) {
@@ -346,14 +346,14 @@ public class ArffParser implements Parser {
}
data[out] = ll;
}
- else if(etyp[out] == TypeUtil.EXTERNALID) {
+ else if(TypeUtil.EXTERNALID.equals(etyp[out])) {
if(tokenizer.ttype != StreamTokenizer.TT_WORD) {
throw new AbortException("Expected word token, got: " + tokenizer.toString());
}
data[out] = new ExternalID(tokenizer.sval);
nextToken(tokenizer);
}
- else if(etyp[out] == TypeUtil.CLASSLABEL) {
+ else if(TypeUtil.CLASSLABEL.equals(etyp[out])) {
if(tokenizer.ttype != StreamTokenizer.TT_WORD) {
throw new AbortException("Expected word token, got: " + tokenizer.toString());
}
@@ -414,32 +414,32 @@ public class ArffParser implements Parser {
break;
}
}
- if(etyp[out] == TypeUtil.NUMBER_VECTOR_FIELD) {
+ if(TypeUtil.NUMBER_VECTOR_FIELD.equals(etyp[out])) {
String[] labels = new String[dimsize[out]];
// Collect labels:
for(int i = 0; i < dimsize[out]; i++) {
labels[i] = names.get(out + i);
}
if(!sparse) {
- VectorFieldTypeInformation<DoubleVector> type = new VectorFieldTypeInformation<DoubleVector>(DoubleVector.class, DoubleVector.STATIC, dimsize[out], labels, new DoubleVector(new double[dimsize[out]]));
+ VectorFieldTypeInformation<DoubleVector> type = new VectorFieldTypeInformation<DoubleVector>(DoubleVector.FACTORY, dimsize[out], labels);
bundle.appendColumn(type, new ArrayList<DoubleVector>());
}
else {
- VectorFieldTypeInformation<SparseFloatVector> type = new VectorFieldTypeInformation<SparseFloatVector>(SparseFloatVector.class, dimsize[out], labels, new SparseFloatVector(SparseFloatVector.EMPTYMAP, dimsize[out]));
+ VectorFieldTypeInformation<SparseFloatVector> type = new VectorFieldTypeInformation<SparseFloatVector>(SparseFloatVector.FACTORY, dimsize[out], labels);
bundle.appendColumn(type, new ArrayList<SparseFloatVector>());
}
}
- else if(etyp[out] == TypeUtil.LABELLIST) {
- String label = names.get(out);
+ else if(TypeUtil.LABELLIST.equals(etyp[out])) {
+ StringBuilder label = new StringBuilder(names.get(out));
for(int i = 1; i < dimsize[out]; i++) {
- label = label + " " + names.get(out + i);
+ label.append(' ').append(names.get(out + i));
}
- bundle.appendColumn(new SimpleTypeInformation<LabelList>(LabelList.class, label), new ArrayList<LabelList>());
+ bundle.appendColumn(new SimpleTypeInformation<LabelList>(LabelList.class, label.toString()), new ArrayList<LabelList>());
}
- else if(etyp[out] == TypeUtil.EXTERNALID) {
+ else if(TypeUtil.EXTERNALID.equals(etyp[out])) {
bundle.appendColumn(new SimpleTypeInformation<ExternalID>(ExternalID.class, names.get(out)), new ArrayList<ExternalID>());
}
- else if(etyp[out] == TypeUtil.CLASSLABEL) {
+ else if(TypeUtil.CLASSLABEL.equals(etyp[out])) {
bundle.appendColumn(new SimpleTypeInformation<ClassLabel>(ClassLabel.class, names.get(out)), new ArrayList<ClassLabel>());
}
else {
@@ -553,7 +553,7 @@ public class ArffParser implements Parser {
}
else if(ARFF_NUMERIC.matcher(types.get(i)).matches()) {
// Create a number vector field
- if(next > 0 && etyp[next - 1] == TypeUtil.NUMBER_VECTOR_FIELD) {
+ if(next > 0 && TypeUtil.NUMBER_VECTOR_FIELD.equals(etyp[next - 1])) {
targ[i] = next - 1;
dims[next - 1]++;
continue;
@@ -568,7 +568,7 @@ public class ArffParser implements Parser {
}
else {
// Use LabelList
- if(next > 0 && etyp[next - 1] == TypeUtil.LABELLIST) {
+ if(next > 0 && TypeUtil.LABELLIST.equals(etyp[next - 1])) {
targ[i] = next - 1;
dims[next - 1]++;
continue;
@@ -598,21 +598,21 @@ public class ArffParser implements Parser {
else if((tokenizer.ttype == StreamTokenizer.TT_WORD) && (tokenizer.sval.equals("?"))) {
tokenizer.ttype = '?';
}
- if(logger.isDebugging()) {
+ if(LOG.isDebugging()) {
if(tokenizer.ttype == StreamTokenizer.TT_NUMBER) {
- logger.debug("token: " + tokenizer.nval);
+ LOG.debug("token: " + tokenizer.nval);
}
else if(tokenizer.ttype == StreamTokenizer.TT_WORD) {
- logger.debug("token: " + tokenizer.sval);
+ LOG.debug("token: " + tokenizer.sval);
}
else if(tokenizer.ttype == StreamTokenizer.TT_EOF) {
- logger.debug("token: EOF");
+ LOG.debug("token: EOF");
}
else if(tokenizer.ttype == StreamTokenizer.TT_EOL) {
- logger.debug("token: EOL");
+ LOG.debug("token: EOL");
}
else {
- logger.debug("token type: " + tokenizer.ttype);
+ LOG.debug("token type: " + tokenizer.ttype);
}
}
}
@@ -628,12 +628,12 @@ public class ArffParser implements Parser {
/**
* Pattern for recognizing external ID attributes.
*/
- public static final OptionID MAGIC_EID_ID = OptionID.getOrCreateOptionID("arff.externalid", "Pattern to recognize external ID attributes.");
+ public static final OptionID MAGIC_EID_ID = new OptionID("arff.externalid", "Pattern to recognize external ID attributes.");
/**
* Pattern for recognizing class label attributes.
*/
- public static final OptionID MAGIC_CLASS_ID = OptionID.getOrCreateOptionID("arff.classlabel", "Pattern to recognize class label attributes.");
+ public static final OptionID MAGIC_CLASS_ID = new OptionID("arff.classlabel", "Pattern to recognize class label attributes.");
/**
* Pattern to recognize external ids
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/parser/BitVectorLabelParser.java b/src/de/lmu/ifi/dbs/elki/datasource/parser/BitVectorLabelParser.java
index c62392b4..32a26d7d 100644
--- a/src/de/lmu/ifi/dbs/elki/datasource/parser/BitVectorLabelParser.java
+++ b/src/de/lmu/ifi/dbs/elki/datasource/parser/BitVectorLabelParser.java
@@ -28,7 +28,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
-import java.util.BitSet;
import java.util.List;
import java.util.regex.Pattern;
@@ -59,7 +58,7 @@ public class BitVectorLabelParser extends AbstractParser implements Parser {
/**
* Class logger
*/
- private static final Logging logger = Logging.getLogger(BitVectorLabelParser.class);
+ private static final Logging LOG = Logging.getLogger(BitVectorLabelParser.class);
/**
* Constructor.
@@ -117,12 +116,12 @@ public class BitVectorLabelParser extends AbstractParser implements Parser {
}
protected VectorFieldTypeInformation<BitVector> getTypeInformation(int dimensionality) {
- return new VectorFieldTypeInformation<BitVector>(BitVector.class, dimensionality, new BitVector(new BitSet(), dimensionality));
+ return new VectorFieldTypeInformation<BitVector>(BitVector.FACTORY, dimensionality);
}
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/parser/DoubleVectorLabelParser.java b/src/de/lmu/ifi/dbs/elki/datasource/parser/DoubleVectorLabelParser.java
index 7bc52a29..0c291fb4 100644
--- a/src/de/lmu/ifi/dbs/elki/datasource/parser/DoubleVectorLabelParser.java
+++ b/src/de/lmu/ifi/dbs/elki/datasource/parser/DoubleVectorLabelParser.java
@@ -56,7 +56,7 @@ public class DoubleVectorLabelParser extends NumberVectorLabelParser<DoubleVecto
/**
* Class logger
*/
- private static final Logging logger = Logging.getLogger(DoubleVectorLabelParser.class);
+ private static final Logging LOG = Logging.getLogger(DoubleVectorLabelParser.class);
/**
* Constructor.
@@ -66,7 +66,7 @@ public class DoubleVectorLabelParser extends NumberVectorLabelParser<DoubleVecto
* @param labelIndices
*/
public DoubleVectorLabelParser(Pattern colSep, char quoteChar, BitSet labelIndices) {
- super(colSep, quoteChar, labelIndices, DoubleVector.STATIC);
+ super(colSep, quoteChar, labelIndices, DoubleVector.FACTORY);
}
/**
@@ -78,7 +78,7 @@ public class DoubleVectorLabelParser extends NumberVectorLabelParser<DoubleVecto
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/parser/FloatVectorLabelParser.java b/src/de/lmu/ifi/dbs/elki/datasource/parser/FloatVectorLabelParser.java
index 6465f89a..6288da8e 100644
--- a/src/de/lmu/ifi/dbs/elki/datasource/parser/FloatVectorLabelParser.java
+++ b/src/de/lmu/ifi/dbs/elki/datasource/parser/FloatVectorLabelParser.java
@@ -59,7 +59,7 @@ public class FloatVectorLabelParser extends NumberVectorLabelParser<FloatVector>
/**
* Class logger
*/
- private static final Logging logger = Logging.getLogger(FloatVectorLabelParser.class);
+ private static final Logging LOG = Logging.getLogger(FloatVectorLabelParser.class);
/**
* Constructor.
@@ -69,12 +69,12 @@ public class FloatVectorLabelParser extends NumberVectorLabelParser<FloatVector>
* @param labelIndices
*/
public FloatVectorLabelParser(Pattern colSep, char quoteChar, BitSet labelIndices) {
- super(colSep, quoteChar, labelIndices, FloatVector.STATIC);
+ super(colSep, quoteChar, labelIndices, FloatVector.FACTORY);
}
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/parser/NumberVectorLabelParser.java b/src/de/lmu/ifi/dbs/elki/datasource/parser/NumberVectorLabelParser.java
index 53ce44bc..ea44c072 100644
--- a/src/de/lmu/ifi/dbs/elki/datasource/parser/NumberVectorLabelParser.java
+++ b/src/de/lmu/ifi/dbs/elki/datasource/parser/NumberVectorLabelParser.java
@@ -42,7 +42,6 @@ import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
import de.lmu.ifi.dbs.elki.data.type.VectorFieldTypeInformation;
import de.lmu.ifi.dbs.elki.datasource.bundle.BundleMeta;
import de.lmu.ifi.dbs.elki.logging.Logging;
-import de.lmu.ifi.dbs.elki.persistent.ByteBufferSerializer;
import de.lmu.ifi.dbs.elki.utilities.datastructures.arraylike.ArrayLikeUtil;
import de.lmu.ifi.dbs.elki.utilities.datastructures.arraylike.NumberArrayAdapter;
import de.lmu.ifi.dbs.elki.utilities.exceptions.AbortException;
@@ -72,11 +71,11 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ObjectParameter;
*
* @param <V> the type of NumberVector used
*/
-public class NumberVectorLabelParser<V extends NumberVector<V, ?>> extends AbstractStreamingParser {
+public class NumberVectorLabelParser<V extends NumberVector<?>> extends AbstractStreamingParser {
/**
* Logging class.
*/
- private static final Logging logger = Logging.getLogger(NumberVectorLabelParser.class);
+ private static final Logging LOG = Logging.getLogger(NumberVectorLabelParser.class);
/**
* A comma separated list of the indices of labels (may be numeric), counting
@@ -86,7 +85,7 @@ public class NumberVectorLabelParser<V extends NumberVector<V, ?>> extends Abstr
* Key: {@code -parser.labelIndices}
* </p>
*/
- public static final OptionID LABEL_INDICES_ID = OptionID.getOrCreateOptionID("parser.labelIndices", "A comma separated list of the indices of labels (may be numeric), counting whitespace separated entries in a line starting with 0. The corresponding entries will be treated as a label.");
+ public static final OptionID LABEL_INDICES_ID = new OptionID("parser.labelIndices", "A comma separated list of the indices of labels (may be numeric), counting whitespace separated entries in a line starting with 0. The corresponding entries will be treated as a label.");
/**
* Parameter to specify the type of vectors to produce.
@@ -95,7 +94,7 @@ public class NumberVectorLabelParser<V extends NumberVector<V, ?>> extends Abstr
* Default: DoubleVector
* </p>
*/
- public static final OptionID VECTOR_TYPE_ID = OptionID.getOrCreateOptionID("parser.vector-type", "The type of vectors to create for numerical attributes.");
+ public static final OptionID VECTOR_TYPE_ID = new OptionID("parser.vector-type", "The type of vectors to create for numerical attributes.");
/**
* Constant used for unknown dimensionality (e.g. empty files)
@@ -113,73 +112,73 @@ public class NumberVectorLabelParser<V extends NumberVector<V, ?>> extends Abstr
protected BitSet labelIndices;
/**
- * Vector factory class
+ * Vector factory class.
*/
- protected V factory;
+ protected NumberVector.Factory<V, ?> factory;
/**
- * Buffer reader
+ * Buffer reader.
*/
private BufferedReader reader;
/**
- * Current line number
+ * Current line number.
*/
protected int lineNumber;
/**
- * Dimensionality reported
+ * Dimensionality reported.
*/
protected int dimensionality;
/**
- * Metadata
+ * Metadata.
*/
protected BundleMeta meta = null;
/**
- * Column names
+ * Column names.
*/
protected List<String> columnnames = null;
/**
- * Bitset to indicate which columns are numeric
+ * Bitset to indicate which columns are not numeric.
*/
protected BitSet labelcolumns = null;
/**
- * Current vector
+ * Current vector.
*/
protected V curvec = null;
/**
- * Current labels
+ * Current labels.
*/
protected LabelList curlbl = null;
/**
- * Event to report next
+ * Event to report next.
*/
Event nextevent = null;
/**
- * Constructor with defaults
+ * Constructor with defaults.
*
* @param factory Vector factory
*/
- public NumberVectorLabelParser(V factory) {
+ public NumberVectorLabelParser(NumberVector.Factory<V, ?> factory) {
this(Pattern.compile(DEFAULT_SEPARATOR), QUOTE_CHAR, null, factory);
}
/**
- * Constructor
+ * Constructor.
*
- * @param colSep
- * @param quoteChar
- * @param labelIndices
+ * @param colSep Column separator
+ * @param quoteChar Quote character
+ * @param labelIndices Column indexes that are numeric.
* @param factory Vector factory
*/
- public NumberVectorLabelParser(Pattern colSep, char quoteChar, BitSet labelIndices, V factory) {
+ public NumberVectorLabelParser(Pattern colSep, char quoteChar, BitSet labelIndices, NumberVector.Factory<V, ?> factory) {
super(colSep, quoteChar);
this.labelIndices = labelIndices;
this.factory = factory;
@@ -192,6 +191,9 @@ public class NumberVectorLabelParser<V extends NumberVector<V, ?>> extends Abstr
dimensionality = DIMENSIONALITY_UNKNOWN;
columnnames = null;
labelcolumns = new BitSet();
+ if (labelIndices != null) {
+ labelcolumns.or(labelIndices);
+ }
}
@Override
@@ -201,27 +203,26 @@ public class NumberVectorLabelParser<V extends NumberVector<V, ?>> extends Abstr
@Override
public Event nextEvent() {
- if(nextevent != null) {
+ if (nextevent != null) {
Event ret = nextevent;
nextevent = null;
return ret;
}
try {
- for(String line; (line = reader.readLine()) != null; lineNumber++) {
- if(!line.startsWith(COMMENT) && line.length() > 0) {
+ for (String line; (line = reader.readLine()) != null; lineNumber++) {
+ if (!line.startsWith(COMMENT) && line.length() > 0) {
parseLineInternal(line);
// Maybe a header column?
- if(curvec == null) {
+ if (curvec == null) {
continue;
}
- if(dimensionality == DIMENSIONALITY_UNKNOWN) {
+ if (dimensionality == DIMENSIONALITY_UNKNOWN) {
dimensionality = curvec.getDimensionality();
buildMeta();
nextevent = Event.NEXT_OBJECT;
return Event.META_CHANGED;
- }
- else if(dimensionality > 0) {
- if(dimensionality != curvec.getDimensionality()) {
+ } else if (dimensionality > 0) {
+ if (dimensionality != curvec.getDimensionality()) {
dimensionality = DIMENSIONALITY_VARIABLE;
buildMeta();
nextevent = Event.NEXT_OBJECT;
@@ -238,8 +239,7 @@ public class NumberVectorLabelParser<V extends NumberVector<V, ?>> extends Abstr
reader.close();
reader = null;
return Event.END_OF_STREAM;
- }
- catch(IOException e) {
+ } catch (IOException e) {
throw new IllegalArgumentException("Error while parsing line " + lineNumber + ".");
}
}
@@ -248,12 +248,11 @@ public class NumberVectorLabelParser<V extends NumberVector<V, ?>> extends Abstr
* Update the meta element.
*/
protected void buildMeta() {
- if(labelcolumns.cardinality() > 0) {
+ if (labelcolumns.cardinality() > 0 || (labelIndices != null && labelIndices.cardinality() > 0)) {
meta = new BundleMeta(2);
meta.add(getTypeInformation(dimensionality));
meta.add(TypeUtil.LABELLIST);
- }
- else {
+ } else {
meta = new BundleMeta(1);
meta.add(getTypeInformation(dimensionality));
}
@@ -261,10 +260,10 @@ public class NumberVectorLabelParser<V extends NumberVector<V, ?>> extends Abstr
@Override
public Object data(int rnum) {
- if(rnum == 0) {
+ if (rnum == 0) {
return curvec;
}
- if(rnum == 1) {
+ if (rnum == 1) {
return curlbl;
}
throw new ArrayIndexOutOfBoundsException();
@@ -284,28 +283,32 @@ public class NumberVectorLabelParser<V extends NumberVector<V, ?>> extends Abstr
LabelList labels = null;
Iterator<String> itr = entries.iterator();
- for(int i = 0; itr.hasNext(); i++) {
+ for (int i = 0; itr.hasNext(); i++) {
String ent = itr.next();
- if(!labelIndices.get(i)) {
+ if (labelIndices == null || !labelIndices.get(i)) {
try {
double attribute = Double.parseDouble(ent);
attributes.add(attribute);
continue;
- }
- catch(NumberFormatException e) {
+ } catch (NumberFormatException e) {
// Ignore attempt, add to labels below.
labelcolumns.set(i);
}
}
- if(labels == null) {
+ // Else: labels.
+ if (labels == null) {
labels = new LabelList(1);
}
- labels.add(ent);
+ // Make a new string, to not keep the whole file in memory!
+ labels.add(new String(ent));
}
// Maybe a label row?
- if(lineNumber == 1 && attributes.size() == 0) {
+ if (lineNumber == 1 && attributes.size() == 0) {
columnnames = labels;
labelcolumns.clear();
+ if (labelIndices != null) {
+ labelcolumns.or(labelIndices);
+ }
curvec = null;
curlbl = null;
return;
@@ -316,11 +319,11 @@ public class NumberVectorLabelParser<V extends NumberVector<V, ?>> extends Abstr
}
/**
- * <p>
* Creates a database object of type V.
- * </p>
*
* @param attributes the attributes of the vector to create.
+ * @param adapter Array adapter
+ * @param <A> attribute type
* @return a RalVector of type V containing the given attribute values
*/
protected <A> V createDBObject(A attributes, NumberArrayAdapter<?, A> adapter) {
@@ -334,47 +337,31 @@ public class NumberVectorLabelParser<V extends NumberVector<V, ?>> extends Abstr
* @return Prototype object
*/
SimpleTypeInformation<V> getTypeInformation(int dimensionality) {
- @SuppressWarnings("unchecked")
- Class<V> cls = (Class<V>) factory.getClass();
- if(dimensionality > 0) {
+ if (dimensionality > 0) {
String[] colnames = null;
- if(columnnames != null) {
- if(columnnames.size() - labelcolumns.cardinality() == dimensionality) {
+ if (columnnames != null) {
+ if (columnnames.size() - labelcolumns.cardinality() == dimensionality) {
colnames = new String[dimensionality];
- for(int i = 0, j = 0; i < columnnames.size(); i++) {
- if(labelcolumns.get(i) == false) {
+ for (int i = 0, j = 0; i < columnnames.size(); i++) {
+ if (!labelcolumns.get(i)) {
colnames[j] = columnnames.get(i);
j++;
}
}
}
}
- V f = factory.newNumberVector(new double[dimensionality]);
- if(f instanceof ByteBufferSerializer) {
- // TODO: Remove, once we have serializers for all types
- @SuppressWarnings("unchecked")
- final ByteBufferSerializer<V> ser = (ByteBufferSerializer<V>) f;
- return new VectorFieldTypeInformation<V>(cls, ser, dimensionality, colnames, f);
- }
- return new VectorFieldTypeInformation<V>(cls, dimensionality, colnames, f);
+ return new VectorFieldTypeInformation<V>(factory, dimensionality, colnames);
}
// Variable dimensionality - return non-vector field type
- if(dimensionality == DIMENSIONALITY_VARIABLE) {
- V f = factory.newNumberVector(new double[0]);
- if(f instanceof ByteBufferSerializer) {
- // TODO: Remove, once we have serializers for all types
- @SuppressWarnings("unchecked")
- final ByteBufferSerializer<V> ser = (ByteBufferSerializer<V>) f;
- return new SimpleTypeInformation<V>(cls, ser);
- }
- return new SimpleTypeInformation<V>(cls);
+ if (dimensionality == DIMENSIONALITY_VARIABLE) {
+ return new SimpleTypeInformation<V>(factory.getRestrictionClass(), factory.getDefaultSerializer());
}
throw new AbortException("No vectors were read from the input file - cannot determine vector data type.");
}
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -384,16 +371,16 @@ public class NumberVectorLabelParser<V extends NumberVector<V, ?>> extends Abstr
*
* @apiviz.exclude
*/
- public static class Parameterizer<V extends NumberVector<V, ?>> extends AbstractParser.Parameterizer {
+ public static class Parameterizer<V extends NumberVector<?>> extends AbstractParser.Parameterizer {
/**
* Keeps the indices of the attributes to be treated as a string label.
*/
protected BitSet labelIndices = null;
/**
- * Factory
+ * Factory object.
*/
- protected V factory;
+ protected NumberVector.Factory<V, ?> factory;
@Override
protected void makeOptions(Parameterization config) {
@@ -402,21 +389,31 @@ public class NumberVectorLabelParser<V extends NumberVector<V, ?>> extends Abstr
getFactory(config);
}
+ /**
+ * Get the object factory.
+ *
+ * @param config Parameterization
+ */
protected void getFactory(Parameterization config) {
- ObjectParameter<V> factoryP = new ObjectParameter<V>(VECTOR_TYPE_ID, NumberVector.class, DoubleVector.class);
- if(config.grab(factoryP)) {
+ ObjectParameter<NumberVector.Factory<V, ?>> factoryP = new ObjectParameter<NumberVector.Factory<V, ?>>(VECTOR_TYPE_ID, NumberVector.Factory.class, DoubleVector.Factory.class);
+ if (config.grab(factoryP)) {
factory = factoryP.instantiateClass(config);
}
}
+ /**
+ * Get the label indices.
+ *
+ * @param config Parameterization
+ */
protected void getLabelIndices(Parameterization config) {
IntListParameter labelIndicesP = new IntListParameter(LABEL_INDICES_ID, true);
- labelIndices = new BitSet();
- if(config.grab(labelIndicesP)) {
+ if (config.grab(labelIndicesP)) {
+ labelIndices = new BitSet();
List<Integer> labelcols = labelIndicesP.getValue();
- for(Integer idx : labelcols) {
- labelIndices.set(idx);
+ for (Integer idx : labelcols) {
+ labelIndices.set(idx.intValue());
}
}
}
@@ -426,4 +423,4 @@ public class NumberVectorLabelParser<V extends NumberVector<V, ?>> extends Abstr
return new NumberVectorLabelParser<V>(colSep, quoteChar, labelIndices, factory);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/parser/Parser.java b/src/de/lmu/ifi/dbs/elki/datasource/parser/Parser.java
index 1f078d19..5b511a92 100644
--- a/src/de/lmu/ifi/dbs/elki/datasource/parser/Parser.java
+++ b/src/de/lmu/ifi/dbs/elki/datasource/parser/Parser.java
@@ -36,7 +36,7 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable;
*
* @apiviz.landmark
* @apiviz.uses InputStream
- * @apiviz.uses MultipleObjectsBundle oneway - - «create»
+ * @apiviz.has MultipleObjectsBundle oneway - - «create»
*/
public interface Parser extends Parameterizable, InspectionUtilFrequentlyScanned {
/**
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/parser/SimplePolygonParser.java b/src/de/lmu/ifi/dbs/elki/datasource/parser/SimplePolygonParser.java
index baa6f0ec..ce366b9e 100644
--- a/src/de/lmu/ifi/dbs/elki/datasource/parser/SimplePolygonParser.java
+++ b/src/de/lmu/ifi/dbs/elki/datasource/parser/SimplePolygonParser.java
@@ -41,7 +41,10 @@ import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
import de.lmu.ifi.dbs.elki.datasource.bundle.MultipleObjectsBundle;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.StringLengthConstraint;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.PatternParameter;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.StringParameter;
/**
* Parser to load polygon data (2D and 3D only) from a simple format. One record
@@ -59,7 +62,7 @@ public class SimplePolygonParser extends AbstractParser implements Parser {
/**
* Class logger
*/
- private static final Logging logger = Logging.getLogger(SimplePolygonParser.class);
+ private static final Logging LOG = Logging.getLogger(SimplePolygonParser.class);
/**
* Pattern to catch coordinates
@@ -132,7 +135,7 @@ public class SimplePolygonParser extends AbstractParser implements Parser {
ExternalID eid = null;
LabelList labels = null;
- List<Polygon> polys = new java.util.Vector<Polygon>(1);
+ List<Polygon> polys = new ArrayList<Polygon>(1);
List<Vector> coords = new ArrayList<Vector>();
while(iter.hasNext()) {
@@ -152,7 +155,7 @@ public class SimplePolygonParser extends AbstractParser implements Parser {
continue;
}
catch(NumberFormatException e) {
- logger.warning("Looked like a coordinate pair but didn't parse: " + cur);
+ LOG.warning("Looked like a coordinate pair but didn't parse: " + cur);
}
}
// Polygon separator.
@@ -184,7 +187,7 @@ public class SimplePolygonParser extends AbstractParser implements Parser {
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -197,7 +200,15 @@ public class SimplePolygonParser extends AbstractParser implements Parser {
public static class Parameterizer extends AbstractParser.Parameterizer {
@Override
protected void makeOptions(Parameterization config) {
- super.makeOptions(config);
+ PatternParameter colParam = new PatternParameter(COLUMN_SEPARATOR_ID, "\\s+");
+ if(config.grab(colParam)) {
+ colSep = colParam.getValue();
+ }
+ StringParameter quoteParam = new StringParameter(QUOTE_ID, String.valueOf(QUOTE_CHAR));
+ quoteParam.addConstraint(new StringLengthConstraint(1, 1));
+ if(config.grab(quoteParam)) {
+ quoteChar = quoteParam.getValue().charAt(0);
+ }
}
@Override
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/parser/SparseBitVectorLabelParser.java b/src/de/lmu/ifi/dbs/elki/datasource/parser/SparseBitVectorLabelParser.java
index 70188d38..35e53bb7 100644
--- a/src/de/lmu/ifi/dbs/elki/datasource/parser/SparseBitVectorLabelParser.java
+++ b/src/de/lmu/ifi/dbs/elki/datasource/parser/SparseBitVectorLabelParser.java
@@ -40,7 +40,6 @@ import de.lmu.ifi.dbs.elki.datasource.bundle.MultipleObjectsBundle;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.utilities.documentation.Description;
import de.lmu.ifi.dbs.elki.utilities.documentation.Title;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
/**
* Provides a parser for parsing one sparse BitVector per line, where the
@@ -60,7 +59,7 @@ public class SparseBitVectorLabelParser extends AbstractParser implements Parser
/**
* Class logger
*/
- private static final Logging logger = Logging.getLogger(SparseBitVectorLabelParser.class);
+ private static final Logging LOG = Logging.getLogger(SparseBitVectorLabelParser.class);
/**
* Constructor.
@@ -90,7 +89,7 @@ public class SparseBitVectorLabelParser extends AbstractParser implements Parser
for(String entry : entries) {
try {
- Integer index = Integer.valueOf(entry);
+ int index = Integer.parseInt(entry);
bitSet.set(index);
dimensionality = Math.max(dimensionality, index);
}
@@ -122,12 +121,12 @@ public class SparseBitVectorLabelParser extends AbstractParser implements Parser
}
protected VectorFieldTypeInformation<BitVector> getTypeInformation(int dimensionality) {
- return new VectorFieldTypeInformation<BitVector>(BitVector.class, dimensionality, new BitVector(new BitSet(), dimensionality));
+ return new VectorFieldTypeInformation<BitVector>(BitVector.FACTORY, dimensionality);
}
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -139,11 +138,6 @@ public class SparseBitVectorLabelParser extends AbstractParser implements Parser
*/
public static class Parameterizer extends AbstractParser.Parameterizer {
@Override
- protected void makeOptions(Parameterization config) {
- super.makeOptions(config);
- }
-
- @Override
protected SparseBitVectorLabelParser makeInstance() {
return new SparseBitVectorLabelParser(colSep, quoteChar);
}
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/parser/SparseFloatVectorLabelParser.java b/src/de/lmu/ifi/dbs/elki/datasource/parser/SparseFloatVectorLabelParser.java
index d0ab7a85..9f658b0a 100644
--- a/src/de/lmu/ifi/dbs/elki/datasource/parser/SparseFloatVectorLabelParser.java
+++ b/src/de/lmu/ifi/dbs/elki/datasource/parser/SparseFloatVectorLabelParser.java
@@ -78,7 +78,7 @@ public class SparseFloatVectorLabelParser extends SparseNumberVectorLabelParser<
* @param labelIndices Label indexes
*/
public SparseFloatVectorLabelParser(Pattern colSep, char quoteChar, BitSet labelIndices) {
- super(colSep, quoteChar, labelIndices, SparseFloatVector.STATIC);
+ super(colSep, quoteChar, labelIndices, SparseFloatVector.FACTORY);
}
/**
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/parser/SparseNumberVectorLabelParser.java b/src/de/lmu/ifi/dbs/elki/datasource/parser/SparseNumberVectorLabelParser.java
index 917dd2aa..f4ec8c59 100644
--- a/src/de/lmu/ifi/dbs/elki/datasource/parser/SparseNumberVectorLabelParser.java
+++ b/src/de/lmu/ifi/dbs/elki/datasource/parser/SparseNumberVectorLabelParser.java
@@ -30,7 +30,6 @@ import java.util.List;
import java.util.regex.Pattern;
import de.lmu.ifi.dbs.elki.data.LabelList;
-import de.lmu.ifi.dbs.elki.data.SparseDoubleVector;
import de.lmu.ifi.dbs.elki.data.SparseFloatVector;
import de.lmu.ifi.dbs.elki.data.SparseNumberVector;
import de.lmu.ifi.dbs.elki.data.type.SimpleTypeInformation;
@@ -74,15 +73,17 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ObjectParameter;
* @author Arthur Zimek
*
* @apiviz.has SparseNumberVector
+ *
+ * @param <V> vector type
*/
// FIXME: Maxdim!
@Title("Sparse Vector Label Parser")
@Description("Parser for the following line format:\n" + "A single line provides a single point. Entries are separated by whitespace. " + "The values will be parsed as floats (resulting in a set of SparseFloatVectors). A line is expected in the following format: The first entry of each line is the number of attributes with coordinate value not zero. Subsequent entries are of the form (index, value), where index is the number of the corresponding dimension, and value is the value of the corresponding attribute." + "Any pair of two subsequent substrings not containing whitespace is tried to be read as int and float. If this fails for the first of the pair (interpreted ans index), it will be appended to a label. (Thus, any label must not be parseable as Integer.) If the float component is not parseable, an exception will be thrown. Empty lines and lines beginning with \"#\" will be ignored.")
-public class SparseNumberVectorLabelParser<V extends SparseNumberVector<V, ?>> extends NumberVectorLabelParser<V> {
+public class SparseNumberVectorLabelParser<V extends SparseNumberVector<?>> extends NumberVectorLabelParser<V> {
/**
- * Class logger
+ * Class logger.
*/
- private static final Logging logger = Logging.getLogger(SparseNumberVectorLabelParser.class);
+ private static final Logging LOG = Logging.getLogger(SparseNumberVectorLabelParser.class);
/**
* Holds the dimensionality of the parsed data which is the maximum occurring
@@ -91,6 +92,11 @@ public class SparseNumberVectorLabelParser<V extends SparseNumberVector<V, ?>> e
private int maxdim = -1;
/**
+ * Same as {@link #factory}, but subtype.
+ */
+ private SparseNumberVector.Factory<V, ?> sparsefactory;
+
+ /**
* Constructor.
*
* @param colSep Column separator
@@ -98,8 +104,9 @@ public class SparseNumberVectorLabelParser<V extends SparseNumberVector<V, ?>> e
* @param labelIndices Label indexes
* @param factory Vector factory
*/
- public SparseNumberVectorLabelParser(Pattern colSep, char quoteChar, BitSet labelIndices, V factory) {
+ public SparseNumberVectorLabelParser(Pattern colSep, char quoteChar, BitSet labelIndices, SparseNumberVector.Factory<V, ?> factory) {
super(colSep, quoteChar, labelIndices, factory);
+ this.sparsefactory = factory;
}
@Override
@@ -110,55 +117,51 @@ public class SparseNumberVectorLabelParser<V extends SparseNumberVector<V, ?>> e
TIntDoubleHashMap values = new TIntDoubleHashMap(cardinality, 1);
LabelList labels = null;
- for(int i = 1; i < entries.size() - 1; i++) {
- if(!labelIndices.get(i)) {
+ for (int i = 1; i < entries.size() - 1; i++) {
+ if (labelIndices == null || !labelIndices.get(i)) {
try {
- int index = Integer.valueOf(entries.get(i));
- if(index >= maxdim) {
+ int index = Integer.parseInt(entries.get(i));
+ if (index >= maxdim) {
maxdim = index + 1;
}
- double attribute = Double.valueOf(entries.get(i));
+ double attribute = Double.parseDouble(entries.get(i));
values.put(index, attribute);
i++;
- }
- catch(NumberFormatException e) {
- if(labels == null) {
+ } catch (NumberFormatException e) {
+ if (labels == null) {
labels = new LabelList(1);
}
labels.add(entries.get(i));
continue;
}
- }
- else {
- if(labels == null) {
+ } else {
+ if (labels == null) {
labels = new LabelList(1);
}
labels.add(entries.get(i));
}
}
- if(values.size() > maxdim) {
+ if (values.size() > maxdim) {
throw new AbortException("Invalid sparse vector seen: " + line);
}
- curvec = factory.newNumberVector(values, maxdim);
+ curvec = sparsefactory.newNumberVector(values, maxdim);
curlbl = labels;
}
@Override
protected SimpleTypeInformation<V> getTypeInformation(int dimensionality) {
- @SuppressWarnings("unchecked")
- Class<V> cls = (Class<V>) factory.getClass();
- if(dimensionality > 0) {
- return new VectorFieldTypeInformation<V>(cls, dimensionality, factory.newNumberVector(SparseDoubleVector.EMPTYMAP, dimensionality));
+ if (dimensionality > 0) {
+ return new VectorFieldTypeInformation<V>(factory, dimensionality);
}
- if(dimensionality == DIMENSIONALITY_VARIABLE) {
- return new SimpleTypeInformation<V>(cls);
+ if (dimensionality == DIMENSIONALITY_VARIABLE) {
+ return new SimpleTypeInformation<V>(factory.getRestrictionClass(), factory.getDefaultSerializer());
}
throw new AbortException("No vectors were read from the input file - cannot determine vector data type.");
}
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -168,18 +171,18 @@ public class SparseNumberVectorLabelParser<V extends SparseNumberVector<V, ?>> e
*
* @apiviz.exclude
*/
- public static class Parameterizer<V extends SparseNumberVector<V, ?>> extends NumberVectorLabelParser.Parameterizer<V> {
+ public static class Parameterizer<V extends SparseNumberVector<?>> extends NumberVectorLabelParser.Parameterizer<V> {
@Override
protected void getFactory(Parameterization config) {
- ObjectParameter<V> factoryP = new ObjectParameter<V>(VECTOR_TYPE_ID, SparseNumberVector.class, SparseFloatVector.class);
- if(config.grab(factoryP)) {
+ ObjectParameter<SparseNumberVector.Factory<V, ?>> factoryP = new ObjectParameter<SparseNumberVector.Factory<V, ?>>(VECTOR_TYPE_ID, SparseNumberVector.Factory.class, SparseFloatVector.Factory.class);
+ if (config.grab(factoryP)) {
factory = factoryP.instantiateClass(config);
}
}
@Override
protected SparseNumberVectorLabelParser<V> makeInstance() {
- return new SparseNumberVectorLabelParser<V>(colSep, quoteChar, labelIndices, factory);
+ return new SparseNumberVectorLabelParser<V>(colSep, quoteChar, labelIndices, (SparseNumberVector.Factory<V, ?>) factory);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/parser/TermFrequencyParser.java b/src/de/lmu/ifi/dbs/elki/datasource/parser/TermFrequencyParser.java
index bb277b17..2ea6ebb5 100644
--- a/src/de/lmu/ifi/dbs/elki/datasource/parser/TermFrequencyParser.java
+++ b/src/de/lmu/ifi/dbs/elki/datasource/parser/TermFrequencyParser.java
@@ -23,8 +23,8 @@ package de.lmu.ifi.dbs.elki.datasource.parser;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import gnu.trove.iterator.TIntFloatIterator;
-import gnu.trove.map.hash.TIntFloatHashMap;
+import gnu.trove.iterator.TIntDoubleIterator;
+import gnu.trove.map.hash.TIntDoubleHashMap;
import java.util.BitSet;
import java.util.HashMap;
@@ -33,6 +33,7 @@ import java.util.regex.Pattern;
import de.lmu.ifi.dbs.elki.data.LabelList;
import de.lmu.ifi.dbs.elki.data.SparseFloatVector;
+import de.lmu.ifi.dbs.elki.data.SparseNumberVector;
import de.lmu.ifi.dbs.elki.data.type.SimpleTypeInformation;
import de.lmu.ifi.dbs.elki.data.type.VectorFieldTypeInformation;
import de.lmu.ifi.dbs.elki.logging.Logging;
@@ -42,6 +43,7 @@ import de.lmu.ifi.dbs.elki.utilities.exceptions.AbortException;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.Flag;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ObjectParameter;
/**
* A parser to load term frequency data, which essentially are sparse vectors
@@ -53,28 +55,33 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.Flag;
*/
@Title("Term frequency parser")
@Description("Parse a file containing term frequencies. The expected format is 'label term1 <freq> term2 <freq> ...'. Terms must not contain the separator character!")
-public class TermFrequencyParser extends NumberVectorLabelParser<SparseFloatVector> {
+public class TermFrequencyParser<V extends SparseNumberVector<?>> extends NumberVectorLabelParser<V> {
/**
- * Class logger
+ * Class logger.
*/
- private static final Logging logger = Logging.getLogger(TermFrequencyParser.class);
+ private static final Logging LOG = Logging.getLogger(TermFrequencyParser.class);
/**
- * Maximum dimension used
+ * Maximum dimension used.
*/
int maxdim;
/**
- * Map
+ * Map.
*/
HashMap<String, Integer> keymap;
/**
- * Normalize
+ * Normalize.
*/
boolean normalize;
/**
+ * Same as {@link #factory}, but subtype.
+ */
+ private SparseNumberVector.Factory<V, ?> sparsefactory;
+
+ /**
* Constructor.
*
* @param normalize Normalize
@@ -82,11 +89,12 @@ public class TermFrequencyParser extends NumberVectorLabelParser<SparseFloatVect
* @param quoteChar
* @param labelIndices
*/
- public TermFrequencyParser(boolean normalize, Pattern colSep, char quoteChar, BitSet labelIndices) {
- super(colSep, quoteChar, labelIndices, SparseFloatVector.STATIC);
+ public TermFrequencyParser(boolean normalize, Pattern colSep, char quoteChar, BitSet labelIndices, SparseNumberVector.Factory<V, ?> factory) {
+ super(colSep, quoteChar, labelIndices, factory);
this.normalize = normalize;
this.maxdim = 0;
this.keymap = new HashMap<String, Integer>();
+ this.sparsefactory = factory;
}
@Override
@@ -94,30 +102,28 @@ public class TermFrequencyParser extends NumberVectorLabelParser<SparseFloatVect
List<String> entries = tokenize(line);
double len = 0;
- TIntFloatHashMap values = new TIntFloatHashMap();
+ TIntDoubleHashMap values = new TIntDoubleHashMap();
LabelList labels = null;
String curterm = null;
- for(int i = 0; i < entries.size(); i++) {
- if(curterm == null) {
+ for (int i = 0; i < entries.size(); i++) {
+ if (curterm == null) {
curterm = entries.get(i);
- }
- else {
+ } else {
try {
- float attribute = Float.valueOf(entries.get(i));
+ double attribute = Double.parseDouble(entries.get(i));
Integer curdim = keymap.get(curterm);
- if(curdim == null) {
- curdim = maxdim + 1;
+ if (curdim == null) {
+ curdim = Integer.valueOf(maxdim + 1);
keymap.put(curterm, curdim);
maxdim += 1;
}
values.put(curdim, attribute);
len += attribute;
curterm = null;
- }
- catch(NumberFormatException e) {
- if(curterm != null) {
- if(labels == null) {
+ } catch (NumberFormatException e) {
+ if (curterm != null) {
+ if (labels == null) {
labels = new LabelList(1);
}
labels.add(curterm);
@@ -126,39 +132,39 @@ public class TermFrequencyParser extends NumberVectorLabelParser<SparseFloatVect
}
}
}
- if(curterm != null) {
- if(labels == null) {
+ if (curterm != null) {
+ if (labels == null) {
labels = new LabelList(1);
}
labels.add(curterm);
}
- if(normalize) {
- if(Math.abs(len - 1.0) > 1E-10 && len > 1E-10) {
- for(TIntFloatIterator iter = values.iterator(); iter.hasNext();) {
+ if (normalize) {
+ if (Math.abs(len - 1.0) > 1E-10 && len > 1E-10) {
+ for (TIntDoubleIterator iter = values.iterator(); iter.hasNext();) {
iter.advance();
- iter.setValue((float) (iter.value() / len));
+ iter.setValue(iter.value() / len);
}
}
}
- curvec = new SparseFloatVector(values, maxdim);
+ curvec = sparsefactory.newNumberVector(values, maxdim);
curlbl = labels;
}
@Override
- protected SimpleTypeInformation<SparseFloatVector> getTypeInformation(int dimensionality) {
- if(dimensionality > 0) {
- return new VectorFieldTypeInformation<SparseFloatVector>(SparseFloatVector.class, dimensionality, new SparseFloatVector(SparseFloatVector.EMPTYMAP, dimensionality));
+ protected SimpleTypeInformation<V> getTypeInformation(int dimensionality) {
+ if (dimensionality > 0) {
+ return new VectorFieldTypeInformation<V>(factory, dimensionality);
}
- if(dimensionality == DIMENSIONALITY_VARIABLE) {
- return new SimpleTypeInformation<SparseFloatVector>(SparseFloatVector.class);
+ if (dimensionality == DIMENSIONALITY_VARIABLE) {
+ return new SimpleTypeInformation<V>(factory.getRestrictionClass(), factory.getDefaultSerializer());
}
throw new AbortException("No vectors were read from the input file - cannot determine vector data type.");
}
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -168,14 +174,14 @@ public class TermFrequencyParser extends NumberVectorLabelParser<SparseFloatVect
*
* @apiviz.exclude
*/
- public static class Parameterizer extends NumberVectorLabelParser.Parameterizer<SparseFloatVector> {
+ public static class Parameterizer<V extends SparseNumberVector<?>> extends NumberVectorLabelParser.Parameterizer<V> {
/**
- * Option ID for normalization
+ * Option ID for normalization.
*/
- public static final OptionID NORMALIZE_FLAG = OptionID.getOrCreateOptionID("tf.normalize", "Normalize vectors to manhattan length 1 (convert term counts to term frequencies)");
+ public static final OptionID NORMALIZE_FLAG = new OptionID("tf.normalize", "Normalize vectors to manhattan length 1 (convert term counts to term frequencies)");
/**
- * Normalization flag
+ * Normalization flag.
*/
boolean normalize = false;
@@ -183,14 +189,22 @@ public class TermFrequencyParser extends NumberVectorLabelParser<SparseFloatVect
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
Flag normF = new Flag(NORMALIZE_FLAG);
- if(config.grab(normF)) {
- normalize = normF.getValue();
+ if (config.grab(normF)) {
+ normalize = normF.getValue().booleanValue();
+ }
+ }
+
+ @Override
+ protected void getFactory(Parameterization config) {
+ ObjectParameter<SparseNumberVector.Factory<V, ?>> factoryP = new ObjectParameter<SparseNumberVector.Factory<V, ?>>(VECTOR_TYPE_ID, SparseNumberVector.Factory.class, SparseFloatVector.Factory.class);
+ if (config.grab(factoryP)) {
+ factory = factoryP.instantiateClass(config);
}
}
@Override
- protected TermFrequencyParser makeInstance() {
- return new TermFrequencyParser(normalize, colSep, quoteChar, labelIndices);
+ protected TermFrequencyParser<V> makeInstance() {
+ return new TermFrequencyParser<V>(normalize, colSep, quoteChar, labelIndices, (SparseNumberVector.Factory<V, ?>) factory);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/datasource/parser/package-info.java b/src/de/lmu/ifi/dbs/elki/datasource/parser/package-info.java
index f1999262..58ae9a77 100644
--- a/src/de/lmu/ifi/dbs/elki/datasource/parser/package-info.java
+++ b/src/de/lmu/ifi/dbs/elki/datasource/parser/package-info.java
@@ -34,6 +34,9 @@
* <p>As an example file following these requirements consider e.g.:
* <a href="http://www.dbs.ifi.lmu.de/research/KDD/ELKI/datasets/example/exampledata.txt">exampledata.txt</a>
* </p>
+ *
+ * @apiviz.exclude java.io.*
+ * @apiviz.exclude de.lmu.ifi.dbs.elki.utilities.*
*/
/*
This file is part of ELKI:
@@ -57,4 +60,4 @@ GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package de.lmu.ifi.dbs.elki.datasource.parser; \ No newline at end of file
+package de.lmu.ifi.dbs.elki.datasource.parser;
diff --git a/src/de/lmu/ifi/dbs/elki/distance/DistanceUtil.java b/src/de/lmu/ifi/dbs/elki/distance/DistanceUtil.java
index 7a394b8b..eaa6c96c 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/DistanceUtil.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/DistanceUtil.java
@@ -23,7 +23,10 @@ package de.lmu.ifi.dbs.elki.distance;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
+import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
+import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
/**
* Class with distance related utility functions.
@@ -89,4 +92,26 @@ public final class DistanceUtil {
return d1;
}
}
+
+ /**
+ * Test whether a distance function is double-valued.
+ *
+ * @param df Distance function
+ * @return True when the distance function returns double values
+ */
+ public static boolean isDoubleDistanceFunction(DistanceFunction<?, ?> df) {
+ Object factory = df.getDistanceFactory();
+ return (factory == DoubleDistance.FACTORY) || (factory instanceof DoubleDistance);
+ }
+
+ /**
+ * Test whether a distance query is double-valued.
+ *
+ * @param df Distance function
+ * @return True when the distance function returns double values
+ */
+ public static boolean isDoubleDistanceFunction(DistanceQuery<?, ?> df) {
+ Object factory = df.getDistanceFactory();
+ return (factory == DoubleDistance.FACTORY) || (factory instanceof DoubleDistance);
+ }
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/AbstractIndexBasedDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/AbstractIndexBasedDistanceFunction.java
index 9280b783..48947692 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/AbstractIndexBasedDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/AbstractIndexBasedDistanceFunction.java
@@ -133,7 +133,7 @@ public abstract class AbstractIndexBasedDistanceFunction<O, I extends Index, D e
*
* @param <F> Factory type
*/
- public static abstract class Parameterizer<F extends IndexFactory<?, ?>> extends AbstractParameterizer {
+ public abstract static class Parameterizer<F extends IndexFactory<?, ?>> extends AbstractParameterizer {
/**
* The index factory we use.
*/
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/AbstractVectorDoubleDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/AbstractVectorDoubleDistanceFunction.java
index dbda2e96..24288ec1 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/AbstractVectorDoubleDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/AbstractVectorDoubleDistanceFunction.java
@@ -38,7 +38,7 @@ import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
* @apiviz.uses NumberVector
* @apiviz.has DoubleDistance
*/
-public abstract class AbstractVectorDoubleDistanceFunction extends AbstractPrimitiveDistanceFunction<NumberVector<?, ?>, DoubleDistance> implements PrimitiveDoubleDistanceFunction<NumberVector<?, ?>>, NumberVectorDistanceFunction<DoubleDistance> {
+public abstract class AbstractVectorDoubleDistanceFunction extends AbstractPrimitiveDistanceFunction<NumberVector<?>, DoubleDistance> implements PrimitiveDoubleDistanceFunction<NumberVector<?>>, NumberVectorDistanceFunction<DoubleDistance> {
/**
* Constructor.
*/
@@ -47,12 +47,12 @@ public abstract class AbstractVectorDoubleDistanceFunction extends AbstractPrimi
}
@Override
- public SimpleTypeInformation<? super NumberVector<?, ?>> getInputTypeRestriction() {
+ public SimpleTypeInformation<? super NumberVector<?>> getInputTypeRestriction() {
return TypeUtil.NUMBER_VECTOR_FIELD;
}
@Override
- public final DoubleDistance distance(NumberVector<?, ?> o1, NumberVector<?, ?> o2) {
+ public final DoubleDistance distance(NumberVector<?> o1, NumberVector<?> o2) {
return new DoubleDistance(doubleDistance(o1, o2));
}
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/AbstractVectorDoubleDistanceNorm.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/AbstractVectorDoubleDistanceNorm.java
index 7a7785e1..16d4160c 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/AbstractVectorDoubleDistanceNorm.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/AbstractVectorDoubleDistanceNorm.java
@@ -32,9 +32,9 @@ import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
*
* @author Erich Schubert
*/
-public abstract class AbstractVectorDoubleDistanceNorm extends AbstractVectorDoubleDistanceFunction implements DoubleNorm<NumberVector<?, ?>> {
+public abstract class AbstractVectorDoubleDistanceNorm extends AbstractVectorDoubleDistanceFunction implements DoubleNorm<NumberVector<?>> {
@Override
- public DoubleDistance norm(NumberVector<?, ?> obj) {
+ public DoubleDistance norm(NumberVector<?> obj) {
return new DoubleDistance(doubleNorm(obj));
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/ArcCosineDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/ArcCosineDistanceFunction.java
index 03563c6f..344bb3ca 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/ArcCosineDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/ArcCosineDistanceFunction.java
@@ -42,7 +42,7 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
*
* @author Arthur Zimek
*/
-public class ArcCosineDistanceFunction extends AbstractVectorDoubleDistanceFunction implements SpatialPrimitiveDoubleDistanceFunction<NumberVector<?, ?>> {
+public class ArcCosineDistanceFunction extends AbstractVectorDoubleDistanceFunction implements SpatialPrimitiveDoubleDistanceFunction<NumberVector<?>> {
/**
* Static instance
*/
@@ -69,7 +69,7 @@ public class ArcCosineDistanceFunction extends AbstractVectorDoubleDistanceFunct
* @return the cosine distance for two given feature vectors v1 and v2
*/
@Override
- public double doubleDistance(NumberVector<?, ?> v1, NumberVector<?, ?> v2) {
+ public double doubleDistance(NumberVector<?> v1, NumberVector<?> v2) {
double d = Math.acos(VectorUtil.cosAngle(v1, v2));
if(d < 0) {
d = 0;
@@ -108,12 +108,12 @@ public class ArcCosineDistanceFunction extends AbstractVectorDoubleDistanceFunct
}
@Override
- public <T extends NumberVector<?, ?>> SpatialDistanceQuery<T, DoubleDistance> instantiate(Relation<T> relation) {
+ public <T extends NumberVector<?>> SpatialDistanceQuery<T, DoubleDistance> instantiate(Relation<T> relation) {
return new SpatialPrimitiveDistanceQuery<T, DoubleDistance>(relation, this);
}
@Override
- public SimpleTypeInformation<? super NumberVector<?, ?>> getInputTypeRestriction() {
+ public SimpleTypeInformation<? super NumberVector<?>> getInputTypeRestriction() {
return TypeUtil.NUMBER_VECTOR_VARIABLE_LENGTH;
}
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/CanberraDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/CanberraDistanceFunction.java
index 6c8647ab..f6db7439 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/CanberraDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/CanberraDistanceFunction.java
@@ -45,7 +45,7 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
* @author Erich Schubert
*/
@Reference(authors = "G. N. Lance, W. T. Williams", title = "Computer programs for hierarchical polythetic classification (similarity analysis).", booktitle = "Computer Journal, Volume 9, Issue 1", url = "http://comjnl.oxfordjournals.org/content/9/1/60.short")
-public class CanberraDistanceFunction extends AbstractVectorDoubleDistanceFunction implements SpatialPrimitiveDoubleDistanceFunction<NumberVector<?, ?>> {
+public class CanberraDistanceFunction extends AbstractVectorDoubleDistanceFunction implements SpatialPrimitiveDoubleDistanceFunction<NumberVector<?>> {
/**
* Static instance. Use this!
*/
@@ -59,10 +59,10 @@ public class CanberraDistanceFunction extends AbstractVectorDoubleDistanceFuncti
}
@Override
- public double doubleDistance(NumberVector<?, ?> o1, NumberVector<?, ?> o2) {
+ public double doubleDistance(NumberVector<?> o1, NumberVector<?> o2) {
final int dim = o1.getDimensionality();
double sum = 0.0;
- for(int i = 1; i <= dim; i++) {
+ for(int i = 0; i < dim; i++) {
double v1 = o1.doubleValue(i);
double v2 = o2.doubleValue(i);
final double div = Math.abs(v1) + Math.abs(v2);
@@ -77,7 +77,7 @@ public class CanberraDistanceFunction extends AbstractVectorDoubleDistanceFuncti
public double doubleMinDist(SpatialComparable mbr1, SpatialComparable mbr2) {
final int dim = mbr1.getDimensionality();
double sum = 0.0;
- for(int d = 1; d <= dim; d++) {
+ for(int d = 0; d < dim; d++) {
final double m1, m2;
if(mbr1.getMax(d) < mbr2.getMin(d)) {
m1 = mbr2.getMin(d);
@@ -107,7 +107,7 @@ public class CanberraDistanceFunction extends AbstractVectorDoubleDistanceFuncti
}
@Override
- public <T extends NumberVector<?, ?>> SpatialDistanceQuery<T, DoubleDistance> instantiate(Relation<T> relation) {
+ public <T extends NumberVector<?>> SpatialDistanceQuery<T, DoubleDistance> instantiate(Relation<T> relation) {
return new SpatialPrimitiveDistanceQuery<T, DoubleDistance>(relation, this);
}
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/CosineDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/CosineDistanceFunction.java
index 2d681dad..f333f6e2 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/CosineDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/CosineDistanceFunction.java
@@ -42,7 +42,7 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
*
* @author Arthur Zimek
*/
-public class CosineDistanceFunction extends AbstractVectorDoubleDistanceFunction implements SpatialPrimitiveDoubleDistanceFunction<NumberVector<?, ?>> {
+public class CosineDistanceFunction extends AbstractVectorDoubleDistanceFunction implements SpatialPrimitiveDoubleDistanceFunction<NumberVector<?>> {
/**
* Static instance
*/
@@ -69,7 +69,7 @@ public class CosineDistanceFunction extends AbstractVectorDoubleDistanceFunction
* @return the cosine distance for two given feature vectors v1 and v2
*/
@Override
- public double doubleDistance(NumberVector<?, ?> v1, NumberVector<?, ?> v2) {
+ public double doubleDistance(NumberVector<?> v1, NumberVector<?> v2) {
double d = 1 - VectorUtil.cosAngle(v1, v2);
if(d < 0) {
d = 0;
@@ -108,12 +108,12 @@ public class CosineDistanceFunction extends AbstractVectorDoubleDistanceFunction
}
@Override
- public <T extends NumberVector<?, ?>> SpatialDistanceQuery<T, DoubleDistance> instantiate(Relation<T> relation) {
+ public <T extends NumberVector<?>> SpatialDistanceQuery<T, DoubleDistance> instantiate(Relation<T> relation) {
return new SpatialPrimitiveDistanceQuery<T, DoubleDistance>(relation, this);
}
@Override
- public SimpleTypeInformation<? super NumberVector<?, ?>> getInputTypeRestriction() {
+ public SimpleTypeInformation<? super NumberVector<?>> getInputTypeRestriction() {
return TypeUtil.NUMBER_VECTOR_VARIABLE_LENGTH;
}
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/EuclideanDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/EuclideanDistanceFunction.java
index c0bc5696..6bb910a4 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/EuclideanDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/EuclideanDistanceFunction.java
@@ -32,7 +32,7 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
*
* @author Arthur Zimek
*/
-public class EuclideanDistanceFunction extends LPNormDistanceFunction implements SpatialPrimitiveDoubleDistanceFunction<NumberVector<?, ?>> {
+public class EuclideanDistanceFunction extends LPNormDistanceFunction implements SpatialPrimitiveDoubleDistanceFunction<NumberVector<?>> {
/**
* Static instance. Use this!
*/
@@ -49,20 +49,14 @@ public class EuclideanDistanceFunction extends LPNormDistanceFunction implements
super(2.0);
}
- /**
- * Provides the Euclidean distance between the given two vectors.
- *
- * @return the Euclidean distance between the given two vectors as raw double
- * value
- */
@Override
- public double doubleDistance(NumberVector<?, ?> v1, NumberVector<?, ?> v2) {
+ public double doubleDistance(NumberVector<?> v1, NumberVector<?> v2) {
final int dim1 = v1.getDimensionality();
if(dim1 != v2.getDimensionality()) {
throw new IllegalArgumentException("Different dimensionality of FeatureVectors" + "\n first argument: " + v1.toString() + "\n second argument: " + v2.toString() + "\n" + v1.getDimensionality() + "!=" + v2.getDimensionality());
}
double sqrDist = 0;
- for(int i = 1; i <= dim1; i++) {
+ for(int i = 0; i < dim1; i++) {
final double delta = v1.doubleValue(i) - v2.doubleValue(i);
sqrDist += delta * delta;
}
@@ -70,24 +64,24 @@ public class EuclideanDistanceFunction extends LPNormDistanceFunction implements
}
@Override
- public double doubleNorm(NumberVector<?, ?> v) {
+ public double doubleNorm(NumberVector<?> v) {
final int dim = v.getDimensionality();
double sqrDist = 0;
- for(int i = 1; i <= dim; i++) {
+ for(int i = 0; i < dim; i++) {
final double delta = v.doubleValue(i);
sqrDist += delta * delta;
}
return Math.sqrt(sqrDist);
}
- protected double doubleMinDistObject(SpatialComparable mbr, NumberVector<?, ?> v) {
+ protected double doubleMinDistObject(SpatialComparable mbr, NumberVector<?> v) {
final int dim = mbr.getDimensionality();
if(dim != v.getDimensionality()) {
throw new IllegalArgumentException("Different dimensionality of objects\n " + "first argument: " + mbr.toString() + "\n " + "second argument: " + v.toString() + "\n" + dim + "!=" + v.getDimensionality());
}
double sqrDist = 0;
- for(int d = 1; d <= dim; d++) {
+ for(int d = 0; d < dim; d++) {
double value = v.doubleValue(d);
double r;
if(value < mbr.getMin(d)) {
@@ -111,14 +105,14 @@ public class EuclideanDistanceFunction extends LPNormDistanceFunction implements
// Some optimizations for simpler cases.
if(mbr1 instanceof NumberVector) {
if(mbr2 instanceof NumberVector) {
- return doubleDistance((NumberVector<?, ?>) mbr1, (NumberVector<?, ?>) mbr2);
+ return doubleDistance((NumberVector<?>) mbr1, (NumberVector<?>) mbr2);
}
else {
- return doubleMinDistObject(mbr2, (NumberVector<?, ?>) mbr1);
+ return doubleMinDistObject(mbr2, (NumberVector<?>) mbr1);
}
}
else if(mbr2 instanceof NumberVector) {
- return doubleMinDistObject(mbr1, (NumberVector<?, ?>) mbr2);
+ return doubleMinDistObject(mbr1, (NumberVector<?>) mbr2);
}
final int dim1 = mbr1.getDimensionality();
if(dim1 != mbr2.getDimensionality()) {
@@ -126,7 +120,7 @@ public class EuclideanDistanceFunction extends LPNormDistanceFunction implements
}
double sqrDist = 0;
- for(int d = 1; d <= dim1; d++) {
+ for(int d = 0; d < dim1; d++) {
final double m1, m2;
if(mbr1.getMax(d) < mbr2.getMin(d)) {
m1 = mbr2.getMin(d);
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/FilteredLocalPCABasedDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/FilteredLocalPCABasedDistanceFunction.java
index 03074815..2cf11a54 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/FilteredLocalPCABasedDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/FilteredLocalPCABasedDistanceFunction.java
@@ -39,7 +39,7 @@ import de.lmu.ifi.dbs.elki.index.preprocessed.localpca.FilteredLocalPCAIndex;
* @param <O> Database object type
* @param <D> Distance type
*/
-public interface FilteredLocalPCABasedDistanceFunction<O extends NumberVector<?, ?>, P extends FilteredLocalPCAIndex<? super O>, D extends Distance<D>> extends IndexBasedDistanceFunction<O, D> {
+public interface FilteredLocalPCABasedDistanceFunction<O extends NumberVector<?>, P extends FilteredLocalPCAIndex<? super O>, D extends Distance<D>> extends IndexBasedDistanceFunction<O, D> {
/**
* Instantiate with a database to get the actual distance query.
*
@@ -58,7 +58,7 @@ public interface FilteredLocalPCABasedDistanceFunction<O extends NumberVector<?,
* @param <I> Index type
* @param <D> Distance type
*/
- public static interface Instance<T extends NumberVector<?, ?>, I extends Index, D extends Distance<D>> extends IndexBasedDistanceFunction.Instance<T, I, D> {
+ public static interface Instance<T extends NumberVector<?>, I extends Index, D extends Distance<D>> extends IndexBasedDistanceFunction.Instance<T, I, D> {
// No additional restrictions
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/IndexBasedDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/IndexBasedDistanceFunction.java
index 8663c8c3..ba7d87d7 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/IndexBasedDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/IndexBasedDistanceFunction.java
@@ -41,7 +41,7 @@ public interface IndexBasedDistanceFunction<O, D extends Distance<D>> extends Di
/**
* OptionID for the index parameter
*/
- public static final OptionID INDEX_ID = OptionID.getOrCreateOptionID("distancefunction.index", "Distance index to use.");
+ public static final OptionID INDEX_ID = new OptionID("distancefunction.index", "Distance index to use.");
/**
* Instance interface for Index based distance functions.
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/JeffreyDivergenceDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/JeffreyDivergenceDistanceFunction.java
index 3aa6908f..1ac5f2a4 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/JeffreyDivergenceDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/JeffreyDivergenceDistanceFunction.java
@@ -32,7 +32,7 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
*
* @author Erich Schubert
*/
-@Reference(authors="J. Puzicha,J.M. Buhmann, Y. Rubner, C. Tomasi", title="Empirical evaluation of dissimilarity measures for color and texture", booktitle="Proc. 7th IEEE International Conference on Computer Vision", url="http://dx.doi.org/10.1109/ICCV.1999.790412")
+@Reference(authors="J. Puzicha, J.M. Buhmann, Y. Rubner, C. Tomasi", title="Empirical evaluation of dissimilarity measures for color and texture", booktitle="Proc. 7th IEEE International Conference on Computer Vision", url="http://dx.doi.org/10.1109/ICCV.1999.790412")
public class JeffreyDivergenceDistanceFunction extends AbstractVectorDoubleDistanceFunction {
/**
* Static instance. Use this!
@@ -49,16 +49,16 @@ public class JeffreyDivergenceDistanceFunction extends AbstractVectorDoubleDista
}
@Override
- public double doubleDistance(NumberVector<?, ?> v1, NumberVector<?, ?> v2) {
+ public double doubleDistance(NumberVector<?> v1, NumberVector<?> v2) {
final int dim1 = v1.getDimensionality();
if(dim1 != v2.getDimensionality()) {
throw new IllegalArgumentException("Different dimensionality of FeatureVectors" + "\n first argument: " + v1.toString() + "\n second argument: " + v2.toString() + "\n" + v1.getDimensionality() + "!=" + v2.getDimensionality());
}
double dist = 0;
- for(int i = 1; i <= dim1; i++) {
+ for(int i = 0; i < dim1; i++) {
final double xi = v1.doubleValue(i);
final double yi = v2.doubleValue(i);
- final double mi = (xi + yi) / 2;
+ final double mi = .5 * (xi + yi);
dist += xi * Math.log(xi / mi);
dist += yi * Math.log(yi / mi);
}
@@ -97,4 +97,4 @@ public class JeffreyDivergenceDistanceFunction extends AbstractVectorDoubleDista
return JeffreyDivergenceDistanceFunction.STATIC;
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/LPNormDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/LPNormDistanceFunction.java
index 134b587f..98b9929a 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/LPNormDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/LPNormDistanceFunction.java
@@ -41,11 +41,11 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.DoubleParameter;
*
* @apiviz.landmark
*/
-public class LPNormDistanceFunction extends AbstractVectorDoubleDistanceNorm implements SpatialPrimitiveDoubleDistanceFunction<NumberVector<?, ?>> {
+public class LPNormDistanceFunction extends AbstractVectorDoubleDistanceNorm implements SpatialPrimitiveDoubleDistanceFunction<NumberVector<?>> {
/**
* OptionID for the "p" parameter
*/
- public static final OptionID P_ID = OptionID.getOrCreateOptionID("lpnorm.p", "the degree of the L-P-Norm (positive number)");
+ public static final OptionID P_ID = new OptionID("lpnorm.p", "the degree of the L-P-Norm (positive number)");
/**
* Keeps the currently set p.
@@ -72,14 +72,14 @@ public class LPNormDistanceFunction extends AbstractVectorDoubleDistanceNorm imp
* the currently set p
*/
@Override
- public double doubleDistance(NumberVector<?, ?> v1, NumberVector<?, ?> v2) {
+ public double doubleDistance(NumberVector<?> v1, NumberVector<?> v2) {
final int dim1 = v1.getDimensionality();
if(dim1 != v2.getDimensionality()) {
throw new IllegalArgumentException("Different dimensionality of FeatureVectors\n first argument: " + v1.toString() + "\n second argument: " + v2.toString());
}
double sqrDist = 0;
- for(int i = 1; i <= dim1; i++) {
+ for(int i = 0; i < dim1; i++) {
final double delta = Math.abs(v1.doubleValue(i) - v2.doubleValue(i));
sqrDist += Math.pow(delta, p);
}
@@ -87,10 +87,10 @@ public class LPNormDistanceFunction extends AbstractVectorDoubleDistanceNorm imp
}
@Override
- public double doubleNorm(NumberVector<?, ?> v) {
+ public double doubleNorm(NumberVector<?> v) {
final int dim = v.getDimensionality();
double sqrDist = 0;
- for(int i = 1; i <= dim; i++) {
+ for(int i = 0; i < dim; i++) {
final double delta = v.doubleValue(i);
sqrDist += Math.pow(delta, p);
}
@@ -111,7 +111,7 @@ public class LPNormDistanceFunction extends AbstractVectorDoubleDistanceNorm imp
// Optimization for the simplest case
if(mbr1 instanceof NumberVector) {
if(mbr2 instanceof NumberVector) {
- return doubleDistance((NumberVector<?, ?>) mbr1, (NumberVector<?, ?>) mbr2);
+ return doubleDistance((NumberVector<?>) mbr1, (NumberVector<?>) mbr2);
}
}
// TODO: optimize for more simpler cases: obj vs. rect?
@@ -121,7 +121,7 @@ public class LPNormDistanceFunction extends AbstractVectorDoubleDistanceNorm imp
}
double sumDist = 0;
- for(int d = 1; d <= dim1; d++) {
+ for(int d = 0; d < dim1; d++) {
final double m1, m2;
if(mbr1.getMax(d) < mbr2.getMin(d)) {
m1 = mbr2.getMin(d);
@@ -167,7 +167,7 @@ public class LPNormDistanceFunction extends AbstractVectorDoubleDistanceNorm imp
}
@Override
- public <T extends NumberVector<?, ?>> SpatialPrimitiveDistanceQuery<T, DoubleDistance> instantiate(Relation<T> relation) {
+ public <T extends NumberVector<?>> SpatialPrimitiveDistanceQuery<T, DoubleDistance> instantiate(Relation<T> relation) {
return new SpatialPrimitiveDistanceQuery<T, DoubleDistance>(relation, this);
}
@@ -187,7 +187,8 @@ public class LPNormDistanceFunction extends AbstractVectorDoubleDistanceNorm imp
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- final DoubleParameter paramP = new DoubleParameter(P_ID, new GreaterConstraint(0));
+ final DoubleParameter paramP = new DoubleParameter(P_ID);
+ paramP.addConstraint(new GreaterConstraint(0));
if(config.grab(paramP)) {
p = paramP.getValue();
}
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/LocallyWeightedDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/LocallyWeightedDistanceFunction.java
index 90c3a94a..2ff0252c 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/LocallyWeightedDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/LocallyWeightedDistanceFunction.java
@@ -47,7 +47,7 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameteriz
* @param <V> the type of NumberVector to compute the distances in between
*/
// FIXME: implements SpatialPrimitiveDistanceFunction<V, DoubleDistance>
-public class LocallyWeightedDistanceFunction<V extends NumberVector<?, ?>> extends AbstractIndexBasedDistanceFunction<V, FilteredLocalPCAIndex<V>, DoubleDistance> implements FilteredLocalPCABasedDistanceFunction<V, FilteredLocalPCAIndex<V>, DoubleDistance> {
+public class LocallyWeightedDistanceFunction<V extends NumberVector<?>> extends AbstractIndexBasedDistanceFunction<V, FilteredLocalPCAIndex<V>, DoubleDistance> implements FilteredLocalPCABasedDistanceFunction<V, FilteredLocalPCAIndex<V>, DoubleDistance> {
/**
* Constructor
*
@@ -100,7 +100,7 @@ public class LocallyWeightedDistanceFunction<V extends NumberVector<?, ?>> exten
*
* @author Erich Schubert
*/
- public static class Instance<V extends NumberVector<?, ?>> extends AbstractIndexBasedDistanceFunction.Instance<V, LocalProjectionIndex<V, ?>, DoubleDistance, LocallyWeightedDistanceFunction<? super V>> implements FilteredLocalPCABasedDistanceFunction.Instance<V, LocalProjectionIndex<V, ?>, DoubleDistance> {
+ public static class Instance<V extends NumberVector<?>> extends AbstractIndexBasedDistanceFunction.Instance<V, LocalProjectionIndex<V, ?>, DoubleDistance, LocallyWeightedDistanceFunction<? super V>> implements FilteredLocalPCABasedDistanceFunction.Instance<V, LocalProjectionIndex<V, ?>, DoubleDistance> {
/**
* Constructor.
*
@@ -166,16 +166,16 @@ public class LocallyWeightedDistanceFunction<V extends NumberVector<?, ?>> exten
}
double[] r = new double[v.getDimensionality()];
- for(int d = 1; d <= v.getDimensionality(); d++) {
+ for(int d = 0; d < v.getDimensionality(); d++) {
double value = v.doubleValue(d);
if(value < mbr.getMin(d)) {
- r[d - 1] = mbr.getMin(d);
+ r[d] = mbr.getMin(d);
}
else if(value > mbr.getMax(d)) {
- r[d - 1] = mbr.getMax(d);
+ r[d] = mbr.getMax(d);
}
else {
- r[d - 1] = value;
+ r[d] = value;
}
}
@@ -200,7 +200,7 @@ public class LocallyWeightedDistanceFunction<V extends NumberVector<?, ?>> exten
}
double sqrDist = 0;
- for(int d = 1; d <= mbr1.getDimensionality(); d++) {
+ for(int d = 0; d < mbr1.getDimensionality(); d++) {
double m1, m2;
if(mbr1.getMax(d) < mbr2.getMin(d)) {
m1 = mbr2.getMin(d);
@@ -228,11 +228,10 @@ public class LocallyWeightedDistanceFunction<V extends NumberVector<?, ?>> exten
}
double sqrDist = 0;
- for(int d = 1; d <= mbr1.getDimensionality(); d++) {
- double c1 = (mbr1.getMin(d) + mbr1.getMax(d)) / 2;
- double c2 = (mbr2.getMin(d) + mbr2.getMax(d)) / 2;
-
- double manhattanI = c1 - c2;
+ for(int d = 0; d < mbr1.getDimensionality(); d++) {
+ final double c1 = .5 * (mbr1.getMin(d) + mbr1.getMax(d));
+ final double c2 = .5 * (mbr2.getMin(d) + mbr2.getMax(d));
+ final double manhattanI = c1 - c2;
sqrDist += manhattanI * manhattanI;
}
return new DoubleDistance(Math.sqrt(sqrDist));
@@ -246,7 +245,7 @@ public class LocallyWeightedDistanceFunction<V extends NumberVector<?, ?>> exten
*
* @apiviz.exclude
*/
- public static class Parameterizer<V extends NumberVector<?, ?>> extends AbstractIndexBasedDistanceFunction.Parameterizer<LocalProjectionIndex.Factory<V, FilteredLocalPCAIndex<V>>> {
+ public static class Parameterizer<V extends NumberVector<?>> extends AbstractIndexBasedDistanceFunction.Parameterizer<LocalProjectionIndex.Factory<V, FilteredLocalPCAIndex<V>>> {
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/ManhattanDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/ManhattanDistanceFunction.java
index c638ce13..a97ef086 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/ManhattanDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/ManhattanDistanceFunction.java
@@ -34,7 +34,7 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
* @author Arthur Zimek
*/
// TODO: add spatial!
-public class ManhattanDistanceFunction extends LPNormDistanceFunction implements SpatialPrimitiveDoubleDistanceFunction<NumberVector<?, ?>> {
+public class ManhattanDistanceFunction extends LPNormDistanceFunction implements SpatialPrimitiveDoubleDistanceFunction<NumberVector<?>> {
/**
* The static instance to use.
*/
@@ -52,25 +52,25 @@ public class ManhattanDistanceFunction extends LPNormDistanceFunction implements
}
/**
- * Compute the Manhattan distance
+ * Compute the Manhattan distance.
*
* @param v1 first vector
* @param v2 second vector
* @return Manhattan distance value
*/
@Override
- public double doubleDistance(NumberVector<?,?> v1, NumberVector<?,?> v2) {
+ public double doubleDistance(NumberVector<?> v1, NumberVector<?> v2) {
final int dim = v1.getDimensionality();
- if(dim != v2.getDimensionality()) {
+ if (dim != v2.getDimensionality()) {
throw new IllegalArgumentException("Different dimensionality of FeatureVectors" + "\n first argument: " + v1.toString() + "\n second argument: " + v2.toString());
}
double sum = 0;
- for(int i = 1; i <= dim; i++) {
+ for (int i = 0; i < dim; i++) {
sum += Math.abs(v1.doubleValue(i) - v2.doubleValue(i));
}
return sum;
}
-
+
/**
* Returns the Manhattan norm of the given vector.
*
@@ -78,32 +78,30 @@ public class ManhattanDistanceFunction extends LPNormDistanceFunction implements
* @return the Manhattan norm of the given vector
*/
@Override
- public double doubleNorm(NumberVector<?,?> v){
+ public double doubleNorm(NumberVector<?> v) {
final int dim = v.getDimensionality();
double sum = 0;
- for(int i = 1; i <= dim; i++) {
+ for (int i = 0; i < dim; i++) {
sum += Math.abs(v.doubleValue(i));
}
return sum;
}
- private double doubleMinDistObject(SpatialComparable mbr, NumberVector<?, ?> v) {
+ private double doubleMinDistObject(SpatialComparable mbr, NumberVector<?> v) {
final int dim = mbr.getDimensionality();
- if(dim != v.getDimensionality()) {
+ if (dim != v.getDimensionality()) {
throw new IllegalArgumentException("Different dimensionality of objects\n " + "first argument: " + mbr.toString() + "\n " + "second argument: " + v.toString() + "\n" + dim + "!=" + v.getDimensionality());
}
double sumDist = 0;
- for(int d = 1; d <= dim; d++) {
- double value = v.doubleValue(d);
- double r;
- if(value < mbr.getMin(d)) {
+ for (int d = 0; d < dim; d++) {
+ final double value = v.doubleValue(d);
+ final double r;
+ if (value < mbr.getMin(d)) {
r = mbr.getMin(d);
- }
- else if(value > mbr.getMax(d)) {
+ } else if (value > mbr.getMax(d)) {
r = mbr.getMax(d);
- }
- else {
+ } else {
r = value;
}
@@ -116,34 +114,30 @@ public class ManhattanDistanceFunction extends LPNormDistanceFunction implements
@Override
public double doubleMinDist(SpatialComparable mbr1, SpatialComparable mbr2) {
// Some optimizations for simpler cases.
- if(mbr1 instanceof NumberVector) {
- if(mbr2 instanceof NumberVector) {
- return doubleDistance((NumberVector<?, ?>) mbr1, (NumberVector<?, ?>) mbr2);
- }
- else {
- return doubleMinDistObject(mbr2, (NumberVector<?, ?>) mbr1);
+ if (mbr1 instanceof NumberVector) {
+ if (mbr2 instanceof NumberVector) {
+ return doubleDistance((NumberVector<?>) mbr1, (NumberVector<?>) mbr2);
+ } else {
+ return doubleMinDistObject(mbr2, (NumberVector<?>) mbr1);
}
- }
- else if(mbr2 instanceof NumberVector) {
- return doubleMinDistObject(mbr1, (NumberVector<?, ?>) mbr2);
+ } else if (mbr2 instanceof NumberVector) {
+ return doubleMinDistObject(mbr1, (NumberVector<?>) mbr2);
}
final int dim1 = mbr1.getDimensionality();
- if(dim1 != mbr2.getDimensionality()) {
+ if (dim1 != mbr2.getDimensionality()) {
throw new IllegalArgumentException("Different dimensionality of objects\n " + "first argument: " + mbr1.toString() + "\n " + "second argument: " + mbr2.toString());
}
double sumDist = 0;
- for(int d = 1; d <= dim1; d++) {
+ for (int d = 0; d < dim1; d++) {
final double m1, m2;
- if(mbr1.getMax(d) < mbr2.getMin(d)) {
+ if (mbr1.getMax(d) < mbr2.getMin(d)) {
m1 = mbr2.getMin(d);
m2 = mbr1.getMax(d);
- }
- else if(mbr1.getMin(d) > mbr2.getMax(d)) {
+ } else if (mbr1.getMin(d) > mbr2.getMax(d)) {
m1 = mbr1.getMin(d);
m2 = mbr2.getMax(d);
- }
- else { // The mbrs intersect!
+ } else { // The mbrs intersect!
continue;
}
final double manhattanI = m1 - m2;
@@ -164,10 +158,10 @@ public class ManhattanDistanceFunction extends LPNormDistanceFunction implements
@Override
public boolean equals(Object obj) {
- if(obj == null) {
+ if (obj == null) {
return false;
}
- if(obj == this) {
+ if (obj == this) {
return true;
}
if (this.getClass().equals(obj.getClass())) {
@@ -189,4 +183,4 @@ public class ManhattanDistanceFunction extends LPNormDistanceFunction implements
return ManhattanDistanceFunction.STATIC;
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/MaximumDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/MaximumDistanceFunction.java
index e718d087..30b47c9d 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/MaximumDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/MaximumDistanceFunction.java
@@ -33,7 +33,7 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
*
* @author Erich Schubert
*/
-public class MaximumDistanceFunction extends LPNormDistanceFunction implements SpatialPrimitiveDoubleDistanceFunction<NumberVector<?, ?>> {
+public class MaximumDistanceFunction extends LPNormDistanceFunction implements SpatialPrimitiveDoubleDistanceFunction<NumberVector<?>> {
/**
* Static instance.
*/
@@ -51,13 +51,13 @@ public class MaximumDistanceFunction extends LPNormDistanceFunction implements S
}
@Override
- public double doubleDistance(NumberVector<?, ?> v1, NumberVector<?, ?> v2) {
+ public double doubleDistance(NumberVector<?> v1, NumberVector<?> v2) {
final int dim1 = v1.getDimensionality();
if(dim1 != v2.getDimensionality()) {
throw new IllegalArgumentException("Different dimensionality of FeatureVectors" + "\n first argument: " + v1.toString() + "\n second argument: " + v2.toString());
}
double max = 0;
- for(int i = 1; i <= dim1; i++) {
+ for(int i = 0; i < dim1; i++) {
final double d = Math.abs(v1.doubleValue(i) - v2.doubleValue(i));
max = Math.max(d, max);
}
@@ -65,10 +65,10 @@ public class MaximumDistanceFunction extends LPNormDistanceFunction implements S
}
@Override
- public double doubleNorm(NumberVector<?, ?> v) {
+ public double doubleNorm(NumberVector<?> v) {
final int dim = v.getDimensionality();
double max = 0;
- for(int i = 1; i <= dim; i++) {
+ for(int i = 0; i < dim; i++) {
max = Math.max(v.doubleValue(i), max);
}
return max;
@@ -81,7 +81,7 @@ public class MaximumDistanceFunction extends LPNormDistanceFunction implements S
throw new IllegalArgumentException("Different dimensionality of objects.");
}
double max = 0;
- for(int i = 1; i <= dim1; i++) {
+ for(int i = 0; i < dim1; i++) {
final double d;
if(mbr1.getMax(i) < mbr2.getMin(i)) {
d = mbr2.getMin(i) - mbr1.getMin(i);
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/MinKDistance.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/MinKDistance.java
index 8f19af60..294b7e40 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/MinKDistance.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/MinKDistance.java
@@ -30,9 +30,9 @@ import de.lmu.ifi.dbs.elki.database.query.DatabaseQuery;
import de.lmu.ifi.dbs.elki.database.query.distance.AbstractDatabaseDistanceQuery;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.database.query.knn.KNNQuery;
-import de.lmu.ifi.dbs.elki.database.query.knn.KNNResult;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.DistanceUtil;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
@@ -71,17 +71,17 @@ public class MinKDistance<O, D extends Distance<D>> extends AbstractDatabaseDist
/**
* OptionID for the base distance used to compute reachability
*/
- public static final OptionID DISTANCE_FUNCTION_ID = OptionID.getOrCreateOptionID("reachdist.basedistance", "Base distance function to use.");
+ public static final OptionID DISTANCE_FUNCTION_ID = new OptionID("reachdist.basedistance", "Base distance function to use.");
/**
* OptionID for the KNN query class to use (preprocessor, approximation, ...)
*/
- public static final OptionID KNNQUERY_ID = OptionID.getOrCreateOptionID("reachdist.knnquery", "kNN query to use");
+ public static final OptionID KNNQUERY_ID = new OptionID("reachdist.knnquery", "kNN query to use");
/**
* OptionID for the "k" parameter.
*/
- public static final OptionID K_ID = OptionID.getOrCreateOptionID("reachdist.k", "The number of nearest neighbors of an object to be considered for computing its reachability distance.");
+ public static final OptionID K_ID = new OptionID("reachdist.k", "The number of nearest neighbors of an object to be considered for computing its reachability distance.");
/**
* The distance function to determine the exact distance.
@@ -124,6 +124,7 @@ public class MinKDistance<O, D extends Distance<D>> extends AbstractDatabaseDist
* @author Erich Schubert
*
* @apiviz.uses KNNQuery
+ * @apiviz.exclude
*/
public class Instance<T extends O> extends AbstractDatabaseDistanceQuery<T, D> {
/**
@@ -236,7 +237,8 @@ public class MinKDistance<O, D extends Distance<D>> extends AbstractDatabaseDist
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- final IntParameter kP = new IntParameter(K_ID, new GreaterConstraint(1));
+ final IntParameter kP = new IntParameter(K_ID);
+ kP.addConstraint(new GreaterConstraint(1));
if(config.grab(kP)) {
k = kP.getValue();
}
@@ -252,4 +254,4 @@ public class MinKDistance<O, D extends Distance<D>> extends AbstractDatabaseDist
return new MinKDistance<O, D>(parentDistance, k + (objectIsInKNN ? 0 : 1));
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/MinimumDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/MinimumDistanceFunction.java
index 2a31a829..a336c126 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/MinimumDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/MinimumDistanceFunction.java
@@ -37,7 +37,7 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
* @author Erich Schubert
*/
// TODO: add spatial?
-public class MinimumDistanceFunction extends AbstractVectorDoubleDistanceNorm implements SpatialPrimitiveDoubleDistanceFunction<NumberVector<?, ?>> {
+public class MinimumDistanceFunction extends AbstractVectorDoubleDistanceNorm implements SpatialPrimitiveDoubleDistanceFunction<NumberVector<?>> {
/**
* Static instance. Use this.
*/
@@ -55,13 +55,13 @@ public class MinimumDistanceFunction extends AbstractVectorDoubleDistanceNorm im
}
@Override
- public double doubleDistance(NumberVector<?, ?> v1, NumberVector<?, ?> v2) {
+ public double doubleDistance(NumberVector<?> v1, NumberVector<?> v2) {
final int dim = v1.getDimensionality();
if(dim != v2.getDimensionality()) {
throw new IllegalArgumentException("Different dimensionality of FeatureVectors" + "\n first argument: " + v1.toString() + "\n second argument: " + v2.toString());
}
double min = Double.MAX_VALUE;
- for(int i = 1; i <= dim; i++) {
+ for(int i = 0; i < dim; i++) {
final double d = Math.abs(v1.doubleValue(i) - v2.doubleValue(i));
min = Math.min(d, min);
}
@@ -69,10 +69,10 @@ public class MinimumDistanceFunction extends AbstractVectorDoubleDistanceNorm im
}
@Override
- public double doubleNorm(NumberVector<?, ?> v) {
+ public double doubleNorm(NumberVector<?> v) {
final int dim = v.getDimensionality();
double min = Double.POSITIVE_INFINITY;
- for(int i = 1; i <= dim; i++) {
+ for(int i = 0; i < dim; i++) {
min = Math.min(v.doubleValue(i), min);
}
return min;
@@ -85,7 +85,7 @@ public class MinimumDistanceFunction extends AbstractVectorDoubleDistanceNorm im
throw new IllegalArgumentException("Different dimensionality of FeatureVectors" + "\n first argument: " + mbr1.toString() + "\n second argument: " + mbr2.toString());
}
double min = Double.MAX_VALUE;
- for(int i = 1; i <= dim; i++) {
+ for(int i = 0; i < dim; i++) {
final double min1 = mbr1.getMin(i);
final double max1 = mbr1.getMax(i);
final double min2 = mbr2.getMin(i);
@@ -118,7 +118,7 @@ public class MinimumDistanceFunction extends AbstractVectorDoubleDistanceNorm im
}
@Override
- public <T extends NumberVector<?, ?>> SpatialPrimitiveDistanceQuery<T, DoubleDistance> instantiate(Relation<T> relation) {
+ public <T extends NumberVector<?>> SpatialPrimitiveDistanceQuery<T, DoubleDistance> instantiate(Relation<T> relation) {
return new SpatialPrimitiveDistanceQuery<T, DoubleDistance>(relation, this);
}
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/NumberVectorDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/NumberVectorDistanceFunction.java
index 68034672..f548c271 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/NumberVectorDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/NumberVectorDistanceFunction.java
@@ -35,6 +35,6 @@ import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
*
* @param <D> Distance type
*/
-public interface NumberVectorDistanceFunction<D extends Distance<D>> extends PrimitiveDistanceFunction<NumberVector<?, ?>, D> {
+public interface NumberVectorDistanceFunction<D extends Distance<D>> extends PrimitiveDistanceFunction<NumberVector<?>, D> {
// Empty - marker interface
}
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/RandomStableDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/RandomStableDistanceFunction.java
index b54f4e74..e57ae501 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/RandomStableDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/RandomStableDistanceFunction.java
@@ -26,6 +26,7 @@ package de.lmu.ifi.dbs.elki.distance.distancefunction;
import java.util.Random;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
import de.lmu.ifi.dbs.elki.utilities.Util;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
@@ -65,7 +66,7 @@ public class RandomStableDistanceFunction extends AbstractDBIDDistanceFunction<D
@Override
public DoubleDistance distance(DBIDRef o1, DBIDRef o2) {
- int c = o1.compareDBID(o2);
+ final int c = DBIDUtil.compare(o1, o2);
if(c == 0) {
return DoubleDistance.FACTORY.nullDistance();
}
@@ -73,7 +74,7 @@ public class RandomStableDistanceFunction extends AbstractDBIDDistanceFunction<D
if(c > 0) {
return distance(o2, o1);
}
- return new DoubleDistance(pseudoRandom(seed, Util.mixHashCodes(o1.getDBID().hashCode(), o2.getDBID().hashCode(), (int) seed)));
+ return new DoubleDistance(pseudoRandom(seed, Util.mixHashCodes(DBIDUtil.deref(o1).hashCode(), DBIDUtil.deref(o2).hashCode(), (int) seed)));
}
/**
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/SharedNearestNeighborJaccardDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/SharedNearestNeighborJaccardDistanceFunction.java
index 4ccf4163..5ff9a433 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/SharedNearestNeighborJaccardDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/SharedNearestNeighborJaccardDistanceFunction.java
@@ -25,6 +25,7 @@ package de.lmu.ifi.dbs.elki.distance.distancefunction;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
@@ -97,7 +98,7 @@ public class SharedNearestNeighborJaccardDistanceFunction<O> extends AbstractInd
DBIDIter iter1 = neighbors1.iter();
DBIDIter iter2 = neighbors2.iter();
while(iter1.valid() && iter2.valid()) {
- final int comp = iter1.compareDBID(iter2);
+ final int comp = DBIDUtil.compare(iter1, iter2);
union++;
if(comp == 0) {
intersection++;
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/SparseEuclideanDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/SparseEuclideanDistanceFunction.java
index f98ed234..8d68eb1d 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/SparseEuclideanDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/SparseEuclideanDistanceFunction.java
@@ -49,7 +49,7 @@ public class SparseEuclideanDistanceFunction extends SparseLPNormDistanceFunctio
}
@Override
- public double doubleDistance(SparseNumberVector<?, ?> v1, SparseNumberVector<?, ?> v2) {
+ public double doubleDistance(SparseNumberVector<?> v1, SparseNumberVector<?> v2) {
// Get the bit masks
BitSet b1 = v1.getNotNullMask();
BitSet b2 = v2.getNotNullMask();
@@ -81,7 +81,7 @@ public class SparseEuclideanDistanceFunction extends SparseLPNormDistanceFunctio
}
@Override
- public double doubleNorm(SparseNumberVector<?, ?> v1) {
+ public double doubleNorm(SparseNumberVector<?> v1) {
double sqrDist = 0;
// Get the bit masks
BitSet b1 = v1.getNotNullMask();
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/SparseLPNormDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/SparseLPNormDistanceFunction.java
index e552d1e5..b0cbc0dc 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/SparseLPNormDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/SparseLPNormDistanceFunction.java
@@ -40,7 +40,7 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.DoubleParameter;
* @author Erich Schubert
*/
// TODO: implement SpatialDistanceFunction
-public class SparseLPNormDistanceFunction extends AbstractPrimitiveDistanceFunction<SparseNumberVector<?, ?>, DoubleDistance> implements DoubleNorm<SparseNumberVector<?, ?>> {
+public class SparseLPNormDistanceFunction extends AbstractPrimitiveDistanceFunction<SparseNumberVector<?>, DoubleDistance> implements DoubleNorm<SparseNumberVector<?>> {
/**
* Keeps the currently set p.
*/
@@ -54,7 +54,7 @@ public class SparseLPNormDistanceFunction extends AbstractPrimitiveDistanceFunct
}
@Override
- public double doubleDistance(SparseNumberVector<?, ?> v1, SparseNumberVector<?, ?> v2) {
+ public double doubleDistance(SparseNumberVector<?> v1, SparseNumberVector<?> v2) {
// Get the bit masks
BitSet b1 = v1.getNotNullMask();
BitSet b2 = v2.getNotNullMask();
@@ -86,7 +86,7 @@ public class SparseLPNormDistanceFunction extends AbstractPrimitiveDistanceFunct
}
@Override
- public double doubleNorm(SparseNumberVector<?, ?> v1) {
+ public double doubleNorm(SparseNumberVector<?> v1) {
double sqrDist = 0;
// Get the bit masks
BitSet b1 = v1.getNotNullMask();
@@ -99,12 +99,12 @@ public class SparseLPNormDistanceFunction extends AbstractPrimitiveDistanceFunct
}
@Override
- public DoubleDistance norm(SparseNumberVector<?, ?> obj) {
+ public DoubleDistance norm(SparseNumberVector<?> obj) {
return new DoubleDistance(doubleNorm(obj));
}
@Override
- public DoubleDistance distance(SparseNumberVector<?, ?> v1, SparseNumberVector<?, ?> v2) {
+ public DoubleDistance distance(SparseNumberVector<?> v1, SparseNumberVector<?> v2) {
return new DoubleDistance(doubleDistance(v1, v2));
}
@@ -114,7 +114,7 @@ public class SparseLPNormDistanceFunction extends AbstractPrimitiveDistanceFunct
}
@Override
- public SimpleTypeInformation<? super SparseNumberVector<?, ?>> getInputTypeRestriction() {
+ public SimpleTypeInformation<? super SparseNumberVector<?>> getInputTypeRestriction() {
return TypeUtil.SPARSE_VECTOR_VARIABLE_LENGTH;
}
@@ -139,7 +139,8 @@ public class SparseLPNormDistanceFunction extends AbstractPrimitiveDistanceFunct
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- DoubleParameter pP = new DoubleParameter(LPNormDistanceFunction.P_ID, new GreaterConstraint(0));
+ DoubleParameter pP = new DoubleParameter(LPNormDistanceFunction.P_ID);
+ pP.addConstraint(new GreaterConstraint(0));
if(config.grab(pP)) {
p = pP.getValue();
}
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/SparseManhattanDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/SparseManhattanDistanceFunction.java
index 9b8399bf..597dd6ca 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/SparseManhattanDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/SparseManhattanDistanceFunction.java
@@ -49,7 +49,7 @@ public class SparseManhattanDistanceFunction extends SparseLPNormDistanceFunctio
}
@Override
- public double doubleDistance(SparseNumberVector<?, ?> v1, SparseNumberVector<?, ?> v2) {
+ public double doubleDistance(SparseNumberVector<?> v1, SparseNumberVector<?> v2) {
// Get the bit masks
BitSet b1 = v1.getNotNullMask();
BitSet b2 = v2.getNotNullMask();
@@ -79,7 +79,7 @@ public class SparseManhattanDistanceFunction extends SparseLPNormDistanceFunctio
}
@Override
- public double doubleNorm(SparseNumberVector<?, ?> v1) {
+ public double doubleNorm(SparseNumberVector<?> v1) {
double sqrDist = 0;
// Get the bit masks
BitSet b1 = v1.getNotNullMask();
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/SparseMaximumDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/SparseMaximumDistanceFunction.java
index 1b399e83..720b8017 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/SparseMaximumDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/SparseMaximumDistanceFunction.java
@@ -49,7 +49,7 @@ public class SparseMaximumDistanceFunction extends SparseLPNormDistanceFunction
}
@Override
- public double doubleDistance(SparseNumberVector<?, ?> v1, SparseNumberVector<?, ?> v2) {
+ public double doubleDistance(SparseNumberVector<?> v1, SparseNumberVector<?> v2) {
// Get the bit masks
BitSet b1 = v1.getNotNullMask();
BitSet b2 = v2.getNotNullMask();
@@ -79,7 +79,7 @@ public class SparseMaximumDistanceFunction extends SparseLPNormDistanceFunction
}
@Override
- public double doubleNorm(SparseNumberVector<?, ?> v1) {
+ public double doubleNorm(SparseNumberVector<?> v1) {
double sqrDist = 0;
// Get the bit masks
BitSet b1 = v1.getNotNullMask();
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/SquaredEuclideanDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/SquaredEuclideanDistanceFunction.java
index 80edcc09..ff17f388 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/SquaredEuclideanDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/SquaredEuclideanDistanceFunction.java
@@ -36,7 +36,7 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
*
* @author Arthur Zimek
*/
-public class SquaredEuclideanDistanceFunction extends AbstractVectorDoubleDistanceNorm implements SpatialPrimitiveDoubleDistanceFunction<NumberVector<?, ?>> {
+public class SquaredEuclideanDistanceFunction extends AbstractVectorDoubleDistanceNorm implements SpatialPrimitiveDoubleDistanceFunction<NumberVector<?>> {
/**
* Static instance. Use this!
*/
@@ -54,10 +54,10 @@ public class SquaredEuclideanDistanceFunction extends AbstractVectorDoubleDistan
}
@Override
- public double doubleNorm(NumberVector<?, ?> v) {
+ public double doubleNorm(NumberVector<?> v) {
final int dim = v.getDimensionality();
double sum = 0;
- for(int i = 1; i <= dim; i++) {
+ for(int i = 0; i < dim; i++) {
final double val = v.doubleValue(i);
sum += val * val;
}
@@ -71,27 +71,27 @@ public class SquaredEuclideanDistanceFunction extends AbstractVectorDoubleDistan
* double value
*/
@Override
- public double doubleDistance(NumberVector<?, ?> v1, NumberVector<?, ?> v2) {
+ public double doubleDistance(NumberVector<?> v1, NumberVector<?> v2) {
final int dim1 = v1.getDimensionality();
if(dim1 != v2.getDimensionality()) {
throw new IllegalArgumentException("Different dimensionality of FeatureVectors" + "\n first argument: " + v1.toString() + "\n second argument: " + v2.toString() + "\n" + v1.getDimensionality() + "!=" + v2.getDimensionality());
}
double sqrDist = 0;
- for(int i = 1; i <= dim1; i++) {
+ for(int i = 0; i < dim1; i++) {
final double delta = v1.doubleValue(i) - v2.doubleValue(i);
sqrDist += delta * delta;
}
return sqrDist;
}
- protected double doubleMinDistObject(SpatialComparable mbr, NumberVector<?, ?> v) {
+ protected double doubleMinDistObject(SpatialComparable mbr, NumberVector<?> v) {
final int dim = mbr.getDimensionality();
if(dim != v.getDimensionality()) {
throw new IllegalArgumentException("Different dimensionality of objects\n " + "first argument: " + mbr.toString() + "\n " + "second argument: " + v.toString() + "\n" + dim + "!=" + v.getDimensionality());
}
double sqrDist = 0;
- for(int d = 1; d <= dim; d++) {
+ for(int d = 0; d < dim; d++) {
double value = v.doubleValue(d);
double r;
if(value < mbr.getMin(d)) {
@@ -115,14 +115,14 @@ public class SquaredEuclideanDistanceFunction extends AbstractVectorDoubleDistan
// Some optimizations for simpler cases.
if(mbr1 instanceof NumberVector) {
if(mbr2 instanceof NumberVector) {
- return doubleDistance((NumberVector<?, ?>) mbr1, (NumberVector<?, ?>) mbr2);
+ return doubleDistance((NumberVector<?>) mbr1, (NumberVector<?>) mbr2);
}
else {
- return doubleMinDistObject(mbr2, (NumberVector<?, ?>) mbr1);
+ return doubleMinDistObject(mbr2, (NumberVector<?>) mbr1);
}
}
else if(mbr2 instanceof NumberVector) {
- return doubleMinDistObject(mbr1, (NumberVector<?, ?>) mbr2);
+ return doubleMinDistObject(mbr1, (NumberVector<?>) mbr2);
}
final int dim1 = mbr1.getDimensionality();
if(dim1 != mbr2.getDimensionality()) {
@@ -130,7 +130,7 @@ public class SquaredEuclideanDistanceFunction extends AbstractVectorDoubleDistan
}
double sqrDist = 0;
- for(int d = 1; d <= dim1; d++) {
+ for(int d = 0; d < dim1; d++) {
final double m1, m2;
if(mbr1.getMax(d) < mbr2.getMin(d)) {
m1 = mbr2.getMin(d);
@@ -160,7 +160,7 @@ public class SquaredEuclideanDistanceFunction extends AbstractVectorDoubleDistan
}
@Override
- public <T extends NumberVector<?, ?>> SpatialPrimitiveDistanceQuery<T, DoubleDistance> instantiate(Relation<T> relation) {
+ public <T extends NumberVector<?>> SpatialPrimitiveDistanceQuery<T, DoubleDistance> instantiate(Relation<T> relation) {
return new SpatialPrimitiveDistanceQuery<T, DoubleDistance>(relation, this);
}
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/WeightedDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/WeightedDistanceFunction.java
index 046cc3e9..7bd09e97 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/WeightedDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/WeightedDistanceFunction.java
@@ -58,7 +58,7 @@ public class WeightedDistanceFunction extends AbstractVectorDoubleDistanceFuncti
* @return the Weighted distance between the given two vectors
*/
@Override
- public double doubleDistance(NumberVector<?, ?> o1, NumberVector<?, ?> o2) {
+ public double doubleDistance(NumberVector<?> o1, NumberVector<?> o2) {
assert (o1.getDimensionality() == o2.getDimensionality()) : "Different dimensionality of FeatureVectors" + "\n first argument: " + o1.toString() + "\n second argument: " + o2.toString();
Vector o1_minus_o2 = o1.getColumnVector().minusEquals(o2.getColumnVector());
@@ -66,7 +66,7 @@ public class WeightedDistanceFunction extends AbstractVectorDoubleDistanceFuncti
}
@Override
- public VectorFieldTypeInformation<? super NumberVector<?, ?>> getInputTypeRestriction() {
- return VectorFieldTypeInformation.get(NumberVector.class, weightMatrix.getColumnDimensionality());
+ public VectorFieldTypeInformation<? super NumberVector<?>> getInputTypeRestriction() {
+ return new VectorFieldTypeInformation<NumberVector<?>>(NumberVector.class, weightMatrix.getColumnDimensionality());
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/WeightedLPNormDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/WeightedLPNormDistanceFunction.java
index 38302573..6a76366f 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/WeightedLPNormDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/WeightedLPNormDistanceFunction.java
@@ -52,7 +52,7 @@ public class WeightedLPNormDistanceFunction extends LPNormDistanceFunction {
}
@Override
- public double doubleDistance(NumberVector<?, ?> v1, NumberVector<?, ?> v2) {
+ public double doubleDistance(NumberVector<?> v1, NumberVector<?> v2) {
final int dim = weights.length;
if(dim != v1.getDimensionality()) {
throw new IllegalArgumentException("Dimensionality of FeatureVector doesn't match weights!");
@@ -63,7 +63,7 @@ public class WeightedLPNormDistanceFunction extends LPNormDistanceFunction {
final double p = getP();
double sqrDist = 0;
- for(int i = 1; i <= dim; i++) {
+ for(int i = 0; i < dim; i++) {
final double delta = Math.abs(v1.doubleValue(i) - v2.doubleValue(i));
sqrDist += Math.pow(delta, p) * weights[i - 1];
}
@@ -75,7 +75,7 @@ public class WeightedLPNormDistanceFunction extends LPNormDistanceFunction {
// Optimization for the simplest case
if(mbr1 instanceof NumberVector) {
if(mbr2 instanceof NumberVector) {
- return doubleDistance((NumberVector<?, ?>) mbr1, (NumberVector<?, ?>) mbr2);
+ return doubleDistance((NumberVector<?>) mbr1, (NumberVector<?>) mbr2);
}
}
// TODO: optimize for more simpler cases: obj vs. rect?
@@ -86,7 +86,7 @@ public class WeightedLPNormDistanceFunction extends LPNormDistanceFunction {
final double p = getP();
double sumDist = 0;
- for(int d = 1; d <= dim1; d++) {
+ for(int d = 0; d < dim1; d++) {
final double m1, m2;
if(mbr1.getMax(d) < mbr2.getMin(d)) {
m1 = mbr2.getMin(d);
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/WeightedSquaredEuclideanDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/WeightedSquaredEuclideanDistanceFunction.java
index a2944385..e00f8e07 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/WeightedSquaredEuclideanDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/WeightedSquaredEuclideanDistanceFunction.java
@@ -56,13 +56,13 @@ public class WeightedSquaredEuclideanDistanceFunction extends AbstractVectorDoub
* double value
*/
@Override
- public double doubleDistance(NumberVector<?, ?> v1, NumberVector<?, ?> v2) {
+ public double doubleDistance(NumberVector<?> v1, NumberVector<?> v2) {
final int dim1 = v1.getDimensionality();
if(dim1 != v2.getDimensionality()) {
throw new IllegalArgumentException("Different dimensionality of FeatureVectors" + "\n first argument: " + v1.toString() + "\n second argument: " + v2.toString() + "\n" + v1.getDimensionality() + "!=" + v2.getDimensionality());
}
double sqrDist = 0;
- for(int i = 1; i <= dim1; i++) {
+ for(int i = 0; i < dim1; i++) {
final double delta = v1.doubleValue(i) - v2.doubleValue(i);
sqrDist += delta * delta * weights[i - 1];
}
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/adapter/AbstractSimilarityAdapter.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/adapter/AbstractSimilarityAdapter.java
index 19e80724..74606083 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/adapter/AbstractSimilarityAdapter.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/adapter/AbstractSimilarityAdapter.java
@@ -65,7 +65,7 @@ public abstract class AbstractSimilarityAdapter<O> extends AbstractDatabaseDista
* {@link de.lmu.ifi.dbs.elki.distance.similarityfunction.FractionalSharedNearestNeighborSimilarityFunction}
* </p>
*/
- public static final OptionID SIMILARITY_FUNCTION_ID = OptionID.getOrCreateOptionID("adapter.similarityfunction", "Similarity function to derive the distance between database objects from.");
+ public static final OptionID SIMILARITY_FUNCTION_ID = new OptionID("adapter.similarityfunction", "Similarity function to derive the distance between database objects from.");
/**
* Holds the similarity function.
@@ -166,7 +166,7 @@ public abstract class AbstractSimilarityAdapter<O> extends AbstractDatabaseDista
*
* @apiviz.exclude
*/
- public static abstract class Parameterizer<O> extends AbstractParameterizer {
+ public abstract static class Parameterizer<O> extends AbstractParameterizer {
/**
* Holds the similarity function.
*/
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/adapter/SimilarityAdapterArccos.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/adapter/SimilarityAdapterArccos.java
index d1a5aa4a..b77e4a7d 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/adapter/SimilarityAdapterArccos.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/adapter/SimilarityAdapterArccos.java
@@ -30,7 +30,6 @@ import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance;
import de.lmu.ifi.dbs.elki.distance.similarityfunction.NormalizedSimilarityFunction;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
/**
* Adapter from a normalized similarity function to a distance function using
@@ -92,11 +91,6 @@ public class SimilarityAdapterArccos<O> extends AbstractSimilarityAdapter<O> {
*/
public static class Parameterizer<O> extends AbstractSimilarityAdapter.Parameterizer<O> {
@Override
- protected void makeOptions(Parameterization config) {
- super.makeOptions(config);
- }
-
- @Override
protected SimilarityAdapterArccos<O> makeInstance() {
return new SimilarityAdapterArccos<O>(similarityFunction);
}
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/adapter/SimilarityAdapterLinear.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/adapter/SimilarityAdapterLinear.java
index e2c0f981..ab7204ce 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/adapter/SimilarityAdapterLinear.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/adapter/SimilarityAdapterLinear.java
@@ -30,7 +30,6 @@ import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance;
import de.lmu.ifi.dbs.elki.distance.similarityfunction.NormalizedSimilarityFunction;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
/**
* Adapter from a normalized similarity function to a distance function using
@@ -92,11 +91,6 @@ public class SimilarityAdapterLinear<O> extends AbstractSimilarityAdapter<O> {
*/
public static class Parameterizer<O> extends AbstractSimilarityAdapter.Parameterizer<O> {
@Override
- protected void makeOptions(Parameterization config) {
- super.makeOptions(config);
- }
-
- @Override
protected SimilarityAdapterLinear<O> makeInstance() {
return new SimilarityAdapterLinear<O>(similarityFunction);
}
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/adapter/SimilarityAdapterLn.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/adapter/SimilarityAdapterLn.java
index a45b6108..b1b63486 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/adapter/SimilarityAdapterLn.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/adapter/SimilarityAdapterLn.java
@@ -30,7 +30,6 @@ import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance;
import de.lmu.ifi.dbs.elki.distance.similarityfunction.NormalizedSimilarityFunction;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
/**
* Adapter from a normalized similarity function to a distance function using
@@ -92,11 +91,6 @@ public class SimilarityAdapterLn<O> extends AbstractSimilarityAdapter<O> {
*/
public static class Parameterizer<O> extends AbstractSimilarityAdapter.Parameterizer<O> {
@Override
- protected void makeOptions(Parameterization config) {
- super.makeOptions(config);
- }
-
- @Override
protected SimilarityAdapterLn<O> makeInstance() {
return new SimilarityAdapterLn<O>(similarityFunction);
}
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/colorhistogram/HSBHistogramQuadraticDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/colorhistogram/HSBHistogramQuadraticDistanceFunction.java
index 4eff8822..771e32f2 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/colorhistogram/HSBHistogramQuadraticDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/colorhistogram/HSBHistogramQuadraticDistanceFunction.java
@@ -23,7 +23,6 @@ package de.lmu.ifi.dbs.elki.distance.distancefunction.colorhistogram;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.util.ArrayList;
import java.util.List;
import de.lmu.ifi.dbs.elki.distance.distancefunction.WeightedDistanceFunction;
@@ -32,9 +31,9 @@ import de.lmu.ifi.dbs.elki.math.linearalgebra.Matrix;
import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.ListGreaterEqualConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterEqualConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.ListEachConstraint;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.ListSizeConstraint;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.ParameterConstraint;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntListParameter;
@@ -56,7 +55,7 @@ public class HSBHistogramQuadraticDistanceFunction extends WeightedDistanceFunct
/**
* Parameter for the kernel dimensionality.
*/
- public static final OptionID BPP_ID = OptionID.getOrCreateOptionID("hsbhist.bpp", "The dimensionality of the histogram in hue, saturation and brightness.");
+ public static final OptionID BPP_ID = new OptionID("hsbhist.bpp", "The dimensionality of the histogram in hue, saturation and brightness.");
/**
* Constructor.
@@ -131,10 +130,9 @@ public class HSBHistogramQuadraticDistanceFunction extends WeightedDistanceFunct
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- final List<ParameterConstraint<List<Integer>>> bppConstraints = new ArrayList<ParameterConstraint<List<Integer>>>(2);
- bppConstraints.add(new ListSizeConstraint<Integer>(3));
- bppConstraints.add(new ListGreaterEqualConstraint<Integer>(1));
- IntListParameter param = new IntListParameter(BPP_ID, bppConstraints, false);
+ IntListParameter param = new IntListParameter(BPP_ID);
+ param.addConstraint(new ListSizeConstraint(3));
+ param.addConstraint(new ListEachConstraint<Integer>(new GreaterEqualConstraint(1)));
if(config.grab(param)) {
List<Integer> quant = param.getValue();
assert (quant.size() == 3);
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/colorhistogram/HistogramIntersectionDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/colorhistogram/HistogramIntersectionDistanceFunction.java
index 6ed8e744..2f38f68b 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/colorhistogram/HistogramIntersectionDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/colorhistogram/HistogramIntersectionDistanceFunction.java
@@ -48,7 +48,7 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
@Title("Color histogram intersection distance")
@Description("Distance function for color histograms that emphasizes 'strong' bins.")
@Reference(authors = "M. J. Swain, D. H. Ballard", title = "Color Indexing", booktitle = "International Journal of Computer Vision, 7(1), 32, 1991")
-public class HistogramIntersectionDistanceFunction extends AbstractVectorDoubleDistanceFunction implements SpatialPrimitiveDoubleDistanceFunction<NumberVector<?, ?>> {
+public class HistogramIntersectionDistanceFunction extends AbstractVectorDoubleDistanceFunction implements SpatialPrimitiveDoubleDistanceFunction<NumberVector<?>> {
/**
* Static instance
*/
@@ -70,7 +70,7 @@ public class HistogramIntersectionDistanceFunction extends AbstractVectorDoubleD
}
@Override
- public double doubleDistance(NumberVector<?, ?> v1, NumberVector<?, ?> v2) {
+ public double doubleDistance(NumberVector<?> v1, NumberVector<?> v2) {
final int dim1 = v1.getDimensionality();
if(dim1 != v2.getDimensionality()) {
throw new IllegalArgumentException("Different dimensionality of FeatureVectors" + "\n first argument: " + v1.toString() + "\n second argument: " + v2.toString() + "\n" + v1.getDimensionality() + "!=" + v2.getDimensionality());
@@ -78,7 +78,7 @@ public class HistogramIntersectionDistanceFunction extends AbstractVectorDoubleD
double dist = 0;
double norm1 = 0;
double norm2 = 0;
- for(int i = 1; i <= dim1; i++) {
+ for(int i = 0; i < dim1; i++) {
final double val1 = v1.doubleValue(i);
final double val2 = v2.doubleValue(i);
dist += Math.min(val1, val2);
@@ -98,7 +98,7 @@ public class HistogramIntersectionDistanceFunction extends AbstractVectorDoubleD
double dist = 0;
double norm1 = 0;
double norm2 = 0;
- for(int i = 1; i <= dim1; i++) {
+ for(int i = 0; i < dim1; i++) {
final double min1 = mbr1.getMin(i);
final double max1 = mbr1.getMax(i);
final double min2 = mbr2.getMin(i);
@@ -112,7 +112,7 @@ public class HistogramIntersectionDistanceFunction extends AbstractVectorDoubleD
}
@Override
- public <T extends NumberVector<?, ?>> SpatialDistanceQuery<T, DoubleDistance> instantiate(Relation<T> relation) {
+ public <T extends NumberVector<?>> SpatialDistanceQuery<T, DoubleDistance> instantiate(Relation<T> relation) {
return new SpatialPrimitiveDistanceQuery<T, DoubleDistance>(relation, this);
}
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/colorhistogram/RGBHistogramQuadraticDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/colorhistogram/RGBHistogramQuadraticDistanceFunction.java
index 5ba02e17..09dfa124 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/colorhistogram/RGBHistogramQuadraticDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/colorhistogram/RGBHistogramQuadraticDistanceFunction.java
@@ -51,7 +51,7 @@ public class RGBHistogramQuadraticDistanceFunction extends WeightedDistanceFunct
/**
* Parameter for the kernel dimensionality.
*/
- public static final OptionID BPP_ID = OptionID.getOrCreateOptionID("rgbhist.bpp", "The dimensionality of the histogram in each color");
+ public static final OptionID BPP_ID = new OptionID("rgbhist.bpp", "The dimensionality of the histogram in each color");
/**
* Constructor.
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/correlation/ERiCDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/correlation/ERiCDistanceFunction.java
index 25766a91..55fd0e72 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/correlation/ERiCDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/correlation/ERiCDistanceFunction.java
@@ -33,7 +33,6 @@ import de.lmu.ifi.dbs.elki.distance.distancevalue.BitDistance;
import de.lmu.ifi.dbs.elki.index.IndexFactory;
import de.lmu.ifi.dbs.elki.index.preprocessed.localpca.FilteredLocalPCAIndex;
import de.lmu.ifi.dbs.elki.index.preprocessed.localpca.KNNQueryFilteredPCAIndex;
-import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Matrix;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector;
import de.lmu.ifi.dbs.elki.math.linearalgebra.pca.PCAFilteredResult;
@@ -50,12 +49,7 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.DoubleParameter;
*
* @apiviz.has Instance
*/
-public class ERiCDistanceFunction extends AbstractIndexBasedDistanceFunction<NumberVector<?, ?>, FilteredLocalPCAIndex<NumberVector<?, ?>>, BitDistance> implements FilteredLocalPCABasedDistanceFunction<NumberVector<?, ?>, FilteredLocalPCAIndex<NumberVector<?, ?>>, BitDistance> {
- /**
- * Logger for debug.
- */
- static Logging logger = Logging.getLogger(PCABasedCorrelationDistanceFunction.class);
-
+public class ERiCDistanceFunction extends AbstractIndexBasedDistanceFunction<NumberVector<?>, FilteredLocalPCAIndex<NumberVector<?>>, BitDistance> implements FilteredLocalPCABasedDistanceFunction<NumberVector<?>, FilteredLocalPCAIndex<NumberVector<?>>, BitDistance> {
/**
* Parameter to specify the threshold for approximate linear dependency: the
* strong eigenvectors of q are approximately linear dependent from the strong
@@ -69,7 +63,7 @@ public class ERiCDistanceFunction extends AbstractIndexBasedDistanceFunction<Num
* Key: {@code -ericdf.delta}
* </p>
*/
- public static final OptionID DELTA_ID = OptionID.getOrCreateOptionID("ericdf.delta", "Threshold for approximate linear dependency: " + "the strong eigenvectors of q are approximately linear dependent " + "from the strong eigenvectors p if the following condition " + "holds for all stroneg eigenvectors q_i of q (lambda_q < lambda_p): " + "q_i' * M^check_p * q_i <= delta^2.");
+ public static final OptionID DELTA_ID = new OptionID("ericdf.delta", "Threshold for approximate linear dependency: " + "the strong eigenvectors of q are approximately linear dependent " + "from the strong eigenvectors p if the following condition " + "holds for all stroneg eigenvectors q_i of q (lambda_q < lambda_p): " + "q_i' * M^check_p * q_i <= delta^2.");
/**
* Parameter to specify the threshold for the maximum distance between two
@@ -83,7 +77,7 @@ public class ERiCDistanceFunction extends AbstractIndexBasedDistanceFunction<Num
* Key: {@code -ericdf.tau}
* </p>
*/
- public static final OptionID TAU_ID = OptionID.getOrCreateOptionID("ericdf.tau", "Threshold for the maximum distance between two approximately linear " + "dependent subspaces of two objects p and q " + "(lambda_q < lambda_p) before considering them as parallel.");
+ public static final OptionID TAU_ID = new OptionID("ericdf.tau", "Threshold for the maximum distance between two approximately linear " + "dependent subspaces of two objects p and q " + "(lambda_q < lambda_p) before considering them as parallel.");
/**
* Holds the value of {@link #DELTA_ID}.
@@ -102,7 +96,7 @@ public class ERiCDistanceFunction extends AbstractIndexBasedDistanceFunction<Num
* @param delta Delta parameter
* @param tau Tau parameter
*/
- public ERiCDistanceFunction(IndexFactory<NumberVector<?, ?>, FilteredLocalPCAIndex<NumberVector<?, ?>>> indexFactory, double delta, double tau) {
+ public ERiCDistanceFunction(IndexFactory<NumberVector<?>, FilteredLocalPCAIndex<NumberVector<?>>> indexFactory, double delta, double tau) {
super(indexFactory);
this.delta = delta;
this.tau = tau;
@@ -114,11 +108,11 @@ public class ERiCDistanceFunction extends AbstractIndexBasedDistanceFunction<Num
}
@Override
- public <T extends NumberVector<?, ?>> Instance<T> instantiate(Relation<T> database) {
+ public <T extends NumberVector<?>> Instance<T> instantiate(Relation<T> database) {
// We can't really avoid these warnings, due to a limitation in Java
// Generics (AFAICT)
@SuppressWarnings("unchecked")
- FilteredLocalPCAIndex<T> indexinst = (FilteredLocalPCAIndex<T>) indexFactory.instantiate((Relation<NumberVector<?, ?>>) database);
+ FilteredLocalPCAIndex<T> indexinst = (FilteredLocalPCAIndex<T>) indexFactory.instantiate((Relation<NumberVector<?>>) database);
return new Instance<T>(database, indexinst, this, delta, tau);
}
@@ -135,14 +129,14 @@ public class ERiCDistanceFunction extends AbstractIndexBasedDistanceFunction<Num
private boolean approximatelyLinearDependent(PCAFilteredResult pca1, PCAFilteredResult pca2) {
Matrix m1_czech = pca1.dissimilarityMatrix();
Matrix v2_strong = pca2.adapatedStrongEigenvectors();
- for(int i = 0; i < v2_strong.getColumnDimensionality(); i++) {
+ for (int i = 0; i < v2_strong.getColumnDimensionality(); i++) {
Vector v2_i = v2_strong.getCol(i);
// check, if distance of v2_i to the space of pca_1 > delta
// (i.e., if v2_i spans up a new dimension)
double dist = Math.sqrt(v2_i.transposeTimes(v2_i) - v2_i.transposeTimesTimes(m1_czech, v2_i));
// if so, return false
- if(dist > delta) {
+ if (dist > delta) {
return false;
}
}
@@ -162,37 +156,35 @@ public class ERiCDistanceFunction extends AbstractIndexBasedDistanceFunction<Num
* @return the distance between two given DatabaseObjects according to this
* distance function
*/
- public BitDistance distance(NumberVector<?, ?> v1, NumberVector<?, ?> v2, PCAFilteredResult pca1, PCAFilteredResult pca2) {
- if(pca1.getCorrelationDimension() < pca2.getCorrelationDimension()) {
+ public BitDistance distance(NumberVector<?> v1, NumberVector<?> v2, PCAFilteredResult pca1, PCAFilteredResult pca2) {
+ if (pca1.getCorrelationDimension() < pca2.getCorrelationDimension()) {
throw new IllegalStateException("pca1.getCorrelationDimension() < pca2.getCorrelationDimension(): " + pca1.getCorrelationDimension() + " < " + pca2.getCorrelationDimension());
}
boolean approximatelyLinearDependent;
- if(pca1.getCorrelationDimension() == pca2.getCorrelationDimension()) {
+ if (pca1.getCorrelationDimension() == pca2.getCorrelationDimension()) {
approximatelyLinearDependent = approximatelyLinearDependent(pca1, pca2) && approximatelyLinearDependent(pca2, pca1);
- }
- else {
+ } else {
approximatelyLinearDependent = approximatelyLinearDependent(pca1, pca2);
}
- if(!approximatelyLinearDependent) {
+ if (!approximatelyLinearDependent) {
return new BitDistance(true);
}
else {
double affineDistance;
- if(pca1.getCorrelationDimension() == pca2.getCorrelationDimension()) {
+ if (pca1.getCorrelationDimension() == pca2.getCorrelationDimension()) {
WeightedDistanceFunction df1 = new WeightedDistanceFunction(pca1.similarityMatrix());
WeightedDistanceFunction df2 = new WeightedDistanceFunction(pca2.similarityMatrix());
affineDistance = Math.max(df1.distance(v1, v2).doubleValue(), df2.distance(v1, v2).doubleValue());
- }
- else {
+ } else {
WeightedDistanceFunction df1 = new WeightedDistanceFunction(pca1.similarityMatrix());
affineDistance = df1.distance(v1, v2).doubleValue();
}
- if(affineDistance > tau) {
+ if (affineDistance > tau) {
return new BitDistance(true);
}
@@ -202,7 +194,7 @@ public class ERiCDistanceFunction extends AbstractIndexBasedDistanceFunction<Num
@Override
public boolean equals(Object obj) {
- if(obj == null) {
+ if (obj == null) {
return false;
}
if (!this.getClass().equals(obj.getClass())) {
@@ -217,7 +209,7 @@ public class ERiCDistanceFunction extends AbstractIndexBasedDistanceFunction<Num
*
* @author Erich Schubert
*/
- public static class Instance<V extends NumberVector<?, ?>> extends AbstractIndexBasedDistanceFunction.Instance<V, FilteredLocalPCAIndex<V>, BitDistance, ERiCDistanceFunction> implements FilteredLocalPCABasedDistanceFunction.Instance<V, FilteredLocalPCAIndex<V>, BitDistance> {
+ public static class Instance<V extends NumberVector<?>> extends AbstractIndexBasedDistanceFunction.Instance<V, FilteredLocalPCAIndex<V>, BitDistance, ERiCDistanceFunction> implements FilteredLocalPCABasedDistanceFunction.Instance<V, FilteredLocalPCAIndex<V>, BitDistance> {
/**
* Holds the value of {@link #DELTA_ID}.
*/
@@ -264,7 +256,7 @@ public class ERiCDistanceFunction extends AbstractIndexBasedDistanceFunction<Num
*
* @apiviz.exclude
*/
- public static class Parameterizer extends AbstractIndexBasedDistanceFunction.Parameterizer<IndexFactory<NumberVector<?, ?>, FilteredLocalPCAIndex<NumberVector<?, ?>>>> {
+ public static class Parameterizer extends AbstractIndexBasedDistanceFunction.Parameterizer<IndexFactory<NumberVector<?>, FilteredLocalPCAIndex<NumberVector<?>>>> {
double delta = 0.0;
double tau = 0.0;
@@ -274,14 +266,16 @@ public class ERiCDistanceFunction extends AbstractIndexBasedDistanceFunction<Num
super.makeOptions(config);
configIndexFactory(config, FilteredLocalPCAIndex.Factory.class, KNNQueryFilteredPCAIndex.Factory.class);
- final DoubleParameter deltaP = new DoubleParameter(DELTA_ID, new GreaterEqualConstraint(0), 0.1);
- if(config.grab(deltaP)) {
- delta = deltaP.getValue();
+ final DoubleParameter deltaP = new DoubleParameter(DELTA_ID, 0.1);
+ deltaP.addConstraint(new GreaterEqualConstraint(0));
+ if (config.grab(deltaP)) {
+ delta = deltaP.doubleValue();
}
- final DoubleParameter tauP = new DoubleParameter(TAU_ID, new GreaterEqualConstraint(0), 0.1);
- if(config.grab(tauP)) {
- tau = tauP.getValue();
+ final DoubleParameter tauP = new DoubleParameter(TAU_ID, 0.1);
+ tauP.addConstraint(new GreaterEqualConstraint(0));
+ if (config.grab(tauP)) {
+ tau = tauP.doubleValue();
}
}
@@ -290,4 +284,4 @@ public class ERiCDistanceFunction extends AbstractIndexBasedDistanceFunction<Num
return new ERiCDistanceFunction(factory, delta, tau);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/correlation/PCABasedCorrelationDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/correlation/PCABasedCorrelationDistanceFunction.java
index 083fc013..20f96a29 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/correlation/PCABasedCorrelationDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/correlation/PCABasedCorrelationDistanceFunction.java
@@ -32,7 +32,6 @@ import de.lmu.ifi.dbs.elki.distance.distancevalue.PCACorrelationDistance;
import de.lmu.ifi.dbs.elki.index.IndexFactory;
import de.lmu.ifi.dbs.elki.index.preprocessed.localpca.FilteredLocalPCAIndex;
import de.lmu.ifi.dbs.elki.index.preprocessed.localpca.KNNQueryFilteredPCAIndex;
-import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Matrix;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector;
import de.lmu.ifi.dbs.elki.math.linearalgebra.pca.PCAFilteredResult;
@@ -48,12 +47,7 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.DoubleParameter;
*
* @apiviz.has Instance
*/
-public class PCABasedCorrelationDistanceFunction extends AbstractIndexBasedDistanceFunction<NumberVector<?, ?>, FilteredLocalPCAIndex<NumberVector<?, ?>>, PCACorrelationDistance> implements FilteredLocalPCABasedDistanceFunction<NumberVector<?, ?>, FilteredLocalPCAIndex<NumberVector<?, ?>>, PCACorrelationDistance> {
- /**
- * Logger for debug.
- */
- static Logging logger = Logging.getLogger(PCABasedCorrelationDistanceFunction.class);
-
+public class PCABasedCorrelationDistanceFunction extends AbstractIndexBasedDistanceFunction<NumberVector<?>, FilteredLocalPCAIndex<NumberVector<?>>, PCACorrelationDistance> implements FilteredLocalPCABasedDistanceFunction<NumberVector<?>, FilteredLocalPCAIndex<NumberVector<?>>, PCACorrelationDistance> {
/**
* Parameter to specify the threshold of a distance between a vector q and a
* given space that indicates that q adds a new dimension to the space, must
@@ -65,7 +59,7 @@ public class PCABasedCorrelationDistanceFunction extends AbstractIndexBasedDista
* Key: {@code -pcabasedcorrelationdf.delta}
* </p>
*/
- public static final OptionID DELTA_ID = OptionID.getOrCreateOptionID("pcabasedcorrelationdf.delta", "Threshold of a distance between a vector q and a given space that indicates that " + "q adds a new dimension to the space.");
+ public static final OptionID DELTA_ID = new OptionID("pcabasedcorrelationdf.delta", "Threshold of a distance between a vector q and a given space that indicates that " + "q adds a new dimension to the space.");
/**
* Holds the value of {@link #DELTA_ID}.
@@ -78,7 +72,7 @@ public class PCABasedCorrelationDistanceFunction extends AbstractIndexBasedDista
* @param indexFactory index factory
* @param delta Delta parameter
*/
- public PCABasedCorrelationDistanceFunction(IndexFactory<NumberVector<?, ?>, FilteredLocalPCAIndex<NumberVector<?, ?>>> indexFactory, double delta) {
+ public PCABasedCorrelationDistanceFunction(IndexFactory<NumberVector<?>, FilteredLocalPCAIndex<NumberVector<?>>> indexFactory, double delta) {
super(indexFactory);
this.delta = delta;
}
@@ -89,20 +83,20 @@ public class PCABasedCorrelationDistanceFunction extends AbstractIndexBasedDista
}
@Override
- public <T extends NumberVector<?, ?>> Instance<T> instantiate(Relation<T> database) {
+ public <T extends NumberVector<?>> Instance<T> instantiate(Relation<T> database) {
// We can't really avoid these warnings, due to a limitation in Java
// Generics (AFAICT)
@SuppressWarnings("unchecked")
- FilteredLocalPCAIndex<T> indexinst = (FilteredLocalPCAIndex<T>) indexFactory.instantiate((Relation<NumberVector<?, ?>>) database);
+ FilteredLocalPCAIndex<T> indexinst = (FilteredLocalPCAIndex<T>) indexFactory.instantiate((Relation<NumberVector<?>>) database);
return new Instance<T>(database, indexinst, delta, this);
}
@Override
public boolean equals(Object obj) {
- if(obj == null) {
+ if (obj == null) {
return false;
}
- if(!this.getClass().equals(obj.getClass())) {
+ if (!this.getClass().equals(obj.getClass())) {
return false;
}
PCABasedCorrelationDistanceFunction other = (PCABasedCorrelationDistanceFunction) obj;
@@ -114,7 +108,7 @@ public class PCABasedCorrelationDistanceFunction extends AbstractIndexBasedDista
*
* @author Erich Schubert
*/
- public static class Instance<V extends NumberVector<?, ?>> extends AbstractIndexBasedDistanceFunction.Instance<V, FilteredLocalPCAIndex<V>, PCACorrelationDistance, PCABasedCorrelationDistanceFunction> implements FilteredLocalPCABasedDistanceFunction.Instance<V, FilteredLocalPCAIndex<V>, PCACorrelationDistance> {
+ public static class Instance<V extends NumberVector<?>> extends AbstractIndexBasedDistanceFunction.Instance<V, FilteredLocalPCAIndex<V>, PCACorrelationDistance, PCABasedCorrelationDistanceFunction> implements FilteredLocalPCABasedDistanceFunction.Instance<V, FilteredLocalPCAIndex<V>, PCACorrelationDistance> {
/**
* Delta value
*/
@@ -172,7 +166,7 @@ public class PCABasedCorrelationDistanceFunction extends AbstractIndexBasedDista
// for all strong eigenvectors of rv2
Matrix m1_czech = pca1.dissimilarityMatrix();
- for(int i = 0; i < v2_strong.getColumnDimensionality(); i++) {
+ for (int i = 0; i < v2_strong.getColumnDimensionality(); i++) {
Vector v2_i = v2_strong.getCol(i);
// check, if distance of v2_i to the space of rv1 > delta
// (i.e., if v2_i spans up a new dimension)
@@ -180,7 +174,7 @@ public class PCABasedCorrelationDistanceFunction extends AbstractIndexBasedDista
// if so, insert v2_i into v1 and adjust v1
// and compute m1_czech new, increase lambda1
- if(lambda1 < dimensionality && dist > delta) {
+ if (lambda1 < dimensionality && dist > delta) {
adjust(v1, e1_czech, v2_i, lambda1++);
m1_czech = v1.times(e1_czech).timesTranspose(v1);
}
@@ -188,7 +182,7 @@ public class PCABasedCorrelationDistanceFunction extends AbstractIndexBasedDista
// for all strong eigenvectors of rv1
Matrix m2_czech = pca2.dissimilarityMatrix();
- for(int i = 0; i < v1_strong.getColumnDimensionality(); i++) {
+ for (int i = 0; i < v1_strong.getColumnDimensionality(); i++) {
Vector v1_i = v1_strong.getCol(i);
// check, if distance of v1_i to the space of rv2 > delta
// (i.e., if v1_i spans up a new dimension)
@@ -196,7 +190,7 @@ public class PCABasedCorrelationDistanceFunction extends AbstractIndexBasedDista
// if so, insert v1_i into v2 and adjust v2
// and compute m2_czech new , increase lambda2
- if(lambda2 < dimensionality && dist > delta) {
+ if (lambda2 < dimensionality && dist > delta) {
adjust(v2, e2_czech, v1_i, lambda2++);
m2_czech = v2.times(e2_czech).timesTranspose(v2);
}
@@ -237,7 +231,7 @@ public class PCABasedCorrelationDistanceFunction extends AbstractIndexBasedDista
// normalize v
Vector v_i = vector.copy();
Vector sum = new Vector(dim);
- for(int k = 0; k < corrDim; k++) {
+ for (int k = 0; k < corrDim; k++) {
Vector v_k = v.getCol(k);
sum.plusTimesEquals(v_k, v_i.transposeTimes(v_k));
}
@@ -254,12 +248,12 @@ public class PCABasedCorrelationDistanceFunction extends AbstractIndexBasedDista
* @return the Euclidean distance between the given two vectors
*/
private double euclideanDistance(V dv1, V dv2) {
- if(dv1.getDimensionality() != dv2.getDimensionality()) {
+ if (dv1.getDimensionality() != dv2.getDimensionality()) {
throw new IllegalArgumentException("Different dimensionality of FeatureVectors\n first argument: " + dv1.toString() + "\n second argument: " + dv2.toString());
}
double sqrDist = 0;
- for(int i = 1; i <= dv1.getDimensionality(); i++) {
+ for (int i = 0; i < dv1.getDimensionality(); i++) {
double manhattanI = dv1.doubleValue(i) - dv2.doubleValue(i);
sqrDist += manhattanI * manhattanI;
}
@@ -274,7 +268,7 @@ public class PCABasedCorrelationDistanceFunction extends AbstractIndexBasedDista
*
* @apiviz.exclude
*/
- public static class Parameterizer extends AbstractIndexBasedDistanceFunction.Parameterizer<FilteredLocalPCAIndex.Factory<NumberVector<?, ?>, FilteredLocalPCAIndex<NumberVector<?, ?>>>> {
+ public static class Parameterizer extends AbstractIndexBasedDistanceFunction.Parameterizer<FilteredLocalPCAIndex.Factory<NumberVector<?>, FilteredLocalPCAIndex<NumberVector<?>>>> {
protected double delta = 0.0;
@Override
@@ -282,9 +276,10 @@ public class PCABasedCorrelationDistanceFunction extends AbstractIndexBasedDista
super.makeOptions(config);
configIndexFactory(config, FilteredLocalPCAIndex.Factory.class, KNNQueryFilteredPCAIndex.Factory.class);
- final DoubleParameter param = new DoubleParameter(DELTA_ID, new GreaterEqualConstraint(0), 0.25);
- if(config.grab(param)) {
- delta = param.getValue();
+ final DoubleParameter param = new DoubleParameter(DELTA_ID, 0.25);
+ param.addConstraint(new GreaterEqualConstraint(0));
+ if (config.grab(param)) {
+ delta = param.doubleValue();
}
}
@@ -293,4 +288,4 @@ public class PCABasedCorrelationDistanceFunction extends AbstractIndexBasedDista
return new PCABasedCorrelationDistanceFunction(factory, delta);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/correlation/PearsonCorrelationDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/correlation/PearsonCorrelationDistanceFunction.java
index a618ae36..1ba14af2 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/correlation/PearsonCorrelationDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/correlation/PearsonCorrelationDistanceFunction.java
@@ -72,7 +72,7 @@ public class PearsonCorrelationDistanceFunction extends AbstractVectorDoubleDist
* and v2
*/
@Override
- public double doubleDistance(NumberVector<?, ?> v1, NumberVector<?, ?> v2) {
+ public double doubleDistance(NumberVector<?> v1, NumberVector<?> v2) {
return 1 - MathUtil.pearsonCorrelationCoefficient(v1, v2);
}
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/correlation/SquaredPearsonCorrelationDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/correlation/SquaredPearsonCorrelationDistanceFunction.java
index 86d822d3..b4f8a3bd 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/correlation/SquaredPearsonCorrelationDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/correlation/SquaredPearsonCorrelationDistanceFunction.java
@@ -74,7 +74,7 @@ public class SquaredPearsonCorrelationDistanceFunction extends AbstractVectorDou
* vectors v1 and v2
*/
@Override
- public double doubleDistance(NumberVector<?, ?> v1, NumberVector<?, ?> v2) {
+ public double doubleDistance(NumberVector<?> v1, NumberVector<?> v2) {
final double pcc = MathUtil.pearsonCorrelationCoefficient(v1, v2);
return 1 - pcc * pcc;
}
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/correlation/WeightedPearsonCorrelationDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/correlation/WeightedPearsonCorrelationDistanceFunction.java
index 01e41f1b..fa1196fe 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/correlation/WeightedPearsonCorrelationDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/correlation/WeightedPearsonCorrelationDistanceFunction.java
@@ -76,7 +76,7 @@ public class WeightedPearsonCorrelationDistanceFunction extends AbstractVectorDo
* and v2
*/
@Override
- public double doubleDistance(NumberVector<?, ?> v1, NumberVector<?, ?> v2) {
+ public double doubleDistance(NumberVector<?> v1, NumberVector<?> v2) {
return 1 - MathUtil.weightedPearsonCorrelationCoefficient(v1, v2, weights);
}
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/correlation/WeightedSquaredPearsonCorrelationDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/correlation/WeightedSquaredPearsonCorrelationDistanceFunction.java
index 8b5ecd98..141229f6 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/correlation/WeightedSquaredPearsonCorrelationDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/correlation/WeightedSquaredPearsonCorrelationDistanceFunction.java
@@ -78,7 +78,7 @@ public class WeightedSquaredPearsonCorrelationDistanceFunction extends AbstractV
* vectors v1 and v2
*/
@Override
- public double doubleDistance(NumberVector<?, ?> v1, NumberVector<?, ?> v2) {
+ public double doubleDistance(NumberVector<?> v1, NumberVector<?> v2) {
final double pcc = MathUtil.weightedPearsonCorrelationCoefficient(v1, v2, weights);
return 1 - pcc * pcc;
}
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/external/DiskCacheBasedDoubleDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/external/DiskCacheBasedDoubleDistanceFunction.java
index 9e865039..a7ddb1ad 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/external/DiskCacheBasedDoubleDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/external/DiskCacheBasedDoubleDistanceFunction.java
@@ -27,6 +27,7 @@ import java.io.File;
import java.io.IOException;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.distance.distancefunction.AbstractDBIDDistanceFunction;
import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
import de.lmu.ifi.dbs.elki.persistent.OnDiskUpperTriangleMatrix;
@@ -56,7 +57,7 @@ public class DiskCacheBasedDoubleDistanceFunction extends AbstractDBIDDistanceFu
* Key: {@code -distance.matrix}
* </p>
*/
- public static final OptionID MATRIX_ID = OptionID.getOrCreateOptionID("distance.matrix", "The name of the file containing the distance matrix.");
+ public static final OptionID MATRIX_ID = new OptionID("distance.matrix", "The name of the file containing the distance matrix.");
/**
* Magic to identify double cache matrices
@@ -101,17 +102,19 @@ public class DiskCacheBasedDoubleDistanceFunction extends AbstractDBIDDistanceFu
if(id2 == null) {
return getDistanceFactory().undefinedDistance();
}
- if(id1.getIntegerID() < 0 || id2.getIntegerID() < 0) {
+ final int intid1 = DBIDUtil.asInteger(id1);
+ final int intid2 = DBIDUtil.asInteger(id2);
+ if(intid1 < 0 || intid2 < 0) {
throw new AbortException("Negative DBIDs not supported in OnDiskCache");
}
// the smaller id is the first key
- if(id1.getIntegerID() > id2.getIntegerID()) {
+ if(intid1 > intid2) {
return distance(id2, id1);
}
double distance;
try {
- distance = cache.getRecordBuffer(id1.getIntegerID(), id2.getIntegerID()).getDouble();
+ distance = cache.getRecordBuffer(intid1, intid2).getDouble();
}
catch(IOException e) {
throw new RuntimeException("Read error when loading distance " + id1 + "," + id2 + " from cache file.", e);
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/external/DiskCacheBasedFloatDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/external/DiskCacheBasedFloatDistanceFunction.java
index df29c280..23da36b2 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/external/DiskCacheBasedFloatDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/external/DiskCacheBasedFloatDistanceFunction.java
@@ -27,6 +27,7 @@ import java.io.File;
import java.io.IOException;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.distance.distancefunction.AbstractDBIDDistanceFunction;
import de.lmu.ifi.dbs.elki.distance.distancevalue.FloatDistance;
import de.lmu.ifi.dbs.elki.persistent.OnDiskUpperTriangleMatrix;
@@ -56,7 +57,7 @@ public class DiskCacheBasedFloatDistanceFunction extends AbstractDBIDDistanceFun
* Key: {@code -distance.matrix}
* </p>
*/
- public static final OptionID MATRIX_ID = OptionID.getOrCreateOptionID("distance.matrix", "The name of the file containing the distance matrix.");
+ public static final OptionID MATRIX_ID = new OptionID("distance.matrix", "The name of the file containing the distance matrix.");
/**
* Magic to identify double cache matrices
@@ -101,17 +102,19 @@ public class DiskCacheBasedFloatDistanceFunction extends AbstractDBIDDistanceFun
if(id2 == null) {
return getDistanceFactory().undefinedDistance();
}
- if(id1.getIntegerID() < 0 || id2.getIntegerID() < 0) {
+ final int intid1 = DBIDUtil.asInteger(id1);
+ final int intid2 = DBIDUtil.asInteger(id2);
+ if(intid1 < 0 || intid2 < 0) {
throw new AbortException("Negative DBIDs not supported in OnDiskCache");
}
// the smaller id is the first key
- if(id1.getIntegerID() > id2.getIntegerID()) {
+ if(intid1 > intid2) {
return distance(id2, id1);
}
float distance;
try {
- distance = cache.getRecordBuffer(id1.getIntegerID(), id2.getIntegerID()).getFloat();
+ distance = cache.getRecordBuffer(intid1, intid2).getFloat();
}
catch(IOException e) {
throw new RuntimeException("Read error when loading distance " + id1 + "," + id2 + " from cache file.", e);
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/external/DistanceParser.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/external/DistanceParser.java
index 93874eaf..c332670a 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/external/DistanceParser.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/external/DistanceParser.java
@@ -41,7 +41,7 @@ public interface DistanceParser<D extends Distance<D>> {
/**
* Parameter for distance function.
*/
- public static final OptionID DISTANCE_ID = OptionID.getOrCreateOptionID("parser.distance", "Distance type used for parsing values.");
+ public static final OptionID DISTANCE_ID = new OptionID("parser.distance", "Distance type used for parsing values.");
/**
* Returns a list of the objects parsed from the specified input stream
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/external/DistanceParsingResult.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/external/DistanceParsingResult.java
index 1fc15b52..916451c0 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/external/DistanceParsingResult.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/external/DistanceParsingResult.java
@@ -25,13 +25,13 @@ package de.lmu.ifi.dbs.elki.distance.distancefunction.external;
import java.util.Map;
-import de.lmu.ifi.dbs.elki.datasource.bundle.MultipleObjectsBundle;
import de.lmu.ifi.dbs.elki.database.ids.DBIDPair;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
/**
- * Provides a list of database objects and labels associated with these objects
- * and a cache of precomputed distances between the database objects.
+ * Provides a cache of precomputed distances between the database objects.
+ *
+ * TODO: this class needs to reworked.
*
* @author Elke Achtert
*
@@ -42,23 +42,15 @@ public class DistanceParsingResult<D extends Distance<D>> {
* The cache of precomputed distances between the database objects.
*/
private final Map<DBIDPair, D> distanceCache;
-
- /**
- * Objects representation (DBIDs and/or external IDs)
- */
- private MultipleObjectsBundle objects;
/**
* Provides a list of database objects, a list of label objects associated
* with these objects and cached distances between these objects.
*
- * @param objectAndLabelList the list of database objects and labels
- * associated with these objects
* @param distanceCache the cache of precomputed distances between the
* database objects
*/
- public DistanceParsingResult(MultipleObjectsBundle objectAndLabelList, Map<DBIDPair, D> distanceCache) {
- this.objects = objectAndLabelList;
+ public DistanceParsingResult(Map<DBIDPair, D> distanceCache) {
this.distanceCache = distanceCache;
}
@@ -70,13 +62,4 @@ public class DistanceParsingResult<D extends Distance<D>> {
public Map<DBIDPair, D> getDistanceCache() {
return distanceCache;
}
-
- /**
- * Get the objects
- *
- * @return the objects bundle
- */
- public MultipleObjectsBundle getObjects() {
- return objects;
- }
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/external/FileBasedDoubleDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/external/FileBasedDoubleDistanceFunction.java
index e8f379cd..5489a241 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/external/FileBasedDoubleDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/external/FileBasedDoubleDistanceFunction.java
@@ -23,6 +23,7 @@ package de.lmu.ifi.dbs.elki.distance.distancefunction.external;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
@@ -32,8 +33,6 @@ import java.util.Map;
import de.lmu.ifi.dbs.elki.database.ids.DBIDPair;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
-import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
-import de.lmu.ifi.dbs.elki.database.ids.ModifiableDBIDs;
import de.lmu.ifi.dbs.elki.distance.distancefunction.AbstractDBIDDistanceFunction;
import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
import de.lmu.ifi.dbs.elki.utilities.FileUtil;
@@ -52,6 +51,8 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ObjectParameter;
* Provides a DistanceFunction that is based on double distances given by a
* distance matrix of an external file.
*
+ * See {@link NumberDistanceParser} for the default input format.
+ *
* @author Elke Achtert
*/
@Title("File based double distance for database objects.")
@@ -63,7 +64,7 @@ public class FileBasedDoubleDistanceFunction extends AbstractDBIDDistanceFunctio
* Key: {@code -distance.matrix}
* </p>
*/
- public static final OptionID MATRIX_ID = OptionID.getOrCreateOptionID("distance.matrix", "The name of the file containing the distance matrix.");
+ public static final OptionID MATRIX_ID = new OptionID("distance.matrix", "The name of the file containing the distance matrix.");
/**
* Optional parameter to specify the parsers to provide a database, must
@@ -73,7 +74,7 @@ public class FileBasedDoubleDistanceFunction extends AbstractDBIDDistanceFunctio
* Key: {@code -distance.parser}
* </p>
*/
- public static final OptionID PARSER_ID = OptionID.getOrCreateOptionID("distance.parser", "Parser used to load the distance matrix.");
+ public static final OptionID PARSER_ID = new OptionID("distance.parser", "Parser used to load the distance matrix.");
/**
* The distance cache
@@ -115,33 +116,23 @@ public class FileBasedDoubleDistanceFunction extends AbstractDBIDDistanceFunctio
return getDistanceFactory().undefinedDistance();
}
// the smaller id is the first key
- if(id1.getIntegerID() > id2.getIntegerID()) {
+ if(DBIDUtil.compare(id1, id2) > 0) {
return distance(id2, id1);
}
- return cache.get(DBIDUtil.newPair(id1, id2));
+ DoubleDistance ret = cache.get(DBIDUtil.newPair(id1, id2));
+ if (ret == null && DBIDUtil.equal(id1, id2)) {
+ return DoubleDistance.ZERO_DISTANCE;
+ }
+ return ret;
}
private void loadCache(DistanceParser<DoubleDistance> parser, File matrixfile) throws IOException {
- InputStream in = FileUtil.tryGzipInput(new FileInputStream(matrixfile));
+ InputStream in = new BufferedInputStream(FileUtil.tryGzipInput(new FileInputStream(matrixfile)));
DistanceParsingResult<DoubleDistance> res = parser.parse(in);
cache = res.getDistanceCache();
}
- /**
- * Return a collection of all IDs in the cache.
- *
- * @return Collection of all IDs in the cache.
- */
- public DBIDs getIDs() {
- ModifiableDBIDs ids = DBIDUtil.newHashSet();
- for(DBIDPair pair : cache.keySet()) {
- ids.add(pair.getFirst());
- ids.add(pair.getSecond());
- }
- return ids;
- }
-
@Override
public DoubleDistance getDistanceFactory() {
return DoubleDistance.FACTORY;
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/external/FileBasedFloatDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/external/FileBasedFloatDistanceFunction.java
index 675a46f3..520c1f76 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/external/FileBasedFloatDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/external/FileBasedFloatDistanceFunction.java
@@ -23,6 +23,7 @@ package de.lmu.ifi.dbs.elki.distance.distancefunction.external;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
@@ -50,6 +51,8 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ObjectParameter;
* Provides a DistanceFunction that is based on float distances given by a
* distance matrix of an external file.
*
+ * See {@link NumberDistanceParser} for the default input format.
+ *
* @author Elke Achtert
*/
@Title("File based float distance for database objects.")
@@ -61,7 +64,7 @@ public class FileBasedFloatDistanceFunction extends AbstractDBIDDistanceFunction
* Key: {@code -distance.matrix}
* </p>
*/
- public static final OptionID MATRIX_ID = OptionID.getOrCreateOptionID("distance.matrix", "The name of the file containing the distance matrix.");
+ public static final OptionID MATRIX_ID = new OptionID("distance.matrix", "The name of the file containing the distance matrix.");
/**
* Optional parameter to specify the parsers to provide a database, must
@@ -71,7 +74,7 @@ public class FileBasedFloatDistanceFunction extends AbstractDBIDDistanceFunction
* Key: {@code -distance.parser}
* </p>
*/
- public static final OptionID PARSER_ID = OptionID.getOrCreateOptionID("distance.parser", "Parser used to load the distance matrix.");
+ public static final OptionID PARSER_ID = new OptionID("distance.parser", "Parser used to load the distance matrix.");
/**
* The distance cache
@@ -113,15 +116,18 @@ public class FileBasedFloatDistanceFunction extends AbstractDBIDDistanceFunction
return getDistanceFactory().undefinedDistance();
}
// the smaller id is the first key
- if(id1.getIntegerID() > id2.getIntegerID()) {
+ if(DBIDUtil.compare(id1, id2) > 0) {
return distance(id2, id1);
}
-
- return cache.get(DBIDUtil.newPair(id1, id2));
+ FloatDistance ret = cache.get(DBIDUtil.newPair(id1, id2));
+ if (ret == null && DBIDUtil.equal(id1, id2)) {
+ return FloatDistance.ZERO_DISTANCE;
+ }
+ return ret;
}
private void loadCache(DistanceParser<FloatDistance> parser, File matrixfile) throws IOException {
- InputStream in = FileUtil.tryGzipInput(new FileInputStream(matrixfile));
+ InputStream in = new BufferedInputStream(FileUtil.tryGzipInput(new FileInputStream(matrixfile)));
DistanceParsingResult<FloatDistance> res = parser.parse(in);
cache = res.getDistanceCache();
}
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/external/NumberDistanceParser.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/external/NumberDistanceParser.java
index 3af02ac3..82cc5b27 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/external/NumberDistanceParser.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/external/NumberDistanceParser.java
@@ -27,24 +27,22 @@ import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
-import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
-import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDPair;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.ModifiableDBIDs;
-import de.lmu.ifi.dbs.elki.datasource.bundle.MultipleObjectsBundle;
import de.lmu.ifi.dbs.elki.datasource.parser.AbstractParser;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance;
import de.lmu.ifi.dbs.elki.logging.Logging;
+import de.lmu.ifi.dbs.elki.logging.progress.IndefiniteProgress;
import de.lmu.ifi.dbs.elki.utilities.documentation.Description;
import de.lmu.ifi.dbs.elki.utilities.documentation.Title;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
@@ -69,7 +67,7 @@ public class NumberDistanceParser<D extends NumberDistance<D, ?>> extends Abstra
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(NumberDistanceParser.class);
+ private static final Logging LOG = Logging.getLogger(NumberDistanceParser.class);
/**
* The distance function.
@@ -79,8 +77,8 @@ public class NumberDistanceParser<D extends NumberDistance<D, ?>> extends Abstra
/**
* Constructor.
*
- * @param colSep
- * @param quoteChar
+ * @param colSep Column separator pattern
+ * @param quoteChar Quote character
* @param distanceFactory Distance factory to use
*/
public NumberDistanceParser(Pattern colSep, char quoteChar, D distanceFactory) {
@@ -93,32 +91,30 @@ public class NumberDistanceParser<D extends NumberDistance<D, ?>> extends Abstra
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
int lineNumber = 0;
+ IndefiniteProgress prog = LOG.isVerbose() ? new IndefiniteProgress("Parsing distance matrix", LOG) : null;
ModifiableDBIDs ids = DBIDUtil.newHashSet();
Map<DBIDPair, D> distanceCache = new HashMap<DBIDPair, D>();
try {
- for(String line; (line = reader.readLine()) != null; lineNumber++) {
- if(lineNumber % 10000 == 0 && logger.isDebugging()) {
- logger.debugFine("parse " + lineNumber / 10000);
- // logger.fine("parse " + lineNumber / 10000);
+ for (String line; (line = reader.readLine()) != null; lineNumber++) {
+ if (prog != null) {
+ prog.incrementProcessed(LOG);
}
- if(!line.startsWith(COMMENT) && line.length() > 0) {
+ if (!line.startsWith(COMMENT) && line.length() > 0) {
List<String> entries = tokenize(line);
- if(entries.size() != 3) {
+ if (entries.size() != 3) {
throw new IllegalArgumentException("Line " + lineNumber + " does not have the " + "required input format: id1 id2 distanceValue! " + line);
}
DBID id1, id2;
try {
id1 = DBIDUtil.importInteger(Integer.parseInt(entries.get(0)));
- }
- catch(NumberFormatException e) {
+ } catch (NumberFormatException e) {
throw new IllegalArgumentException("Error in line " + lineNumber + ": id1 is no integer!");
}
try {
id2 = DBIDUtil.importInteger(Integer.parseInt(entries.get(1)));
- }
- catch(NumberFormatException e) {
+ } catch (NumberFormatException e) {
throw new IllegalArgumentException("Error in line " + lineNumber + ": id2 is no integer!");
}
@@ -127,44 +123,31 @@ public class NumberDistanceParser<D extends NumberDistance<D, ?>> extends Abstra
put(id1, id2, distance, distanceCache);
ids.add(id1);
ids.add(id2);
- }
- catch(IllegalArgumentException e) {
+ } catch (IllegalArgumentException e) {
throw new IllegalArgumentException("Error in line " + lineNumber + ":" + e.getMessage(), e);
}
}
}
- }
- catch(IOException e) {
+ } catch (IOException e) {
throw new IllegalArgumentException("Error while parsing line " + lineNumber + ".");
}
- if(logger.isDebugging()) {
- logger.debugFine("check");
+ if (prog != null) {
+ prog.setCompleted(LOG);
}
// check if all distance values are specified
for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
for (DBIDIter iter2 = ids.iter(); iter2.valid(); iter2.advance()) {
- if(iter2.getIntegerID() < iter.getIntegerID()) {
+ if (DBIDUtil.compare(iter2, iter) <= 0) {
continue;
}
- if(!containsKey(iter, iter2, distanceCache)) {
- throw new IllegalArgumentException("Distance value for " + iter.getDBID() + " - " + iter2.getDBID() + " is missing!");
+ if (!containsKey(iter, iter2, distanceCache)) {
+ throw new IllegalArgumentException("Distance value for " + DBIDUtil.toString(iter) + " - " + DBIDUtil.toString(iter2) + " is missing!");
}
}
}
-
- if(logger.isDebugging()) {
- logger.debugFine("add to objectAndLabelsList");
- }
-
- // This is unusual for ELKI, but here we really need an ArrayList, not a
- // DBIDs object. So convert.
- List<DBID> objects = new ArrayList<DBID>(ids.size());
- for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
- objects.add(iter.getDBID());
- }
- return new DistanceParsingResult<D>(MultipleObjectsBundle.makeSimple(TypeUtil.DBID, objects), distanceCache);
+ return new DistanceParsingResult<D>(distanceCache);
}
/**
@@ -177,14 +160,14 @@ public class NumberDistanceParser<D extends NumberDistance<D, ?>> extends Abstra
*/
private void put(DBID id1, DBID id2, D distance, Map<DBIDPair, D> cache) {
// the smaller id is the first key
- if(id1.getIntegerID() > id2.getIntegerID()) {
+ if (DBIDUtil.compare(id1, id2) > 0) {
put(id2, id1, distance, cache);
return;
}
D oldDistance = cache.put(DBIDUtil.newPair(id1, id2), distance);
- if(oldDistance != null) {
+ if (oldDistance != null) {
throw new IllegalArgumentException("Distance value for specified ids is already assigned!");
}
}
@@ -200,7 +183,7 @@ public class NumberDistanceParser<D extends NumberDistance<D, ?>> extends Abstra
* specified ids, false otherwise
*/
public boolean containsKey(DBIDRef id1, DBIDRef id2, Map<DBIDPair, D> cache) {
- if(id1.getIntegerID() > id2.getIntegerID()) {
+ if (DBIDUtil.compare(id1, id2) > 0) {
return containsKey(id2, id1, cache);
}
@@ -209,7 +192,7 @@ public class NumberDistanceParser<D extends NumberDistance<D, ?>> extends Abstra
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -229,7 +212,7 @@ public class NumberDistanceParser<D extends NumberDistance<D, ?>> extends Abstra
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
ObjectParameter<D> distFuncP = new ObjectParameter<D>(DISTANCE_ID, Distance.class);
- if(config.grab(distFuncP)) {
+ if (config.grab(distFuncP)) {
distanceFactory = distFuncP.instantiateClass(config);
}
}
@@ -239,4 +222,4 @@ public class NumberDistanceParser<D extends NumberDistance<D, ?>> extends Abstra
return new NumberDistanceParser<D>(colSep, quoteChar, distanceFactory);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/geo/DimensionSelectingLatLngDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/geo/DimensionSelectingLatLngDistanceFunction.java
index d4d7d9f5..42fd869d 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/geo/DimensionSelectingLatLngDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/geo/DimensionSelectingLatLngDistanceFunction.java
@@ -24,10 +24,15 @@ package de.lmu.ifi.dbs.elki.distance.distancefunction.geo;
*/
import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.data.spatial.SpatialComparable;
import de.lmu.ifi.dbs.elki.data.type.SimpleTypeInformation;
import de.lmu.ifi.dbs.elki.data.type.VectorFieldTypeInformation;
+import de.lmu.ifi.dbs.elki.database.query.distance.SpatialPrimitiveDistanceQuery;
+import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.distancefunction.AbstractVectorDoubleDistanceFunction;
-import de.lmu.ifi.dbs.elki.math.MathUtil;
+import de.lmu.ifi.dbs.elki.distance.distancefunction.SpatialPrimitiveDoubleDistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
+import de.lmu.ifi.dbs.elki.math.GeoUtil;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterEqualConstraint;
@@ -40,14 +45,14 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
*
* @author Erich Schubert
*/
-public class DimensionSelectingLatLngDistanceFunction extends AbstractVectorDoubleDistanceFunction {
+public class DimensionSelectingLatLngDistanceFunction extends AbstractVectorDoubleDistanceFunction implements SpatialPrimitiveDoubleDistanceFunction<NumberVector<?>> {
/**
- * Latitude dimension
+ * Latitude dimension.
*/
final int dimlat;
/**
- * Longitude dimension
+ * Longitude dimension.
*/
final int dimlng;
@@ -64,13 +69,42 @@ public class DimensionSelectingLatLngDistanceFunction extends AbstractVectorDoub
}
@Override
- public double doubleDistance(NumberVector<?, ?> o1, NumberVector<?, ?> o2) {
- return MathUtil.latlngDistance(o1.doubleValue(dimlat), o1.doubleValue(dimlng), o2.doubleValue(dimlat), o2.doubleValue(dimlng));
+ public double doubleDistance(NumberVector<?> o1, NumberVector<?> o2) {
+ return GeoUtil.haversineFormulaDeg(o1.doubleValue(dimlat), o1.doubleValue(dimlng), o2.doubleValue(dimlat), o2.doubleValue(dimlng));
}
@Override
- public SimpleTypeInformation<? super NumberVector<?, ?>> getInputTypeRestriction() {
- return new VectorFieldTypeInformation<NumberVector<?, ?>>(NumberVector.class, Math.max(dimlat, dimlng), Integer.MAX_VALUE);
+ public double doubleMinDist(SpatialComparable mbr1, SpatialComparable mbr2) {
+ if (mbr1 instanceof NumberVector) {
+ if (mbr2 instanceof NumberVector) {
+ return doubleDistance((NumberVector<?>) mbr1, (NumberVector<?>) mbr2);
+ } else {
+ NumberVector<?> o1 = (NumberVector<?>) mbr1;
+ return GeoUtil.latlngMinDistDeg(o1.doubleValue(dimlat), o1.doubleValue(dimlng), mbr2.getMin(dimlat), mbr2.getMin(dimlng), mbr2.getMax(dimlat), mbr2.getMax(dimlng));
+ }
+ } else {
+ if (mbr2 instanceof NumberVector) {
+ NumberVector<?> o2 = (NumberVector<?>) mbr2;
+ return GeoUtil.latlngMinDistDeg(o2.doubleValue(dimlat), o2.doubleValue(dimlng), mbr1.getMin(dimlat), mbr1.getMin(dimlng), mbr1.getMax(dimlat), mbr1.getMax(dimlng));
+ } else {
+ throw new UnsupportedOperationException("MBR to MBR mindist is not yet implemented.");
+ }
+ }
+ }
+
+ @Override
+ public DoubleDistance minDist(SpatialComparable mbr1, SpatialComparable mbr2) {
+ return new DoubleDistance(doubleMinDist(mbr1, mbr2));
+ }
+
+ @Override
+ public SimpleTypeInformation<? super NumberVector<?>> getInputTypeRestriction() {
+ return new VectorFieldTypeInformation<NumberVector<?>>(NumberVector.class, Math.max(dimlat, dimlng), Integer.MAX_VALUE);
+ }
+
+ @Override
+ public <T extends NumberVector<?>> SpatialPrimitiveDistanceQuery<T, DoubleDistance> instantiate(Relation<T> relation) {
+ return new SpatialPrimitiveDistanceQuery<T, DoubleDistance>(relation, this);
}
/**
@@ -84,32 +118,34 @@ public class DimensionSelectingLatLngDistanceFunction extends AbstractVectorDoub
/**
* Latitude dimension parameter.
*/
- public static final OptionID LATDIM_ID = OptionID.getOrCreateOptionID("distance.latitudedim", "The dimension containing the latitude.");
+ public static final OptionID LATDIM_ID = new OptionID("distance.latitudedim", "The dimension containing the latitude.");
/**
* Longitude dimension parameter.
*/
- public static final OptionID LNGDIM_ID = OptionID.getOrCreateOptionID("distance.longitudedim", "The dimension containing the longitude.");
+ public static final OptionID LNGDIM_ID = new OptionID("distance.longitudedim", "The dimension containing the longitude.");
/**
- * Latitude dimension
+ * Latitude dimension.
*/
int dimlat;
/**
- * Longitude dimension
+ * Longitude dimension.
*/
int dimlng;
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- final IntParameter dimlatP = new IntParameter(LATDIM_ID, new GreaterEqualConstraint(1));
- if(config.grab(dimlatP)) {
+ final IntParameter dimlatP = new IntParameter(LATDIM_ID);
+ dimlatP.addConstraint(new GreaterEqualConstraint(0));
+ if (config.grab(dimlatP)) {
dimlat = dimlatP.getValue();
}
- final IntParameter dimlngP = new IntParameter(LNGDIM_ID, new GreaterEqualConstraint(1));
- if(config.grab(dimlngP)) {
+ final IntParameter dimlngP = new IntParameter(LNGDIM_ID);
+ dimlngP.addConstraint(new GreaterEqualConstraint(0));
+ if (config.grab(dimlngP)) {
dimlng = dimlngP.getValue();
}
config.checkConstraint(new NoDuplicateValueGlobalConstraint(dimlatP, dimlngP));
@@ -120,4 +156,4 @@ public class DimensionSelectingLatLngDistanceFunction extends AbstractVectorDoub
return new DimensionSelectingLatLngDistanceFunction(dimlat, dimlng);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/geo/LatLngDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/geo/LatLngDistanceFunction.java
index c50b3326..31bed1c5 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/geo/LatLngDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/geo/LatLngDistanceFunction.java
@@ -24,10 +24,15 @@ package de.lmu.ifi.dbs.elki.distance.distancefunction.geo;
*/
import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.data.spatial.SpatialComparable;
import de.lmu.ifi.dbs.elki.data.type.SimpleTypeInformation;
import de.lmu.ifi.dbs.elki.data.type.VectorFieldTypeInformation;
+import de.lmu.ifi.dbs.elki.database.query.distance.SpatialPrimitiveDistanceQuery;
+import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.distancefunction.AbstractVectorDoubleDistanceFunction;
-import de.lmu.ifi.dbs.elki.math.MathUtil;
+import de.lmu.ifi.dbs.elki.distance.distancefunction.SpatialPrimitiveDoubleDistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
+import de.lmu.ifi.dbs.elki.math.GeoUtil;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
/**
@@ -35,9 +40,9 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
*
* @author Erich Schubert
*/
-public class LatLngDistanceFunction extends AbstractVectorDoubleDistanceFunction {
+public class LatLngDistanceFunction extends AbstractVectorDoubleDistanceFunction implements SpatialPrimitiveDoubleDistanceFunction<NumberVector<?>> {
/**
- * Static instance
+ * Static instance.
*/
public static final LatLngDistanceFunction STATIC = new LatLngDistanceFunction();
@@ -50,29 +55,58 @@ public class LatLngDistanceFunction extends AbstractVectorDoubleDistanceFunction
}
@Override
- public double doubleDistance(NumberVector<?, ?> o1, NumberVector<?, ?> o2) {
- return MathUtil.latlngDistance(o1.doubleValue(1), o1.doubleValue(2), o2.doubleValue(1), o2.doubleValue(2));
+ public double doubleDistance(NumberVector<?> o1, NumberVector<?> o2) {
+ return GeoUtil.haversineFormulaDeg(o1.doubleValue(0), o1.doubleValue(1), o2.doubleValue(0), o2.doubleValue(1));
}
@Override
- public SimpleTypeInformation<? super NumberVector<?, ?>> getInputTypeRestriction() {
- return VectorFieldTypeInformation.get(NumberVector.class, 2);
+ public double doubleMinDist(SpatialComparable mbr1, SpatialComparable mbr2) {
+ if (mbr1 instanceof NumberVector) {
+ if (mbr2 instanceof NumberVector) {
+ return doubleDistance((NumberVector<?>) mbr1, (NumberVector<?>) mbr2);
+ } else {
+ NumberVector<?> o1 = (NumberVector<?>) mbr1;
+ return GeoUtil.latlngMinDistDeg(o1.doubleValue(0), o1.doubleValue(1), mbr2.getMin(0), mbr2.getMin(1), mbr2.getMax(0), mbr2.getMax(1));
+ }
+ } else {
+ if (mbr2 instanceof NumberVector) {
+ NumberVector<?> o2 = (NumberVector<?>) mbr2;
+ return GeoUtil.latlngMinDistDeg(o2.doubleValue(0), o2.doubleValue(1), mbr1.getMin(0), mbr1.getMin(1), mbr1.getMax(0), mbr1.getMax(1));
+ } else {
+ throw new UnsupportedOperationException("MBR to MBR mindist is not yet implemented.");
+ }
+ }
+ }
+
+ @Override
+ public DoubleDistance minDist(SpatialComparable mbr1, SpatialComparable mbr2) {
+ return new DoubleDistance(doubleMinDist(mbr1, mbr2));
+ }
+
+ @Override
+ public SimpleTypeInformation<? super NumberVector<?>> getInputTypeRestriction() {
+ return new VectorFieldTypeInformation<NumberVector<?>>(NumberVector.class, 2);
}
@Override
public boolean equals(Object obj) {
- if(obj == null) {
+ if (obj == null) {
return false;
}
- if(obj == this) {
+ if (obj == this) {
return true;
}
- if(this.getClass().equals(obj.getClass())) {
+ if (this.getClass().equals(obj.getClass())) {
return true;
}
return super.equals(obj);
}
+ @Override
+ public <T extends NumberVector<?>> SpatialPrimitiveDistanceQuery<T, DoubleDistance> instantiate(Relation<T> relation) {
+ return new SpatialPrimitiveDistanceQuery<T, DoubleDistance>(relation, this);
+ }
+
/**
* Parameterization class.
*
@@ -86,4 +120,4 @@ public class LatLngDistanceFunction extends AbstractVectorDoubleDistanceFunction
return LatLngDistanceFunction.STATIC;
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/geo/LngLatDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/geo/LngLatDistanceFunction.java
index b48e7cae..7064224a 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/geo/LngLatDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/geo/LngLatDistanceFunction.java
@@ -24,10 +24,15 @@ package de.lmu.ifi.dbs.elki.distance.distancefunction.geo;
*/
import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.data.spatial.SpatialComparable;
import de.lmu.ifi.dbs.elki.data.type.SimpleTypeInformation;
import de.lmu.ifi.dbs.elki.data.type.VectorFieldTypeInformation;
+import de.lmu.ifi.dbs.elki.database.query.distance.SpatialPrimitiveDistanceQuery;
+import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.distancefunction.AbstractVectorDoubleDistanceFunction;
-import de.lmu.ifi.dbs.elki.math.MathUtil;
+import de.lmu.ifi.dbs.elki.distance.distancefunction.SpatialPrimitiveDoubleDistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
+import de.lmu.ifi.dbs.elki.math.GeoUtil;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
/**
@@ -35,9 +40,9 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
*
* @author Erich Schubert
*/
-public class LngLatDistanceFunction extends AbstractVectorDoubleDistanceFunction {
+public class LngLatDistanceFunction extends AbstractVectorDoubleDistanceFunction implements SpatialPrimitiveDoubleDistanceFunction<NumberVector<?>> {
/**
- * Static instance
+ * Static instance.
*/
public static final LngLatDistanceFunction STATIC = new LngLatDistanceFunction();
@@ -50,29 +55,58 @@ public class LngLatDistanceFunction extends AbstractVectorDoubleDistanceFunction
}
@Override
- public double doubleDistance(NumberVector<?, ?> o1, NumberVector<?, ?> o2) {
- return MathUtil.latlngDistance(o1.doubleValue(2), o1.doubleValue(1), o2.doubleValue(2), o2.doubleValue(1));
+ public double doubleDistance(NumberVector<?> o1, NumberVector<?> o2) {
+ return GeoUtil.haversineFormulaDeg(o1.doubleValue(1), o1.doubleValue(0), o2.doubleValue(1), o2.doubleValue(0));
}
@Override
- public SimpleTypeInformation<? super NumberVector<?, ?>> getInputTypeRestriction() {
- return VectorFieldTypeInformation.get(NumberVector.class, 2);
+ public double doubleMinDist(SpatialComparable mbr1, SpatialComparable mbr2) {
+ if (mbr1 instanceof NumberVector) {
+ if (mbr2 instanceof NumberVector) {
+ return doubleDistance((NumberVector<?>) mbr1, (NumberVector<?>) mbr2);
+ } else {
+ NumberVector<?> o1 = (NumberVector<?>) mbr1;
+ return GeoUtil.latlngMinDistDeg(o1.doubleValue(1), o1.doubleValue(0), mbr2.getMin(1), mbr2.getMin(0), mbr2.getMax(1), mbr2.getMax(0));
+ }
+ } else {
+ if (mbr2 instanceof NumberVector) {
+ NumberVector<?> o2 = (NumberVector<?>) mbr2;
+ return GeoUtil.latlngMinDistDeg(o2.doubleValue(1), o2.doubleValue(0), mbr1.getMin(1), mbr1.getMin(0), mbr1.getMax(1), mbr1.getMax(0));
+ } else {
+ throw new UnsupportedOperationException("MBR to MBR mindist is not yet implemented.");
+ }
+ }
+ }
+
+ @Override
+ public DoubleDistance minDist(SpatialComparable mbr1, SpatialComparable mbr2) {
+ return new DoubleDistance(doubleMinDist(mbr1, mbr2));
+ }
+
+ @Override
+ public SimpleTypeInformation<? super NumberVector<?>> getInputTypeRestriction() {
+ return new VectorFieldTypeInformation<NumberVector<?>>(NumberVector.class, 2);
}
@Override
public boolean equals(Object obj) {
- if(obj == null) {
+ if (obj == null) {
return false;
}
- if(obj == this) {
+ if (obj == this) {
return true;
}
- if(this.getClass().equals(obj.getClass())) {
+ if (this.getClass().equals(obj.getClass())) {
return true;
}
return super.equals(obj);
}
+ @Override
+ public <T extends NumberVector<?>> SpatialPrimitiveDistanceQuery<T, DoubleDistance> instantiate(Relation<T> relation) {
+ return new SpatialPrimitiveDistanceQuery<T, DoubleDistance>(relation, this);
+ }
+
/**
* Parameterization class.
*
@@ -86,4 +120,4 @@ public class LngLatDistanceFunction extends AbstractVectorDoubleDistanceFunction
return LngLatDistanceFunction.STATIC;
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/package-info.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/package-info.java
index c47db2f8..f71ae4a2 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/package-info.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/package-info.java
@@ -31,7 +31,12 @@
* @apiviz.exclude de.lmu.ifi.dbs.elki.application.*
* @apiviz.exclude de.lmu.ifi.dbs.elki.algorithm.*
* @apiviz.exclude de.lmu.ifi.dbs.elki.database.*
+ * @apiviz.exclude de.lmu.ifi.dbs.elki.distance.distancefunction.subspace.*
+ * @apiviz.exclude de.lmu.ifi.dbs.elki.distance.distancefunction.correlation.*
+ * @apiviz.exclude de.lmu.ifi.dbs.elki.distance.distancefunction.geo.*
+ * @apiviz.exclude de.lmu.ifi.dbs.elki.distance.distancefunction.timeseries.*
* @apiviz.exclude de.lmu.ifi.dbs.elki.index.*
+ * @apiviz.exclude de.lmu.ifi.dbs.elki.*Instance
*/
/*
This file is part of ELKI:
@@ -55,4 +60,4 @@ GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package de.lmu.ifi.dbs.elki.distance.distancefunction; \ No newline at end of file
+package de.lmu.ifi.dbs.elki.distance.distancefunction;
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/subspace/AbstractDimensionsSelectingDoubleDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/subspace/AbstractDimensionsSelectingDoubleDistanceFunction.java
index b58979ef..6bedd394 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/subspace/AbstractDimensionsSelectingDoubleDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/subspace/AbstractDimensionsSelectingDoubleDistanceFunction.java
@@ -31,7 +31,8 @@ import de.lmu.ifi.dbs.elki.distance.distancefunction.PrimitiveDoubleDistanceFunc
import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.ListGreaterEqualConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterEqualConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.ListEachConstraint;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntListParameter;
@@ -42,11 +43,11 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntListParameter;
* @author Elke Achtert
* @param <V> the type of FeatureVector to compute the distances in between
*/
-public abstract class AbstractDimensionsSelectingDoubleDistanceFunction<V extends FeatureVector<?, ?>> extends AbstractPrimitiveDistanceFunction<V, DoubleDistance> implements PrimitiveDoubleDistanceFunction<V>, DimensionSelectingSubspaceDistanceFunction<V, DoubleDistance> {
+public abstract class AbstractDimensionsSelectingDoubleDistanceFunction<V extends FeatureVector<?>> extends AbstractPrimitiveDistanceFunction<V, DoubleDistance> implements PrimitiveDoubleDistanceFunction<V>, DimensionSelectingSubspaceDistanceFunction<V, DoubleDistance> {
/**
* Dimensions parameter.
*/
- public static final OptionID DIMS_ID = OptionID.getOrCreateOptionID("distance.dims", "a comma separated array of integer values, where 1 <= d_i <= the dimensionality of the feature space specifying the dimensions to be considered for distance computation. If this parameter is not set, no dimensions will be considered, i.e. the distance between two objects is always 0.");
+ public static final OptionID DIMS_ID = new OptionID("distance.dims", "a comma separated array of integer values, where 0 <= d_i < the dimensionality of the feature space specifying the dimensions to be considered for distance computation. If this parameter is not set, no dimensions will be considered, i.e. the distance between two objects is always 0.");
/**
* The dimensions to be considered for distance computation.
@@ -86,10 +87,10 @@ public abstract class AbstractDimensionsSelectingDoubleDistanceFunction<V extend
@Override
public boolean equals(Object obj) {
- if(obj == null) {
+ if (obj == null) {
return false;
}
- if(!this.getClass().equals(obj.getClass())) {
+ if (!this.getClass().equals(obj.getClass())) {
return false;
}
return this.dimensions.equals(((AbstractDimensionsSelectingDoubleDistanceFunction<?>) obj).dimensions);
@@ -102,19 +103,21 @@ public abstract class AbstractDimensionsSelectingDoubleDistanceFunction<V extend
*
* @apiviz.exclude
*/
- public static abstract class Parameterizer extends AbstractParameterizer {
+ public abstract static class Parameterizer extends AbstractParameterizer {
protected BitSet dimensions = null;
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
dimensions = new BitSet();
- final IntListParameter dimsP = new IntListParameter(DIMS_ID, new ListGreaterEqualConstraint<Integer>(1), true);
- if(config.grab(dimsP)) {
- for(int d : dimsP.getValue()) {
- dimensions.set(d - 1);
+ final IntListParameter dimsP = new IntListParameter(DIMS_ID);
+ dimsP.addConstraint(new ListEachConstraint<Integer>(new GreaterEqualConstraint(0)));
+ dimsP.setOptional(true);
+ if (config.grab(dimsP)) {
+ for (int d : dimsP.getValue()) {
+ dimensions.set(d);
}
}
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/subspace/AbstractPreferenceVectorBasedCorrelationDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/subspace/AbstractPreferenceVectorBasedCorrelationDistanceFunction.java
index 8164cffa..e662bef1 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/subspace/AbstractPreferenceVectorBasedCorrelationDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/subspace/AbstractPreferenceVectorBasedCorrelationDistanceFunction.java
@@ -46,7 +46,7 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.DoubleParameter;
* @param <V> the type of NumberVector to compute the distances in between
* @param <P> the type of Preprocessor used
*/
-public abstract class AbstractPreferenceVectorBasedCorrelationDistanceFunction<V extends NumberVector<?, ?>, P extends PreferenceVectorIndex<V>> extends AbstractIndexBasedDistanceFunction<V, P, PreferenceVectorBasedCorrelationDistance> {
+public abstract class AbstractPreferenceVectorBasedCorrelationDistanceFunction<V extends NumberVector<?>, P extends PreferenceVectorIndex<V>> extends AbstractIndexBasedDistanceFunction<V, P, PreferenceVectorBasedCorrelationDistance> {
/**
* Parameter to specify the maximum distance between two vectors with equal
* preference vectors before considering them as parallel, must be a double
@@ -58,7 +58,7 @@ public abstract class AbstractPreferenceVectorBasedCorrelationDistanceFunction<V
* Key: {@code -pvbasedcorrelationdf.epsilon}
* </p>
*/
- public static final OptionID EPSILON_ID = OptionID.getOrCreateOptionID("distancefunction.epsilon", "The maximum distance between two vectors with equal preference vectors before considering them as parallel.");
+ public static final OptionID EPSILON_ID = new OptionID("distancefunction.epsilon", "The maximum distance between two vectors with equal preference vectors before considering them as parallel.");
/**
* Holds the value of {@link #EPSILON_ID}.
@@ -92,21 +92,22 @@ public abstract class AbstractPreferenceVectorBasedCorrelationDistanceFunction<V
@Override
public boolean equals(Object obj) {
- if(obj == null) {
+ if (obj == null) {
return false;
}
- if(!this.getClass().equals(obj.getClass())) {
+ if (!this.getClass().equals(obj.getClass())) {
return false;
}
AbstractPreferenceVectorBasedCorrelationDistanceFunction<?, ?> other = (AbstractPreferenceVectorBasedCorrelationDistanceFunction<?, ?>) obj;
return (this.indexFactory.equals(other.indexFactory)) && this.epsilon == other.epsilon;
}
+
/**
* Instance to compute the distances on an actual database.
*
* @author Erich Schubert
*/
- abstract public static class Instance<V extends NumberVector<?, ?>, P extends PreferenceVectorIndex<V>> extends AbstractIndexBasedDistanceFunction.Instance<V, P, PreferenceVectorBasedCorrelationDistance, AbstractPreferenceVectorBasedCorrelationDistanceFunction<? super V, ?>> {
+ abstract public static class Instance<V extends NumberVector<?>, P extends PreferenceVectorIndex<V>> extends AbstractIndexBasedDistanceFunction.Instance<V, P, PreferenceVectorBasedCorrelationDistance, AbstractPreferenceVectorBasedCorrelationDistanceFunction<? super V, ?>> {
/**
* The epsilon value
*/
@@ -157,13 +158,13 @@ public abstract class AbstractPreferenceVectorBasedCorrelationDistanceFunction<V
* to the given preference vector
*/
public double weightedDistance(V v1, V v2, BitSet weightVector) {
- if(v1.getDimensionality() != v2.getDimensionality()) {
+ if (v1.getDimensionality() != v2.getDimensionality()) {
throw new IllegalArgumentException("Different dimensionality of FeatureVectors\n first argument: " + v1.toString() + "\n second argument: " + v2.toString());
}
double sqrDist = 0;
- for(int i = 1; i <= v1.getDimensionality(); i++) {
- if(weightVector.get(i - 1)) {
+ for (int i = 0; i < v1.getDimensionality(); i++) {
+ if (weightVector.get(i)) {
double manhattanI = v1.doubleValue(i) - v2.doubleValue(i);
sqrDist += manhattanI * manhattanI;
}
@@ -210,7 +211,7 @@ public abstract class AbstractPreferenceVectorBasedCorrelationDistanceFunction<V
*
* @apiviz.exclude
*/
- public static abstract class Parameterizer<F extends IndexFactory<?, ?>> extends AbstractIndexBasedDistanceFunction.Parameterizer<F> {
+ public abstract static class Parameterizer<F extends IndexFactory<?, ?>> extends AbstractIndexBasedDistanceFunction.Parameterizer<F> {
protected double epsilon = 0.0;
@Override
@@ -220,10 +221,11 @@ public abstract class AbstractPreferenceVectorBasedCorrelationDistanceFunction<V
}
protected void configEpsilon(Parameterization config) {
- final DoubleParameter epsilonP = new DoubleParameter(EPSILON_ID, new GreaterEqualConstraint(0), 0.001);
- if(config.grab(epsilonP)) {
- epsilon = epsilonP.getValue();
+ final DoubleParameter epsilonP = new DoubleParameter(EPSILON_ID, 0.001);
+ epsilonP.addConstraint(new GreaterEqualConstraint(0));
+ if (config.grab(epsilonP)) {
+ epsilon = epsilonP.doubleValue();
}
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/subspace/DiSHDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/subspace/DiSHDistanceFunction.java
index f0eb8289..77633578 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/subspace/DiSHDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/subspace/DiSHDistanceFunction.java
@@ -27,11 +27,11 @@ import java.util.BitSet;
import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.distance.distancevalue.PreferenceVectorBasedCorrelationDistance;
import de.lmu.ifi.dbs.elki.index.preprocessed.preference.DiSHPreferenceVectorIndex;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.utilities.ClassGenericsUtil;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.utilities.FormatUtil;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
@@ -42,28 +42,28 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameteriz
*
* @apiviz.has Instance
*/
-public class DiSHDistanceFunction extends AbstractPreferenceVectorBasedCorrelationDistanceFunction<NumberVector<?, ?>, DiSHPreferenceVectorIndex<NumberVector<?, ?>>> {
+public class DiSHDistanceFunction extends AbstractPreferenceVectorBasedCorrelationDistanceFunction<NumberVector<?>, DiSHPreferenceVectorIndex<NumberVector<?>>> {
/**
* Logger for debug.
*/
- static Logging logger = Logging.getLogger(DiSHDistanceFunction.class);
+ private static final Logging LOG = Logging.getLogger(DiSHDistanceFunction.class);
/**
* Constructor.
*
- * @param indexFactory
- * @param epsilon
+ * @param indexFactory DiSH index factory
+ * @param epsilon Epsilon value
*/
- public DiSHDistanceFunction(DiSHPreferenceVectorIndex.Factory<NumberVector<?, ?>> indexFactory, double epsilon) {
+ public DiSHDistanceFunction(DiSHPreferenceVectorIndex.Factory<NumberVector<?>> indexFactory, double epsilon) {
super(indexFactory, epsilon);
}
@Override
- public <T extends NumberVector<?, ?>> Instance<T> instantiate(Relation<T> database) {
+ public <T extends NumberVector<?>> Instance<T> instantiate(Relation<T> database) {
// We can't really avoid these warnings, due to a limitation in Java
// Generics (AFAICT)
@SuppressWarnings("unchecked")
- DiSHPreferenceVectorIndex<T> indexinst = (DiSHPreferenceVectorIndex<T>) indexFactory.instantiate((Relation<NumberVector<?, ?>>) database);
+ DiSHPreferenceVectorIndex<T> indexinst = (DiSHPreferenceVectorIndex<T>) indexFactory.instantiate((Relation<NumberVector<?>>) database);
return new Instance<T>(database, indexinst, getEpsilon(), this);
}
@@ -74,7 +74,7 @@ public class DiSHDistanceFunction extends AbstractPreferenceVectorBasedCorrelati
*/
public int getMinpts() {
// TODO: get rid of this cast?
- return ((DiSHPreferenceVectorIndex.Factory<NumberVector<?, ?>>) indexFactory).getMinpts();
+ return ((DiSHPreferenceVectorIndex.Factory<NumberVector<?>>) indexFactory).getMinpts();
}
/**
@@ -82,7 +82,7 @@ public class DiSHDistanceFunction extends AbstractPreferenceVectorBasedCorrelati
*
* @author Erich Schubert
*/
- public static class Instance<V extends NumberVector<?, ?>> extends AbstractPreferenceVectorBasedCorrelationDistanceFunction.Instance<V, DiSHPreferenceVectorIndex<V>> {
+ public static class Instance<V extends NumberVector<?>> extends AbstractPreferenceVectorBasedCorrelationDistanceFunction.Instance<V, DiSHPreferenceVectorIndex<V>> {
/**
* Constructor.
*
@@ -119,15 +119,15 @@ public class DiSHDistanceFunction extends AbstractPreferenceVectorBasedCorrelati
double d = weightedDistance(v1, v2, commonPreferenceVector);
if(d > 2 * epsilon) {
subspaceDim++;
- if(logger.isDebugging()) {
+ if(LOG.isDebugging()) {
//Representation<String> rep = database.getObjectLabelQuery();
- StringBuffer msg = new StringBuffer();
+ StringBuilder msg = new StringBuilder();
msg.append("d ").append(d);
//msg.append("\nv1 ").append(rep.get(v1.getID()));
//msg.append("\nv2 ").append(rep.get(v2.getID()));
msg.append("\nsubspaceDim ").append(subspaceDim);
msg.append("\ncommon pv ").append(FormatUtil.format(dim, commonPreferenceVector));
- logger.debugFine(msg.toString());
+ LOG.debugFine(msg.toString());
}
}
}
@@ -136,7 +136,7 @@ public class DiSHDistanceFunction extends AbstractPreferenceVectorBasedCorrelati
BitSet inverseCommonPreferenceVector = (BitSet) commonPreferenceVector.clone();
inverseCommonPreferenceVector.flip(0, dim);
- return new PreferenceVectorBasedCorrelationDistance(DatabaseUtil.dimensionality(relation), subspaceDim, weightedDistance(v1, v2, inverseCommonPreferenceVector), commonPreferenceVector);
+ return new PreferenceVectorBasedCorrelationDistance(RelationUtil.dimensionality(relation), subspaceDim, weightedDistance(v1, v2, inverseCommonPreferenceVector), commonPreferenceVector);
}
}
@@ -147,11 +147,11 @@ public class DiSHDistanceFunction extends AbstractPreferenceVectorBasedCorrelati
*
* @apiviz.exclude
*/
- public static class Parameterizer extends AbstractPreferenceVectorBasedCorrelationDistanceFunction.Parameterizer<DiSHPreferenceVectorIndex.Factory<NumberVector<?, ?>>> {
+ public static class Parameterizer extends AbstractPreferenceVectorBasedCorrelationDistanceFunction.Parameterizer<DiSHPreferenceVectorIndex.Factory<NumberVector<?>>> {
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- Class<DiSHPreferenceVectorIndex.Factory<NumberVector<?, ?>>> cls = ClassGenericsUtil.uglyCastIntoSubclass(DiSHPreferenceVectorIndex.Factory.class);
+ Class<DiSHPreferenceVectorIndex.Factory<NumberVector<?>>> cls = ClassGenericsUtil.uglyCastIntoSubclass(DiSHPreferenceVectorIndex.Factory.class);
factory = config.tryInstantiate(cls);
}
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/subspace/DimensionSelectingDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/subspace/DimensionSelectingDistanceFunction.java
index 382167e9..e1d52710 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/subspace/DimensionSelectingDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/subspace/DimensionSelectingDistanceFunction.java
@@ -25,6 +25,7 @@ package de.lmu.ifi.dbs.elki.distance.distancefunction.subspace;
import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.data.spatial.SpatialComparable;
+import de.lmu.ifi.dbs.elki.data.type.VectorFieldTypeInformation;
import de.lmu.ifi.dbs.elki.data.type.VectorTypeInformation;
import de.lmu.ifi.dbs.elki.database.query.distance.SpatialPrimitiveDistanceQuery;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
@@ -43,11 +44,11 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
*
* @author Elke Achtert
*/
-public class DimensionSelectingDistanceFunction extends AbstractPrimitiveDistanceFunction<NumberVector<?, ?>, DoubleDistance> implements SpatialPrimitiveDoubleDistanceFunction<NumberVector<?, ?>> {
+public class DimensionSelectingDistanceFunction extends AbstractPrimitiveDistanceFunction<NumberVector<?>, DoubleDistance> implements SpatialPrimitiveDoubleDistanceFunction<NumberVector<?>> {
/**
* Parameter for dimensionality.
*/
- public static final OptionID DIM_ID = OptionID.getOrCreateOptionID("dim", "an integer between 1 and the dimensionality of the " + "feature space 1 specifying the dimension to be considered " + "for distance computation.");
+ public static final OptionID DIM_ID = new OptionID("dim", "an integer between 1 and the dimensionality of the " + "feature space 1 specifying the dimension to be considered " + "for distance computation.");
/**
* The dimension to be considered for distance computation.
@@ -74,8 +75,8 @@ public class DimensionSelectingDistanceFunction extends AbstractPrimitiveDistanc
* distance function
*/
@Override
- public double doubleDistance(NumberVector<?, ?> v1, NumberVector<?, ?> v2) {
- if(dim > v1.getDimensionality() || dim > v2.getDimensionality()) {
+ public double doubleDistance(NumberVector<?> v1, NumberVector<?> v2) {
+ if(dim >= v1.getDimensionality() || dim >= v2.getDimensionality() || dim < 0) {
throw new IllegalArgumentException("Specified dimension to be considered " + "is larger that dimensionality of FeatureVectors:" + "\n first argument: " + v1.toString() + "\n second argument: " + v2.toString() + "\n dimension: " + dim);
}
@@ -85,7 +86,7 @@ public class DimensionSelectingDistanceFunction extends AbstractPrimitiveDistanc
@Override
public double doubleMinDist(SpatialComparable mbr1, SpatialComparable mbr2) {
- if(dim > mbr1.getDimensionality() || dim > mbr2.getDimensionality()) {
+ if(dim >= mbr1.getDimensionality() || dim >= mbr2.getDimensionality() || dim < 0) {
throw new IllegalArgumentException("Specified dimension to be considered " + "is larger that dimensionality of FeatureVectors:" + "\n first argument: " + mbr1.toString() + "\n second argument: " + mbr2.toString() + "\n dimension: " + dim);
}
@@ -108,7 +109,7 @@ public class DimensionSelectingDistanceFunction extends AbstractPrimitiveDistanc
}
@Override
- public DoubleDistance distance(NumberVector<?, ?> o1, NumberVector<?, ?> o2) {
+ public DoubleDistance distance(NumberVector<?> o1, NumberVector<?> o2) {
return new DoubleDistance(doubleDistance(o1, o2));
}
@@ -127,8 +128,8 @@ public class DimensionSelectingDistanceFunction extends AbstractPrimitiveDistanc
}
@Override
- public VectorTypeInformation<? super NumberVector<?, ?>> getInputTypeRestriction() {
- return VectorTypeInformation.get(NumberVector.class, dim, Integer.MAX_VALUE);
+ public VectorTypeInformation<? super NumberVector<?>> getInputTypeRestriction() {
+ return new VectorFieldTypeInformation<NumberVector<?>>(NumberVector.class, dim, Integer.MAX_VALUE);
}
@Override
@@ -137,7 +138,7 @@ public class DimensionSelectingDistanceFunction extends AbstractPrimitiveDistanc
}
@Override
- public <T extends NumberVector<?, ?>> SpatialPrimitiveDistanceQuery<T, DoubleDistance> instantiate(Relation<T> database) {
+ public <T extends NumberVector<?>> SpatialPrimitiveDistanceQuery<T, DoubleDistance> instantiate(Relation<T> database) {
return new SpatialPrimitiveDistanceQuery<T, DoubleDistance>(database, this);
}
@@ -165,7 +166,8 @@ public class DimensionSelectingDistanceFunction extends AbstractPrimitiveDistanc
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- final IntParameter dimP = new IntParameter(DIM_ID, new GreaterEqualConstraint(1));
+ final IntParameter dimP = new IntParameter(DIM_ID);
+ dimP.addConstraint(new GreaterEqualConstraint(0));
if(config.grab(dimP)) {
dim = dimP.getValue();
}
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/subspace/HiSCDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/subspace/HiSCDistanceFunction.java
index 4835a6c6..0703ab2d 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/subspace/HiSCDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/subspace/HiSCDistanceFunction.java
@@ -27,11 +27,11 @@ import java.util.BitSet;
import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.distance.distancevalue.PreferenceVectorBasedCorrelationDistance;
import de.lmu.ifi.dbs.elki.index.preprocessed.preference.HiSCPreferenceVectorIndex;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.utilities.ClassGenericsUtil;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.utilities.FormatUtil;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
@@ -44,17 +44,17 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameteriz
*
* @param <V> the type of NumberVector to compute the distances in between
*/
-public class HiSCDistanceFunction<V extends NumberVector<?, ?>> extends AbstractPreferenceVectorBasedCorrelationDistanceFunction<V, HiSCPreferenceVectorIndex<V>> {
+public class HiSCDistanceFunction<V extends NumberVector<?>> extends AbstractPreferenceVectorBasedCorrelationDistanceFunction<V, HiSCPreferenceVectorIndex<V>> {
/**
* Logger for debug.
*/
- static Logging logger = Logging.getLogger(DiSHDistanceFunction.class);
+ private static final Logging LOG = Logging.getLogger(HiSCDistanceFunction.class);
/**
* Constructor.
*
- * @param indexFactory
- * @param epsilon
+ * @param indexFactory HiSC index factory
+ * @param epsilon Epsilon value
*/
public HiSCDistanceFunction(HiSCPreferenceVectorIndex.Factory<V> indexFactory, double epsilon) {
super(indexFactory, epsilon);
@@ -76,7 +76,7 @@ public class HiSCDistanceFunction<V extends NumberVector<?, ?>> extends Abstract
*
* @param <V> the type of NumberVector to compute the distances in between
*/
- public static class Instance<V extends NumberVector<?, ?>> extends AbstractPreferenceVectorBasedCorrelationDistanceFunction.Instance<V, HiSCPreferenceVectorIndex<V>> {
+ public static class Instance<V extends NumberVector<?>> extends AbstractPreferenceVectorBasedCorrelationDistanceFunction.Instance<V, HiSCPreferenceVectorIndex<V>> {
/**
* Constructor.
*
@@ -114,16 +114,16 @@ public class HiSCDistanceFunction<V extends NumberVector<?, ?>> extends Abstract
if(Math.max(dist1, dist2) > epsilon) {
subspaceDim++;
- if(logger.isDebugging()) {
+ if(LOG.isDebugging()) {
//Representation<String> rep = rep.getObjectLabelQuery();
- StringBuffer msg = new StringBuffer();
- msg.append("\ndist1 " + dist1);
- msg.append("\ndist2 " + dist2);
- // msg.append("\nv1 " + rep.get(v1.getID()));
- // msg.append("\nv2 " + rep.get(v2.getID()));
- msg.append("\nsubspaceDim " + subspaceDim);
- msg.append("\ncommon pv " + FormatUtil.format(dim, commonPreferenceVector));
- logger.debugFine(msg.toString());
+ StringBuilder msg = new StringBuilder();
+ msg.append("\ndist1 ").append(dist1);
+ msg.append("\ndist2 ").append(dist2);
+ // msg.append("\nv1 ").append(rep.get(v1.getID()));
+ // msg.append("\nv2 ").append(rep.get(v2.getID()));
+ msg.append("\nsubspaceDim ").append(subspaceDim);
+ msg.append("\ncommon pv ").append(FormatUtil.format(dim, commonPreferenceVector));
+ LOG.debugFine(msg.toString());
}
}
@@ -131,7 +131,7 @@ public class HiSCDistanceFunction<V extends NumberVector<?, ?>> extends Abstract
BitSet inverseCommonPreferenceVector = (BitSet) commonPreferenceVector.clone();
inverseCommonPreferenceVector.flip(0, dim);
- return new PreferenceVectorBasedCorrelationDistance(DatabaseUtil.dimensionality(relation), subspaceDim, weightedDistance(v1, v2, inverseCommonPreferenceVector), commonPreferenceVector);
+ return new PreferenceVectorBasedCorrelationDistance(RelationUtil.dimensionality(relation), subspaceDim, weightedDistance(v1, v2, inverseCommonPreferenceVector), commonPreferenceVector);
}
}
@@ -142,7 +142,7 @@ public class HiSCDistanceFunction<V extends NumberVector<?, ?>> extends Abstract
*
* @apiviz.exclude
*/
- public static class Parameterizer<V extends NumberVector<?, ?>> extends AbstractPreferenceVectorBasedCorrelationDistanceFunction.Parameterizer<HiSCPreferenceVectorIndex.Factory<V>> {
+ public static class Parameterizer<V extends NumberVector<?>> extends AbstractPreferenceVectorBasedCorrelationDistanceFunction.Parameterizer<HiSCPreferenceVectorIndex.Factory<V>> {
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/subspace/LocalSubspaceDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/subspace/LocalSubspaceDistanceFunction.java
index 8bb912e0..ff38d3d9 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/subspace/LocalSubspaceDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/subspace/LocalSubspaceDistanceFunction.java
@@ -48,13 +48,13 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameteriz
*
* @apiviz.has Instance
*/
-public class LocalSubspaceDistanceFunction extends AbstractIndexBasedDistanceFunction<NumberVector<?, ?>, FilteredLocalPCAIndex<NumberVector<?, ?>>, SubspaceDistance> implements FilteredLocalPCABasedDistanceFunction<NumberVector<?, ?>, FilteredLocalPCAIndex<NumberVector<?, ?>>, SubspaceDistance> {
+public class LocalSubspaceDistanceFunction extends AbstractIndexBasedDistanceFunction<NumberVector<?>, FilteredLocalPCAIndex<NumberVector<?>>, SubspaceDistance> implements FilteredLocalPCABasedDistanceFunction<NumberVector<?>, FilteredLocalPCAIndex<NumberVector<?>>, SubspaceDistance> {
/**
* Constructor
*
* @param indexFactory Index factory
*/
- public LocalSubspaceDistanceFunction(IndexFactory<NumberVector<?, ?>, FilteredLocalPCAIndex<NumberVector<?, ?>>> indexFactory) {
+ public LocalSubspaceDistanceFunction(IndexFactory<NumberVector<?>, FilteredLocalPCAIndex<NumberVector<?>>> indexFactory) {
super(indexFactory);
}
@@ -64,10 +64,10 @@ public class LocalSubspaceDistanceFunction extends AbstractIndexBasedDistanceFun
}
@Override
- public <V extends NumberVector<?, ?>> Instance<V> instantiate(Relation<V> database) {
+ public <V extends NumberVector<?>> Instance<V> instantiate(Relation<V> database) {
// We can't really avoid these warnings, due to a limitation in Java Generics (AFAICT)
@SuppressWarnings("unchecked")
- FilteredLocalPCAIndex<V> indexinst = (FilteredLocalPCAIndex<V>) indexFactory.instantiate((Relation<NumberVector<?, ?>>)database);
+ FilteredLocalPCAIndex<V> indexinst = (FilteredLocalPCAIndex<V>) indexFactory.instantiate((Relation<NumberVector<?>>)database);
return new Instance<V>(database, indexinst, this);
}
@@ -76,7 +76,7 @@ public class LocalSubspaceDistanceFunction extends AbstractIndexBasedDistanceFun
*
* @author Erich Schubert
*/
- public static class Instance<V extends NumberVector<?, ?>> extends AbstractIndexBasedDistanceFunction.Instance<V, FilteredLocalPCAIndex<V>, SubspaceDistance, LocalSubspaceDistanceFunction> implements FilteredLocalPCABasedDistanceFunction.Instance<V, FilteredLocalPCAIndex<V>, SubspaceDistance> {
+ public static class Instance<V extends NumberVector<?>> extends AbstractIndexBasedDistanceFunction.Instance<V, FilteredLocalPCAIndex<V>, SubspaceDistance, LocalSubspaceDistanceFunction> implements FilteredLocalPCABasedDistanceFunction.Instance<V, FilteredLocalPCAIndex<V>, SubspaceDistance> {
/**
* @param database Database
* @param index Index
@@ -137,7 +137,7 @@ public class LocalSubspaceDistanceFunction extends AbstractIndexBasedDistanceFun
*
* @apiviz.exclude
*/
- public static class Parameterizer extends AbstractIndexBasedDistanceFunction.Parameterizer<LocalProjectionIndex.Factory<NumberVector<?, ?>, FilteredLocalPCAIndex<NumberVector<?, ?>>>> {
+ public static class Parameterizer extends AbstractIndexBasedDistanceFunction.Parameterizer<LocalProjectionIndex.Factory<NumberVector<?>, FilteredLocalPCAIndex<NumberVector<?>>>> {
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/subspace/SubspaceEuclideanDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/subspace/SubspaceEuclideanDistanceFunction.java
index 6330ed34..48518afd 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/subspace/SubspaceEuclideanDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/subspace/SubspaceEuclideanDistanceFunction.java
@@ -54,21 +54,21 @@ public class SubspaceEuclideanDistanceFunction extends SubspaceLPNormDistanceFun
* selected dimensions
*/
@Override
- public double doubleDistance(NumberVector<?, ?> v1, NumberVector<?, ?> v2) {
+ public double doubleDistance(NumberVector<?> v1, NumberVector<?> v2) {
if(v1.getDimensionality() != v2.getDimensionality()) {
throw new IllegalArgumentException("Different dimensionality of FeatureVectors\n " + "first argument: " + v1 + "\n " + "second argument: " + v2);
}
double sqrDist = 0;
for(int d = dimensions.nextSetBit(0); d >= 0; d = dimensions.nextSetBit(d + 1)) {
- final double delta = v1.doubleValue(d + 1) - v2.doubleValue(d + 1);
+ final double delta = v1.doubleValue(d) - v2.doubleValue(d);
sqrDist += delta * delta;
}
return Math.sqrt(sqrDist);
}
@Override
- protected double doubleMinDistObject(SpatialComparable mbr, NumberVector<?, ?> v) {
+ protected double doubleMinDistObject(SpatialComparable mbr, NumberVector<?> v) {
if(mbr.getDimensionality() != v.getDimensionality()) {
throw new IllegalArgumentException("Different dimensionality of objects\n " + "first argument: " + mbr.toString() + "\n " + "second argument: " + v.toString());
}
@@ -76,13 +76,13 @@ public class SubspaceEuclideanDistanceFunction extends SubspaceLPNormDistanceFun
double sqrDist = 0;
for(int d = dimensions.nextSetBit(0); d >= 0; d = dimensions.nextSetBit(d + 1)) {
final double delta;
- final double value = v.doubleValue(d + 1);
- final double omin = mbr.getMin(d + 1);
+ final double value = v.doubleValue(d);
+ final double omin = mbr.getMin(d);
if(value < omin) {
delta = omin - value;
}
else {
- final double omax = mbr.getMax(d + 1);
+ final double omax = mbr.getMax(d);
if(value > omax) {
delta = value - omax;
}
@@ -103,14 +103,14 @@ public class SubspaceEuclideanDistanceFunction extends SubspaceLPNormDistanceFun
double sqrDist = 0;
for(int d = dimensions.nextSetBit(0); d >= 0; d = dimensions.nextSetBit(d + 1)) {
final double delta;
- final double max1 = mbr1.getMax(d + 1);
- final double min2 = mbr2.getMin(d + 1);
+ final double max1 = mbr1.getMax(d);
+ final double min2 = mbr2.getMin(d);
if(max1 < min2) {
delta = min2 - max1;
}
else {
- final double min1 = mbr1.getMin(d + 1);
- final double max2 = mbr2.getMax(d + 1);
+ final double min1 = mbr1.getMin(d);
+ final double max2 = mbr2.getMax(d);
if(min1 > max2) {
delta = min1 - max2;
}
@@ -124,10 +124,10 @@ public class SubspaceEuclideanDistanceFunction extends SubspaceLPNormDistanceFun
}
@Override
- public double doubleNorm(NumberVector<?, ?> obj) {
+ public double doubleNorm(NumberVector<?> obj) {
double sqrDist = 0;
for(int d = dimensions.nextSetBit(0); d >= 0; d = dimensions.nextSetBit(d + 1)) {
- final double delta = obj.doubleValue(d + 1);
+ final double delta = obj.doubleValue(d);
sqrDist += delta * delta;
}
return Math.sqrt(sqrDist);
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/subspace/SubspaceLPNormDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/subspace/SubspaceLPNormDistanceFunction.java
index 7904c333..ccfc51d9 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/subspace/SubspaceLPNormDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/subspace/SubspaceLPNormDistanceFunction.java
@@ -45,7 +45,7 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.DoubleParameter;
*
* @author Elke Achtert
*/
-public class SubspaceLPNormDistanceFunction extends AbstractDimensionsSelectingDoubleDistanceFunction<NumberVector<?, ?>> implements SpatialPrimitiveDoubleDistanceFunction<NumberVector<?, ?>>, DoubleNorm<NumberVector<?, ?>> {
+public class SubspaceLPNormDistanceFunction extends AbstractDimensionsSelectingDoubleDistanceFunction<NumberVector<?>> implements SpatialPrimitiveDoubleDistanceFunction<NumberVector<?>>, DoubleNorm<NumberVector<?>> {
/**
* Value of p
*/
@@ -81,20 +81,20 @@ public class SubspaceLPNormDistanceFunction extends AbstractDimensionsSelectingD
* selected dimensions
*/
@Override
- public double doubleDistance(NumberVector<?, ?> v1, NumberVector<?, ?> v2) {
+ public double doubleDistance(NumberVector<?> v1, NumberVector<?> v2) {
if(v1.getDimensionality() != v2.getDimensionality()) {
throw new IllegalArgumentException("Different dimensionality of FeatureVectors\n " + "first argument: " + v1 + "\n " + "second argument: " + v2);
}
double sqrDist = 0;
for(int d = dimensions.nextSetBit(0); d >= 0; d = dimensions.nextSetBit(d + 1)) {
- double delta = Math.abs(v1.doubleValue(d + 1) - v2.doubleValue(d + 1));
+ double delta = Math.abs(v1.doubleValue(d) - v2.doubleValue(d));
sqrDist += Math.pow(delta, p);
}
return Math.pow(sqrDist, 1. / p);
}
- protected double doubleMinDistObject(SpatialComparable mbr, NumberVector<?, ?> v) {
+ protected double doubleMinDistObject(SpatialComparable mbr, NumberVector<?> v) {
if(mbr.getDimensionality() != v.getDimensionality()) {
throw new IllegalArgumentException("Different dimensionality of objects\n " + "first argument: " + mbr.toString() + "\n " + "second argument: " + v.toString());
}
@@ -102,13 +102,13 @@ public class SubspaceLPNormDistanceFunction extends AbstractDimensionsSelectingD
double sqrDist = 0;
for(int d = dimensions.nextSetBit(0); d >= 0; d = dimensions.nextSetBit(d + 1)) {
final double delta;
- final double value = v.doubleValue(d + 1);
- final double omin = mbr.getMin(d + 1);
+ final double value = v.doubleValue(d);
+ final double omin = mbr.getMin(d);
if(value < omin) {
delta = omin - value;
}
else {
- final double omax = mbr.getMax(d + 1);
+ final double omax = mbr.getMax(d);
if(value > omax) {
delta = value - omax;
}
@@ -129,14 +129,14 @@ public class SubspaceLPNormDistanceFunction extends AbstractDimensionsSelectingD
double sqrDist = 0;
for(int d = dimensions.nextSetBit(0); d >= 0; d = dimensions.nextSetBit(d + 1)) {
final double delta;
- final double max1 = mbr1.getMax(d + 1);
- final double min2 = mbr2.getMin(d + 1);
+ final double max1 = mbr1.getMax(d);
+ final double min2 = mbr2.getMin(d);
if(max1 < min2) {
delta = min2 - max1;
}
else {
- final double min1 = mbr1.getMin(d + 1);
- final double max2 = mbr2.getMax(d + 1);
+ final double min1 = mbr1.getMin(d);
+ final double max2 = mbr2.getMax(d);
if(min1 > max2) {
delta = min1 - max2;
}
@@ -155,27 +155,27 @@ public class SubspaceLPNormDistanceFunction extends AbstractDimensionsSelectingD
}
@Override
- public DoubleDistance norm(NumberVector<?, ?> obj) {
+ public DoubleDistance norm(NumberVector<?> obj) {
return new DoubleDistance(doubleNorm(obj));
}
@Override
- public double doubleNorm(NumberVector<?, ?> obj) {
+ public double doubleNorm(NumberVector<?> obj) {
double sqrDist = 0;
for(int d = dimensions.nextSetBit(0); d >= 0; d = dimensions.nextSetBit(d + 1)) {
- double delta = Math.abs(obj.doubleValue(d + 1));
+ double delta = Math.abs(obj.doubleValue(d));
sqrDist += Math.pow(delta, p);
}
return Math.pow(sqrDist, 1. / p);
}
@Override
- public <T extends NumberVector<?, ?>> SpatialPrimitiveDistanceQuery<T, DoubleDistance> instantiate(Relation<T> database) {
+ public <T extends NumberVector<?>> SpatialPrimitiveDistanceQuery<T, DoubleDistance> instantiate(Relation<T> database) {
return new SpatialPrimitiveDistanceQuery<T, DoubleDistance>(database, this);
}
@Override
- public VectorFieldTypeInformation<? super NumberVector<?, ?>> getInputTypeRestriction() {
+ public VectorFieldTypeInformation<? super NumberVector<?>> getInputTypeRestriction() {
return TypeUtil.NUMBER_VECTOR_FIELD;
}
@@ -193,13 +193,14 @@ public class SubspaceLPNormDistanceFunction extends AbstractDimensionsSelectingD
*/
public static class Parameterizer extends AbstractDimensionsSelectingDoubleDistanceFunction.Parameterizer {
/**
- * Value of p
+ * Value of p.
*/
private double p;
@Override
protected void makeOptions(Parameterization config) {
- final DoubleParameter paramP = new DoubleParameter(LPNormDistanceFunction.P_ID, new GreaterConstraint(0));
+ final DoubleParameter paramP = new DoubleParameter(LPNormDistanceFunction.P_ID);
+ paramP.addConstraint(new GreaterConstraint(0));
if(config.grab(paramP)) {
p = paramP.getValue();
}
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/subspace/SubspaceManhattanDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/subspace/SubspaceManhattanDistanceFunction.java
index 905ee037..fb777d81 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/subspace/SubspaceManhattanDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/subspace/SubspaceManhattanDistanceFunction.java
@@ -54,32 +54,33 @@ public class SubspaceManhattanDistanceFunction extends SubspaceLPNormDistanceFun
* selected dimensions
*/
@Override
- public double doubleDistance(NumberVector<?, ?> v1, NumberVector<?, ?> v2) {
+ public double doubleDistance(NumberVector<?> v1, NumberVector<?> v2) {
if(v1.getDimensionality() != v2.getDimensionality()) {
throw new IllegalArgumentException("Different dimensionality of FeatureVectors\n " + "first argument: " + v1 + "\n " + "second argument: " + v2);
}
double sum = 0;
for(int d = dimensions.nextSetBit(0); d >= 0; d = dimensions.nextSetBit(d + 1)) {
- sum += Math.abs(v1.doubleValue(d + 1) - v2.doubleValue(d + 1));
+ sum += Math.abs(v1.doubleValue(d) - v2.doubleValue(d));
}
return sum;
}
- protected double doubleMinDistObject(SpatialComparable mbr, NumberVector<?, ?> v) {
+ @Override
+ protected double doubleMinDistObject(SpatialComparable mbr, NumberVector<?> v) {
if(mbr.getDimensionality() != v.getDimensionality()) {
throw new IllegalArgumentException("Different dimensionality of objects\n " + "first argument: " + mbr.toString() + "\n " + "second argument: " + v.toString());
}
double sum = 0;
for(int d = dimensions.nextSetBit(0); d >= 0; d = dimensions.nextSetBit(d + 1)) {
- final double value = v.doubleValue(d + 1);
- final double omin = mbr.getMin(d + 1);
+ final double value = v.doubleValue(d);
+ final double omin = mbr.getMin(d);
if(value < omin) {
sum += omin - value;
}
else {
- final double omax = mbr.getMax(d + 1);
+ final double omax = mbr.getMax(d);
if(value > omax) {
sum += value - omax;
}
@@ -98,14 +99,14 @@ public class SubspaceManhattanDistanceFunction extends SubspaceLPNormDistanceFun
}
double sum = 0;
for(int d = dimensions.nextSetBit(0); d >= 0; d = dimensions.nextSetBit(d + 1)) {
- final double max1 = mbr1.getMax(d + 1);
- final double min2 = mbr2.getMin(d + 1);
+ final double max1 = mbr1.getMax(d);
+ final double min2 = mbr2.getMin(d);
if(max1 < min2) {
sum += min2 - max1;
}
else {
- final double min1 = mbr1.getMin(d + 1);
- final double max2 = mbr2.getMax(d + 1);
+ final double min1 = mbr1.getMin(d);
+ final double max2 = mbr2.getMax(d);
if(min1 > max2) {
sum += min1 - max2;
}
@@ -118,10 +119,10 @@ public class SubspaceManhattanDistanceFunction extends SubspaceLPNormDistanceFun
}
@Override
- public double doubleNorm(NumberVector<?, ?> obj) {
+ public double doubleNorm(NumberVector<?> obj) {
double sum = 0;
for(int d = dimensions.nextSetBit(0); d >= 0; d = dimensions.nextSetBit(d + 1)) {
- sum += Math.abs(obj.doubleValue(d + 1));
+ sum += Math.abs(obj.doubleValue(d));
}
return sum;
}
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/timeseries/AbstractEditDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/timeseries/AbstractEditDistanceFunction.java
index a504f680..a88dbded 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/timeseries/AbstractEditDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/timeseries/AbstractEditDistanceFunction.java
@@ -29,8 +29,8 @@ import de.lmu.ifi.dbs.elki.data.type.VectorFieldTypeInformation;
import de.lmu.ifi.dbs.elki.distance.distancefunction.AbstractVectorDoubleDistanceFunction;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.IntervalConstraint;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.IntervalConstraint.IntervalBoundary;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterEqualConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.LessEqualConstraint;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.DoubleParameter;
@@ -43,7 +43,7 @@ public abstract class AbstractEditDistanceFunction extends AbstractVectorDoubleD
/**
* BANDSIZE parameter
*/
- public static final OptionID BANDSIZE_ID = OptionID.getOrCreateOptionID("edit.bandSize", "the band size for Edit Distance alignment (positive double value, 0 <= bandSize <= 1)");
+ public static final OptionID BANDSIZE_ID = new OptionID("edit.bandSize", "the band size for Edit Distance alignment (positive double value, 0 <= bandSize <= 1)");
/**
* Keeps the currently set bandSize.
@@ -62,13 +62,13 @@ public abstract class AbstractEditDistanceFunction extends AbstractVectorDoubleD
// TODO: relax this to VectorTypeInformation!
@Override
- public VectorFieldTypeInformation<? super NumberVector<?, ?>> getInputTypeRestriction() {
+ public VectorFieldTypeInformation<? super NumberVector<?>> getInputTypeRestriction() {
return TypeUtil.NUMBER_VECTOR_FIELD;
}
@Override
public boolean equals(Object obj) {
- if(obj == null) {
+ if (obj == null) {
return false;
}
if (!this.getClass().equals(obj.getClass())) {
@@ -76,7 +76,7 @@ public abstract class AbstractEditDistanceFunction extends AbstractVectorDoubleD
}
return this.bandSize == ((AbstractEditDistanceFunction) obj).bandSize;
}
-
+
/**
* Parameterization class.
*
@@ -84,16 +84,18 @@ public abstract class AbstractEditDistanceFunction extends AbstractVectorDoubleD
*
* @apiviz.exclude
*/
- public static abstract class Parameterizer extends AbstractParameterizer {
+ public abstract static class Parameterizer extends AbstractParameterizer {
protected double bandSize = 0.0;
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- final DoubleParameter bandSizeP = new DoubleParameter(BANDSIZE_ID, new IntervalConstraint(0, IntervalBoundary.CLOSE, 1, IntervalBoundary.CLOSE), 0.1);
- if(config.grab(bandSizeP)) {
- bandSize = bandSizeP.getValue();
+ final DoubleParameter bandSizeP = new DoubleParameter(BANDSIZE_ID, 0.1);
+ bandSizeP.addConstraint(new GreaterEqualConstraint(0));
+ bandSizeP.addConstraint(new LessEqualConstraint(1));
+ if (config.grab(bandSizeP)) {
+ bandSize = bandSizeP.doubleValue();
}
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/timeseries/DTWDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/timeseries/DTWDistanceFunction.java
index 4461a680..d3fab3c0 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/timeseries/DTWDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/timeseries/DTWDistanceFunction.java
@@ -52,7 +52,7 @@ public class DTWDistanceFunction extends AbstractEditDistanceFunction {
* an instance of {@link DoubleDistance DoubleDistance}.
*/
@Override
- public double doubleDistance(NumberVector<?, ?> v1, NumberVector<?, ?> v2) {
+ public double doubleDistance(NumberVector<?> v1, NumberVector<?> v2) {
// Current and previous columns of the matrix
double[] curr = new double[v2.getDimensionality()];
double[] prev = new double[v2.getDimensionality()];
@@ -82,8 +82,8 @@ public class DTWDistanceFunction extends AbstractEditDistanceFunction {
for(int j = l; j <= r; j++) {
if(Math.abs(i - j) <= band) {
- double val1 = v1.doubleValue(i + 1);
- double val2 = v2.doubleValue(j + 1);
+ double val1 = v1.doubleValue(i);
+ double val2 = v2.doubleValue(j);
double diff = (val1 - val2);
// Formally: diff = Math.sqrt(diff * diff);
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/timeseries/EDRDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/timeseries/EDRDistanceFunction.java
index c918ab03..4c177b03 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/timeseries/EDRDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/timeseries/EDRDistanceFunction.java
@@ -43,7 +43,7 @@ public class EDRDistanceFunction extends AbstractEditDistanceFunction {
/**
* DELTA parameter
*/
- public static final OptionID DELTA_ID = OptionID.getOrCreateOptionID("edr.delta", "the delta parameter (similarity threshold) for EDR (positive number)");
+ public static final OptionID DELTA_ID = new OptionID("edr.delta", "the delta parameter (similarity threshold) for EDR (positive number)");
/**
* Keeps the currently set delta.
@@ -69,7 +69,7 @@ public class EDRDistanceFunction extends AbstractEditDistanceFunction {
* vectors as an instance of {@link DoubleDistance DoubleDistance}.
*/
@Override
- public double doubleDistance(NumberVector<?, ?> v1, NumberVector<?, ?> v2) {
+ public double doubleDistance(NumberVector<?> v1, NumberVector<?> v2) {
// Current and previous columns of the matrix
double[] curr = new double[v2.getDimensionality()];
double[] prev = new double[v2.getDimensionality()];
@@ -82,7 +82,7 @@ public class EDRDistanceFunction extends AbstractEditDistanceFunction {
// features2.length + ", band: " + band);
final double deltaValue = delta;
- for(int i = 0; i < v1.getDimensionality(); i++) {
+ for (int i = 0; i < v1.getDimensionality(); i++) {
// Swap current and prev arrays. We'll just overwrite the new curr.
{
double[] temp = prev;
@@ -90,19 +90,19 @@ public class EDRDistanceFunction extends AbstractEditDistanceFunction {
curr = temp;
}
int l = i - (band + 1);
- if(l < 0) {
+ if (l < 0) {
l = 0;
}
int r = i + (band + 1);
- if(r > (v2.getDimensionality() - 1)) {
+ if (r > (v2.getDimensionality() - 1)) {
r = (v2.getDimensionality() - 1);
}
- for(int j = l; j <= r; j++) {
- if(Math.abs(i - j) <= band) {
+ for (int j = l; j <= r; j++) {
+ if (Math.abs(i - j) <= band) {
// compute squared distance
- double val1 = v1.doubleValue(i + 1);
- double val2 = v2.doubleValue(j + 1);
+ double val1 = v1.doubleValue(i);
+ double val2 = v2.doubleValue(j);
double diff = (val1 - val2);
final double d = Math.sqrt(diff * diff);
@@ -110,27 +110,23 @@ public class EDRDistanceFunction extends AbstractEditDistanceFunction {
final double subcost = (d <= deltaValue) ? 0 : 1;
- if((i + j) != 0) {
- if((i == 0) || ((j != 0) && (((prev[j - 1] + subcost) > (curr[j - 1] + 1)) && ((curr[j - 1] + 1) < (prev[j] + 1))))) {
+ if ((i + j) != 0) {
+ if ((i == 0) || ((j != 0) && (((prev[j - 1] + subcost) > (curr[j - 1] + 1)) && ((curr[j - 1] + 1) < (prev[j] + 1))))) {
// del
cost = curr[j - 1] + 1;
- }
- else if((j == 0) || ((i != 0) && (((prev[j - 1] + subcost) > (prev[j] + 1)) && ((prev[j] + 1) < (curr[j - 1] + 1))))) {
+ } else if ((j == 0) || ((i != 0) && (((prev[j - 1] + subcost) > (prev[j] + 1)) && ((prev[j] + 1) < (curr[j - 1] + 1))))) {
// ins
cost = prev[j] + 1;
- }
- else {
+ } else {
// match
cost = prev[j - 1] + subcost;
}
- }
- else {
+ } else {
cost = 0;
}
curr[j] = cost;
- }
- else {
+ } else {
curr[j] = Double.POSITIVE_INFINITY; // outside band
}
}
@@ -141,7 +137,7 @@ public class EDRDistanceFunction extends AbstractEditDistanceFunction {
@Override
public boolean equals(Object obj) {
- if(!super.equals(obj)) {
+ if (!super.equals(obj)) {
return false;
}
return this.delta == ((EDRDistanceFunction) obj).delta;
@@ -160,9 +156,10 @@ public class EDRDistanceFunction extends AbstractEditDistanceFunction {
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- final DoubleParameter deltaP = new DoubleParameter(DELTA_ID, new GreaterEqualConstraint(0), 1.0);
- if(config.grab(deltaP)) {
- delta = deltaP.getValue();
+ final DoubleParameter deltaP = new DoubleParameter(DELTA_ID, 1.0);
+ deltaP.addConstraint(new GreaterEqualConstraint(0));
+ if (config.grab(deltaP)) {
+ delta = deltaP.doubleValue();
}
}
@@ -181,4 +178,4 @@ public class EDRDistanceFunction extends AbstractEditDistanceFunction {
return new EDRDistanceFunction(bandSize, delta);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/timeseries/ERPDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/timeseries/ERPDistanceFunction.java
index 40ed00ba..a4525a6b 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/timeseries/ERPDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/timeseries/ERPDistanceFunction.java
@@ -43,7 +43,7 @@ public class ERPDistanceFunction extends AbstractEditDistanceFunction {
/**
* G parameter
*/
- public static final OptionID G_ID = OptionID.getOrCreateOptionID("erp.g", "the g parameter ERP (positive number)");
+ public static final OptionID G_ID = new OptionID("erp.g", "the g parameter ERP (positive number)");
/**
* Keeps the currently set g.
@@ -69,7 +69,7 @@ public class ERPDistanceFunction extends AbstractEditDistanceFunction {
* vectors as an instance of {@link DoubleDistance DoubleDistance}.
*/
@Override
- public double doubleDistance(NumberVector<?, ?> v1, NumberVector<?, ?> v2) {
+ public double doubleDistance(NumberVector<?> v1, NumberVector<?> v2) {
// Current and previous columns of the matrix
double[] curr = new double[v2.getDimensionality()];
double[] prev = new double[v2.getDimensionality()];
@@ -78,7 +78,7 @@ public class ERPDistanceFunction extends AbstractEditDistanceFunction {
// bandsize is the maximum allowed distance to the diagonal
final int band = (int) Math.ceil(v2.getDimensionality() * bandSize);
- for(int i = 0; i < v1.getDimensionality(); i++) {
+ for (int i = 0; i < v1.getDimensionality(); i++) {
// Swap current and prev arrays. We'll just overwrite the new curr.
{
double[] temp = prev;
@@ -86,29 +86,29 @@ public class ERPDistanceFunction extends AbstractEditDistanceFunction {
curr = temp;
}
int l = i - (band + 1);
- if(l < 0) {
+ if (l < 0) {
l = 0;
}
int r = i + (band + 1);
- if(r > (v2.getDimensionality() - 1)) {
+ if (r > (v2.getDimensionality() - 1)) {
r = (v2.getDimensionality() - 1);
}
- for(int j = l; j <= r; j++) {
- if(Math.abs(i - j) <= band) {
+ for (int j = l; j <= r; j++) {
+ if (Math.abs(i - j) <= band) {
// compute squared distance of feature vectors
- double val1 = v1.doubleValue(i + 1);
+ double val1 = v1.doubleValue(i);
double val2 = g;
double diff = (val1 - val2);
final double d1 = Math.sqrt(diff * diff);
val1 = g;
- val2 = v2.doubleValue(j + 1);
+ val2 = v2.doubleValue(j);
diff = (val1 - val2);
final double d2 = Math.sqrt(diff * diff);
- val1 = v1.doubleValue(i + 1);
- val2 = v2.doubleValue(j + 1);
+ val1 = v1.doubleValue(i);
+ val2 = v2.doubleValue(j);
diff = (val1 - val2);
final double d12 = Math.sqrt(diff * diff);
@@ -118,28 +118,24 @@ public class ERPDistanceFunction extends AbstractEditDistanceFunction {
final double cost;
- if((i + j) != 0) {
- if((i == 0) || ((j != 0) && (((prev[j - 1] + dist12) > (curr[j - 1] + dist2)) && ((curr[j - 1] + dist2) < (prev[j] + dist1))))) {
+ if ((i + j) != 0) {
+ if ((i == 0) || ((j != 0) && (((prev[j - 1] + dist12) > (curr[j - 1] + dist2)) && ((curr[j - 1] + dist2) < (prev[j] + dist1))))) {
// del
cost = curr[j - 1] + dist2;
- }
- else if((j == 0) || ((i != 0) && (((prev[j - 1] + dist12) > (prev[j] + dist1)) && ((prev[j] + dist1) < (curr[j - 1] + dist2))))) {
+ } else if ((j == 0) || ((i != 0) && (((prev[j - 1] + dist12) > (prev[j] + dist1)) && ((prev[j] + dist1) < (curr[j - 1] + dist2))))) {
// ins
cost = prev[j] + dist1;
- }
- else {
+ } else {
// match
cost = prev[j - 1] + dist12;
}
- }
- else {
+ } else {
cost = 0;
}
curr[j] = cost;
// steps[i][j] = step;
- }
- else {
+ } else {
curr[j] = Double.POSITIVE_INFINITY; // outside band
}
}
@@ -150,12 +146,12 @@ public class ERPDistanceFunction extends AbstractEditDistanceFunction {
@Override
public boolean equals(Object obj) {
- if(!super.equals(obj)) {
+ if (!super.equals(obj)) {
return false;
}
return this.g == ((ERPDistanceFunction) obj).g;
}
-
+
/**
* Parameterization class.
*
@@ -169,9 +165,10 @@ public class ERPDistanceFunction extends AbstractEditDistanceFunction {
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- final DoubleParameter gP = new DoubleParameter(G_ID, new GreaterEqualConstraint(0), 0.0);
- if(config.grab(gP)) {
- g = gP.getValue();
+ final DoubleParameter gP = new DoubleParameter(G_ID, 0.0);
+ gP.addConstraint(new GreaterEqualConstraint(0));
+ if (config.grab(gP)) {
+ g = gP.doubleValue();
}
}
@@ -180,4 +177,4 @@ public class ERPDistanceFunction extends AbstractEditDistanceFunction {
return new ERPDistanceFunction(bandSize, g);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/timeseries/LCSSDistanceFunction.java b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/timeseries/LCSSDistanceFunction.java
index b9a26c8b..192f8d6f 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancefunction/timeseries/LCSSDistanceFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancefunction/timeseries/LCSSDistanceFunction.java
@@ -34,8 +34,8 @@ import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
import de.lmu.ifi.dbs.elki.utilities.documentation.Title;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.IntervalConstraint;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.IntervalConstraint.IntervalBoundary;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterEqualConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.LessEqualConstraint;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.DoubleParameter;
@@ -80,12 +80,12 @@ public class LCSSDistanceFunction extends AbstractVectorDoubleDistanceFunction {
/**
* PDELTA parameter
*/
- public static final OptionID PDELTA_ID = OptionID.getOrCreateOptionID("lcss.pDelta", "the allowed deviation in x direction for LCSS alignment (positive double value, 0 <= pDelta <= 1)");
+ public static final OptionID PDELTA_ID = new OptionID("lcss.pDelta", "the allowed deviation in x direction for LCSS alignment (positive double value, 0 <= pDelta <= 1)");
/**
* PEPSILON parameter
*/
- public static final OptionID PEPSILON_ID = OptionID.getOrCreateOptionID("lcss.pEpsilon", "the allowed deviation in y directionfor LCSS alignment (positive double value, 0 <= pEpsilon <= 1)");
+ public static final OptionID PEPSILON_ID = new OptionID("lcss.pEpsilon", "the allowed deviation in y directionfor LCSS alignment (positive double value, 0 <= pEpsilon <= 1)");
/**
* Keeps the currently set pDelta.
@@ -117,7 +117,7 @@ public class LCSSDistanceFunction extends AbstractVectorDoubleDistanceFunction {
* vectors as an instance of {@link DoubleDistance DoubleDistance}.
*/
@Override
- public double doubleDistance(NumberVector<?, ?> v1, NumberVector<?, ?> v2) {
+ public double doubleDistance(NumberVector<?> v1, NumberVector<?> v2) {
final int delta = (int) Math.ceil(v2.getDimensionality() * pDelta);
DoubleMinMax extrema1 = VectorUtil.getRangeDouble(v1);
@@ -130,45 +130,42 @@ public class LCSSDistanceFunction extends AbstractVectorDoubleDistanceFunction {
double[] a, b;
// put shorter vector first
- if(v1.getDimensionality() < v2.getDimensionality()) {
+ if (v1.getDimensionality() < v2.getDimensionality()) {
m = v1.getDimensionality();
n = v2.getDimensionality();
a = new double[m];
b = new double[n];
- for(int i = 0; i < v1.getDimensionality(); i++) {
- a[i] = v1.doubleValue(i + 1);
+ for (int i = 0; i < v1.getDimensionality(); i++) {
+ a[i] = v1.doubleValue(i);
}
- for(int j = 0; j < v2.getDimensionality(); j++) {
- b[j] = v2.doubleValue(j + 1);
+ for (int j = 0; j < v2.getDimensionality(); j++) {
+ b[j] = v2.doubleValue(j);
}
- }
- else {
+ } else {
m = v2.getDimensionality();
n = v1.getDimensionality();
a = new double[m];
b = new double[n];
- for(int i = 0; i < v2.getDimensionality(); i++) {
- a[i] = v2.doubleValue(i + 1);
+ for (int i = 0; i < v2.getDimensionality(); i++) {
+ a[i] = v2.doubleValue(i);
}
- for(int j = 0; j < v1.getDimensionality(); j++) {
- b[j] = v1.doubleValue(j + 1);
+ for (int j = 0; j < v1.getDimensionality(); j++) {
+ b[j] = v1.doubleValue(j);
}
}
double[] curr = new double[n + 1];
- for(int i = 0; i < m; i++) {
+ for (int i = 0; i < m; i++) {
double[] next = new double[n + 1];
- for(int j = Math.max(0, i - delta); j <= Math.min(n - 1, i + delta); j++) {
- if((b[j] + epsilon) >= a[i] & (b[j] - epsilon) <= a[i]) { // match
+ for (int j = Math.max(0, i - delta); j <= Math.min(n - 1, i + delta); j++) {
+ if ((b[j] + epsilon) >= a[i] && (b[j] - epsilon) <= a[i]) { // match
next[j + 1] = curr[j] + 1;
- }
- else if(curr[j + 1] > next[j]) { // ins
+ } else if (curr[j + 1] > next[j]) { // ins
next[j + 1] = curr[j + 1];
- }
- else { // del
+ } else { // del
next[j + 1] = next[j];
}
}
@@ -177,8 +174,8 @@ public class LCSSDistanceFunction extends AbstractVectorDoubleDistanceFunction {
// search for maximum in the last line
double maxEntry = -1;
- for(int i = 1; i < n + 1; i++) {
- if(curr[i] > maxEntry) {
+ for (int i = 1; i < n + 1; i++) {
+ if (curr[i] > maxEntry) {
maxEntry = curr[i];
}
}
@@ -188,13 +185,16 @@ public class LCSSDistanceFunction extends AbstractVectorDoubleDistanceFunction {
// TODO: relax this to VectorTypeInformation!
@Override
- public VectorFieldTypeInformation<? super NumberVector<?, ?>> getInputTypeRestriction() {
+ public VectorFieldTypeInformation<? super NumberVector<?>> getInputTypeRestriction() {
return TypeUtil.NUMBER_VECTOR_FIELD;
}
@Override
public boolean equals(Object obj) {
- if(obj == null) {
+ if (obj == this) {
+ return true;
+ }
+ if (obj == null) {
return false;
}
if (!this.getClass().equals(obj.getClass())) {
@@ -202,7 +202,7 @@ public class LCSSDistanceFunction extends AbstractVectorDoubleDistanceFunction {
}
return (this.pDelta == ((LCSSDistanceFunction) obj).pDelta) && (this.pEpsilon == ((LCSSDistanceFunction) obj).pEpsilon);
}
-
+
/**
* Parameterization class.
*
@@ -211,21 +211,25 @@ public class LCSSDistanceFunction extends AbstractVectorDoubleDistanceFunction {
* @apiviz.exclude
*/
public static class Parameterizer extends AbstractParameterizer {
- protected Double pDelta = 0.0;
+ protected double pDelta = 0.0;
- protected Double pEpsilon = 0.0;
+ protected double pEpsilon = 0.0;
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- final DoubleParameter pDeltaP = new DoubleParameter(PDELTA_ID, new IntervalConstraint(0, IntervalBoundary.CLOSE, 1, IntervalBoundary.CLOSE), 0.1);
- if(config.grab(pDeltaP)) {
- pDelta = pDeltaP.getValue();
+ final DoubleParameter pDeltaP = new DoubleParameter(PDELTA_ID, 0.1);
+ pDeltaP.addConstraint(new GreaterEqualConstraint(0));
+ pDeltaP.addConstraint(new LessEqualConstraint(1));
+ if (config.grab(pDeltaP)) {
+ pDelta = pDeltaP.doubleValue();
}
- final DoubleParameter pEpsilonP = new DoubleParameter(PEPSILON_ID, new IntervalConstraint(0, IntervalBoundary.CLOSE, 1, IntervalBoundary.CLOSE), 0.05);
- if(config.grab(pEpsilonP)) {
- pEpsilon = pEpsilonP.getValue();
+ final DoubleParameter pEpsilonP = new DoubleParameter(PEPSILON_ID, 0.05);
+ pEpsilonP.addConstraint(new GreaterEqualConstraint(0));
+ pEpsilonP.addConstraint(new LessEqualConstraint(1));
+ if (config.grab(pEpsilonP)) {
+ pEpsilon = pEpsilonP.doubleValue();
}
}
@@ -234,4 +238,4 @@ public class LCSSDistanceFunction extends AbstractVectorDoubleDistanceFunction {
return new LCSSDistanceFunction(pDelta, pEpsilon);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/AbstractKNNHeap.java b/src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/AbstractKNNHeap.java
new file mode 100644
index 00000000..30df7c16
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/AbstractKNNHeap.java
@@ -0,0 +1,113 @@
+package de.lmu.ifi.dbs.elki.distance.distanceresultlist;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.Comparator;
+
+import de.lmu.ifi.dbs.elki.database.ids.DistanceDBIDPair;
+import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
+import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
+import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.TiedTopBoundedHeap;
+
+/**
+ * Heap used for KNN management.
+ *
+ * @author Erich Schubert
+ *
+ * @param <P> pair type
+ * @param <D> distance type
+ */
+abstract public class AbstractKNNHeap<P extends DistanceDBIDPair<D>, D extends Distance<D>> implements KNNHeap<D> {
+ /**
+ * Static comparator.
+ */
+ public static final Comparator<? super DistanceDBIDPair<?>> COMPARATOR = new Comp();
+
+ /**
+ * The actual heap.
+ */
+ protected final TiedTopBoundedHeap<P> heap;
+
+ /**
+ * Constructor.
+ *
+ * @param k Maximum heap size (unless tied)
+ */
+ public AbstractKNNHeap(int k) {
+ super();
+ heap = new TiedTopBoundedHeap<P>(k, COMPARATOR);
+ }
+
+ /**
+ * Add a pair to the heap.
+ *
+ * @param pair Pair to add.
+ */
+ public abstract void add(P pair);
+
+ @Override
+ public final int getK() {
+ return heap.getMaxSize();
+ }
+
+ @Override
+ public int size() {
+ return heap.size();
+ }
+
+ @Override
+ public P peek() {
+ return heap.peek();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return heap.isEmpty();
+ }
+
+ @Override
+ public void clear() {
+ heap.clear();
+ }
+
+ @Override
+ public P poll() {
+ return heap.poll();
+ }
+
+ /**
+ * Comparator to use.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ protected static class Comp implements Comparator<DistanceDBIDPair<?>> {
+ @SuppressWarnings("unchecked")
+ @Override
+ public int compare(DistanceDBIDPair<?> o1, DistanceDBIDPair<?> o2) {
+ return -((DistanceDBIDPair<DoubleDistance>)o1).compareByDistance((DistanceDBIDPair<DoubleDistance>)o2);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/DistanceDBIDResult.java b/src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/DistanceDBIDResult.java
new file mode 100644
index 00000000..2d7d68de
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/DistanceDBIDResult.java
@@ -0,0 +1,88 @@
+package de.lmu.ifi.dbs.elki.distance.distanceresultlist;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
+import de.lmu.ifi.dbs.elki.database.ids.DistanceDBIDPair;
+import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
+
+/**
+ * Collection of objects and their distances.
+ *
+ * To iterate over the results, use the following code:
+ *
+ * <pre>
+ * {@code
+ * for (DistanceDBIDResultIter<D> iter = result.iter(); iter.valid(); iter.advance()) {
+ * // You can get the distance via: iter.getDistance();
+ * // Or use iter just like any other DBIDRef
+ * }
+ * }
+ * </pre>
+ *
+ * If you are only interested in the IDs of the objects, the following is also
+ * sufficient:
+ *
+ * <pre>
+ * {@code
+ * for (DBIDIter<D> iter = result.iter(); iter.valid(); iter.advance()) {
+ * // Use iter just like any other DBIDRef
+ * }
+ * }
+ * </pre>
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.landmark
+ *
+ * @apiviz.composedOf DistanceDBIDPair
+ * @apiviz.has DistanceDBIDResultIter
+ *
+ * @param <D> Distance type
+ */
+public interface DistanceDBIDResult<D extends Distance<D>> extends DBIDs {
+ /**
+ * Size of list.
+ *
+ * @return Size
+ */
+ @Override
+ public int size();
+
+ /**
+ * Access a single pair.
+ *
+ * @param off Offset
+ * @return Pair
+ */
+ public DistanceDBIDPair<D> get(int off);
+
+ /**
+ * Get an iterator
+ *
+ * @return New iterator
+ */
+ @Override
+ public DistanceDBIDResultIter<D> iter();
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/database/query/GenericDistanceDBIDList.java b/src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/DistanceDBIDResultIter.java
index 2283c401..d84e5d18 100644
--- a/src/de/lmu/ifi/dbs/elki/database/query/GenericDistanceDBIDList.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/DistanceDBIDResultIter.java
@@ -1,4 +1,4 @@
-package de.lmu.ifi.dbs.elki.database.query;
+package de.lmu.ifi.dbs.elki.distance.distanceresultlist;
/*
This file is part of ELKI:
@@ -23,46 +23,35 @@ package de.lmu.ifi.dbs.elki.database.query;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.util.ArrayList;
-import java.util.Collection;
-
+import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DistanceDBIDPair;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
/**
- * Default class to keep a list of distance-object pairs.
+ * Iterator over distance-based query results.
+ *
+ * There is no getter for the DBID, as this implements
+ * {@link de.lmu.ifi.dbs.elki.database.ids.DBIDRef}.
*
* @author Erich Schubert
*
- * @param <D> Distance type
+ * @apiviz.landmark
+ *
+ * @apiviz.has DistanceDBIDPair - - iterator for
+ * @apiviz.has de.lmu.ifi.dbs.elki.database.ids.DBID - - iterator for
*/
-public class GenericDistanceDBIDList<D extends Distance<D>> extends ArrayList<DistanceResultPair<D>> implements DistanceDBIDResult<D> {
- /**
- * Serialization Version
- */
- private static final long serialVersionUID = 1L;
-
- /**
- * Constructor.
- */
- public GenericDistanceDBIDList() {
- super();
- }
-
+public interface DistanceDBIDResultIter<D extends Distance<D>> extends DBIDIter {
/**
- * Constructor.
+ * Get the distance
*
- * @param c existing collection
+ * @return distance
*/
- public GenericDistanceDBIDList(Collection<? extends DistanceResultPair<D>> c) {
- super(c);
- }
+ public D getDistance();
/**
- * Constructor.
+ * Get an object pair.
*
- * @param initialCapacity Capacity
+ * @return object pair
*/
- public GenericDistanceDBIDList(int initialCapacity) {
- super(initialCapacity);
- }
+ public DistanceDBIDPair<D> getDistancePair();
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/DistanceDBIDResultUtil.java b/src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/DistanceDBIDResultUtil.java
new file mode 100644
index 00000000..aea23be6
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/DistanceDBIDResultUtil.java
@@ -0,0 +1,86 @@
+package de.lmu.ifi.dbs.elki.distance.distanceresultlist;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
+import de.lmu.ifi.dbs.elki.database.ids.DistanceDBIDPair;
+import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
+
+/**
+ * Utility classes for distance DBID results.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.uses DistanceDBIDPair oneway - -
+ */
+public final class DistanceDBIDResultUtil {
+ /**
+ * Get a comparator to sort by distance, then DBID
+ *
+ * @return comparator
+ */
+ @SuppressWarnings("unchecked")
+ public static <D extends DistanceDBIDPair<?>> Comparator<D> distanceComparator() {
+ return (Comparator<D>) BY_DISTANCE_THEN_DBID;
+ }
+
+ /**
+ * Sort a Java list by distance.
+ *
+ * @param list List to sort
+ */
+ @SuppressWarnings("unchecked")
+ public static <D extends DistanceDBIDPair<?>> void sortByDistance(List<? extends D> list) {
+ Collections.sort(list, (Comparator<D>) BY_DISTANCE_THEN_DBID);
+ }
+
+ /**
+ * Static comparator.
+ */
+ private static final Comparator<DistanceDBIDPair<?>> BY_DISTANCE_THEN_DBID = new Comparator<DistanceDBIDPair<?>>() {
+ @SuppressWarnings("unchecked")
+ @Override
+ public int compare(DistanceDBIDPair<?> o1, DistanceDBIDPair<?> o2) {
+ final int d = ((DistanceDBIDPair<DoubleDistance>)o1).compareByDistance((DistanceDBIDPair<DoubleDistance>)o2);
+ return (d == 0) ? DBIDUtil.compare(o1, o2) : d;
+ }
+ };
+
+ public static String toString(DistanceDBIDResult<?> res) {
+ StringBuilder buf = new StringBuilder();
+ buf.append('[');
+ DistanceDBIDResultIter<?> iter = res.iter();
+ for(; iter.valid(); iter.advance()) {
+ if(buf.length() > 1) {
+ buf.append(", ");
+ }
+ buf.append(iter.getDistancePair().toString());
+ }
+ buf.append(']');
+ return buf.toString();
+ }
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/DoubleDistanceDBIDList.java b/src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/DoubleDistanceDBIDList.java
new file mode 100644
index 00000000..9ff22b74
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/DoubleDistanceDBIDList.java
@@ -0,0 +1,190 @@
+package de.lmu.ifi.dbs.elki.distance.distanceresultlist;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+import de.lmu.ifi.dbs.elki.database.ids.DBIDFactory;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
+import de.lmu.ifi.dbs.elki.database.ids.DoubleDistanceDBIDPair;
+import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
+
+/**
+ * Default class to keep a list of distance-object pairs.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.composedOf DoubleDistanceDBIDPair
+ * @apiviz.has DoubleDistanceDBIDResultIter
+ */
+public class DoubleDistanceDBIDList implements ModifiableDistanceDBIDResult<DoubleDistance> {
+ /**
+ * Actual storage.
+ */
+ final ArrayList<DoubleDistanceDBIDPair> storage;
+
+ /**
+ * Constructor.
+ */
+ public DoubleDistanceDBIDList() {
+ super();
+ storage = new ArrayList<DoubleDistanceDBIDPair>();
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param initialCapacity Capacity
+ */
+ public DoubleDistanceDBIDList(int initialCapacity) {
+ super();
+ storage = new ArrayList<DoubleDistanceDBIDPair>(initialCapacity);
+ }
+
+ /**
+ * Add an element.
+ *
+ * @deprecated Pass a double value instead.
+ *
+ * @param dist Distance
+ * @param id ID
+ */
+ @Override
+ @Deprecated
+ public void add(DoubleDistance dist, DBIDRef id) {
+ storage.add(DBIDFactory.FACTORY.newDistancePair(dist.doubleValue(), id));
+ }
+
+ /**
+ * Add an element.
+ *
+ * @param dist Distance
+ * @param id ID
+ */
+ public void add(double dist, DBIDRef id) {
+ storage.add(DBIDFactory.FACTORY.newDistancePair(dist, id));
+ }
+
+ /**
+ * Add an element.
+ *
+ * @param pair Pair to add
+ */
+ public void add(DoubleDistanceDBIDPair pair) {
+ storage.add(pair);
+ }
+
+ @Override
+ public void sort() {
+ Collections.sort(storage, DistanceDBIDResultUtil.distanceComparator());
+ }
+
+ @Override
+ public int size() {
+ return storage.size();
+ }
+
+ @Override
+ public DoubleDistanceDBIDPair get(int off) {
+ return storage.get(off);
+ }
+
+ @Override
+ public DoubleDistanceDBIDResultIter iter() {
+ return new Iter();
+ }
+
+ @Override
+ public boolean contains(DBIDRef o) {
+ for(DBIDIter iter = iter(); iter.valid(); iter.advance()) {
+ if(DBIDUtil.equal(iter, o)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return size() == 0;
+ }
+
+ @Override
+ public String toString() {
+ return DistanceDBIDResultUtil.toString(this);
+ }
+
+ /**
+ * Iterator class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ protected class Iter implements DoubleDistanceDBIDResultIter {
+ /**
+ * Iterator position.
+ */
+ int pos = 0;
+
+ @Override
+ public int internalGetIndex() {
+ return get(pos).internalGetIndex();
+ }
+
+ @Override
+ public boolean valid() {
+ return pos < size();
+ }
+
+ @Override
+ public void advance() {
+ pos++;
+ }
+
+ @Override
+ @Deprecated
+ public DoubleDistance getDistance() {
+ return get(pos).getDistance();
+ }
+
+ @Override
+ public double doubleDistance() {
+ return get(pos).doubleDistance();
+ }
+
+ @Override
+ public DoubleDistanceDBIDPair getDistancePair() {
+ return get(pos);
+ }
+
+ @Override
+ public String toString() {
+ return valid() ? getDistancePair().toString() : "null";
+ }
+ }
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/DoubleDistanceDBIDResultIter.java b/src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/DoubleDistanceDBIDResultIter.java
new file mode 100644
index 00000000..d022fe4c
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/DoubleDistanceDBIDResultIter.java
@@ -0,0 +1,61 @@
+package de.lmu.ifi.dbs.elki.distance.distanceresultlist;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import de.lmu.ifi.dbs.elki.database.ids.DoubleDistanceDBIDPair;
+import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
+
+/**
+ * Iterator for double valued distance-based query results.
+ *
+ * @author Erich Schubert
+ */
+public interface DoubleDistanceDBIDResultIter extends DistanceDBIDResultIter<DoubleDistance> {
+ /**
+ * Get the distance
+ *
+ * @return distance
+ */
+ public double doubleDistance();
+
+ /**
+ * Get an object pair.
+ *
+ * @return object pair
+ */
+ @Override
+ public DoubleDistanceDBIDPair getDistancePair();
+
+ /**
+ * Get the distance
+ *
+ * @deprecated Use {@link #doubleDistance} to avoid creating unnecessary
+ * objects.
+ *
+ * @return distance
+ */
+ @Deprecated
+ @Override
+ public DoubleDistance getDistance();
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/DoubleDistanceKNNHeap.java b/src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/DoubleDistanceKNNHeap.java
new file mode 100644
index 00000000..7faa407d
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/DoubleDistanceKNNHeap.java
@@ -0,0 +1,191 @@
+package de.lmu.ifi.dbs.elki.distance.distanceresultlist;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.Comparator;
+
+import de.lmu.ifi.dbs.elki.database.ids.DBIDFactory;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.database.ids.DoubleDistanceDBIDPair;
+import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
+
+/**
+ * Heap for collecting double-valued KNN instances.
+ *
+ * See also: {@link KNNUtil#newHeap}!
+ *
+ * Experiments have shown that it can be much more performant to track the
+ * knndistance <em>outside</em> of the heap, and do comparisons on the stack:
+ * <blockquote>
+ *
+ * <pre>
+ * {@code
+ * double knndist = Double.POSITIVE_INFINITY;
+ * DoubleDistanceKNNHeap heap = new DoubleDistanceKNNHeap(k);
+ * for (DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
+ * double dist = computeDistance(iditer, ...);
+ * if (dist < knndist) {
+ * heap.add(dist, iditer);
+ * if (heap.size() >= k) {
+ * max = heap.doubleKNNDistance();
+ * }
+ * }
+ * }
+ * }
+ * </pre>
+ *
+ * </blockquote>
+ *
+ * The reason probably is that {@code knndist} resides on the stack and can be
+ * better optimized by the hotspot compiler.
+ *
+ * @author Erich Schubert
+ */
+public class DoubleDistanceKNNHeap extends AbstractKNNHeap<DoubleDistanceDBIDPair, DoubleDistance> {
+ /**
+ * Comparator class.
+ */
+ public static final Comparator<DoubleDistanceDBIDPair> COMPARATOR = new Comp();
+
+ /**
+ * Cached distance to k nearest neighbor (to avoid going through {@link #peek}
+ * too often).
+ */
+ protected double knndistance = Double.POSITIVE_INFINITY;
+
+ /**
+ * Constructor.
+ *
+ * See also: {@link KNNUtil#newHeap}!
+ *
+ * @param k Heap size
+ */
+ public DoubleDistanceKNNHeap(int k) {
+ super(k);
+ }
+
+ /**
+ * Serialize to a {@link DoubleDistanceKNNList}. This empties the heap!
+ *
+ * @return KNNList with the heaps contents.
+ */
+ @Override
+ public DoubleDistanceKNNList toKNNList() {
+ return new DoubleDistanceKNNList(this);
+ }
+
+ /**
+ * Add a distance-id pair to the heap unless the distance is too large.
+ *
+ * Compared to the super.add() method, this often saves the pair construction.
+ *
+ * @param distance Distance value
+ * @param id ID number
+ */
+ public final void add(final double distance, final DBIDRef id) {
+ if (size() < getK() || knndistance >= distance) {
+ heap.add(DBIDFactory.FACTORY.newDistancePair(distance, id));
+ heapModified();
+ }
+ }
+
+ /**
+ * Add a distance-id pair to the heap unless the distance is too large.
+ *
+ * Compared to the super.add() method, this often saves the pair construction.
+ *
+ * @param distance Distance value
+ * @param id ID number
+ */
+ public final void add(final Double distance, final DBIDRef id) {
+ if (size() < getK() || knndistance >= distance) {
+ heap.add(DBIDFactory.FACTORY.newDistancePair(distance, id));
+ heapModified();
+ }
+ }
+
+ // @Override
+ protected void heapModified() {
+ // super.heapModified();
+ if (size() >= getK()) {
+ knndistance = heap.peek().doubleDistance();
+ }
+ }
+
+ // @Override
+ public void add(final DoubleDistanceDBIDPair e) {
+ if (size() < getK() || knndistance >= e.doubleDistance()) {
+ heap.add(e);
+ heapModified();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @deprecated if you know your distances are double-valued, you should be
+ * using the primitive type.
+ *
+ */
+ @Override
+ @Deprecated
+ public void add(DoubleDistance dist, DBIDRef id) {
+ add(dist.doubleValue(), id);
+ }
+
+ /**
+ * Get the distance to the k nearest neighbor, or maxdist otherwise.
+ *
+ * @return Maximum distance
+ */
+ public double doubleKNNDistance() {
+ return knndistance;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @deprecated if you know your distances are double-valued, you should be
+ * using the primitive type.
+ */
+ @Override
+ @Deprecated
+ public DoubleDistance getKNNDistance() {
+ return new DoubleDistance(knndistance);
+ }
+
+ /**
+ * Comparator to use.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ protected static class Comp implements Comparator<DoubleDistanceDBIDPair> {
+ @Override
+ public int compare(DoubleDistanceDBIDPair o1, DoubleDistanceDBIDPair o2) {
+ return -Double.compare(o1.doubleDistance(), o2.doubleDistance());
+ }
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/DoubleDistanceKNNList.java b/src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/DoubleDistanceKNNList.java
new file mode 100644
index 00000000..656377c6
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/DoubleDistanceKNNList.java
@@ -0,0 +1,238 @@
+package de.lmu.ifi.dbs.elki.distance.distanceresultlist;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.Collection;
+import java.util.Iterator;
+
+import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
+import de.lmu.ifi.dbs.elki.database.ids.DoubleDistanceDBIDPair;
+import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
+import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.Heap;
+
+/**
+ * Finalized KNN List.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.composedOf DoubleDistanceDBIDPair
+ * @apiviz.has DoubleDistanceDBIDResultIter
+ */
+public class DoubleDistanceKNNList implements KNNResult<DoubleDistance> {
+ /**
+ * The value of k this was materialized for.
+ */
+ private final int k;
+
+ /**
+ * The actual data array.
+ */
+ private final DoubleDistanceDBIDPair[] data;
+
+ /**
+ * Constructor. This will <em>clone</em> the given collection!
+ *
+ * @param col Existing collection
+ * @param k K parameter
+ */
+ public DoubleDistanceKNNList(Collection<DoubleDistanceDBIDPair> col, int k) {
+ super();
+ this.data = new DoubleDistanceDBIDPair[col.size()];
+ this.k = k;
+ assert (col.size() >= this.k) : "Collection doesn't contain enough objects!";
+ // Get sorted data from heap; but in reverse.
+ Iterator<DoubleDistanceDBIDPair> it = col.iterator();
+ for(int i = 0; it.hasNext(); i++) {
+ data[i] = it.next();
+ }
+ assert (data.length == 0 || data[0] != null);
+ }
+
+ /**
+ * Constructor, to be called from KNNHeap only. Use {@link KNNHeap#toKNNList}
+ * instead!
+ *
+ * @param heap Calling heap
+ */
+ protected DoubleDistanceKNNList(DoubleDistanceKNNHeap heap) {
+ super();
+ this.data = new DoubleDistanceDBIDPair[heap.size()];
+ this.k = heap.getK();
+ assert (heap.size() >= this.k) : "Heap doesn't contain enough objects!";
+ // Get sorted data from heap; but in reverse.
+ int i = heap.size();
+ while(heap.size() > 0) {
+ i--;
+ assert (i >= 0);
+ data[i] = heap.poll();
+ }
+ assert (data.length == 0 || data[0] != null);
+ assert (heap.size() == 0);
+ }
+
+ /**
+ * Constructor, to be called from KNNHeap only. Use {@link KNNHeap#toKNNList}
+ * instead!
+ *
+ * @param heap Calling heap
+ * @param k Target number of neighbors (before ties)
+ */
+ public DoubleDistanceKNNList(Heap<DoubleDistanceDBIDPair> heap, int k) {
+ super();
+ this.data = new DoubleDistanceDBIDPair[heap.size()];
+ this.k = k;
+ assert (heap.size() >= this.k) : "Heap doesn't contain enough objects!";
+ // Get sorted data from heap; but in reverse.
+ int i = heap.size();
+ while(heap.size() > 0) {
+ i--;
+ assert (i >= 0);
+ data[i] = heap.poll();
+ }
+ assert (data.length == 0 || data[0] != null);
+ assert (heap.size() == 0);
+ }
+
+ @Override
+ public int getK() {
+ return k;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @deprecated use doubleKNNDistance()!
+ */
+ @Override
+ @Deprecated
+ public DoubleDistance getKNNDistance() {
+ return get(getK() - 1).getDistance();
+ }
+
+ /**
+ * Get the kNN distance as double value.
+ *
+ * @return Distance
+ */
+ public double doubleKNNDistance() {
+ return get(getK() - 1).doubleDistance();
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder buf = new StringBuilder();
+ buf.append("kNNList[");
+ for(DoubleDistanceDBIDResultIter iter = this.iter(); iter.valid();) {
+ buf.append(iter.doubleDistance()).append(':').append(DBIDUtil.toString(iter));
+ iter.advance();
+ if(iter.valid()) {
+ buf.append(',');
+ }
+ }
+ buf.append(']');
+ return buf.toString();
+ }
+
+ @Override
+ public DoubleDistanceDBIDPair get(int index) {
+ return data[index];
+ }
+
+ @Override
+ public DoubleDistanceDBIDResultIter iter() {
+ return new Itr();
+ }
+
+ @Override
+ public int size() {
+ return data.length;
+ }
+
+ @Override
+ public boolean contains(DBIDRef o) {
+ for(DBIDIter iter = iter(); iter.valid(); iter.advance()) {
+ if(DBIDUtil.equal(iter, o)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return size() == 0;
+ }
+
+ /**
+ * Iterator.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ private class Itr implements DoubleDistanceDBIDResultIter {
+ /**
+ * Cursor position.
+ */
+ private int pos = 0;
+
+ @Override
+ public int internalGetIndex() {
+ return get(pos).internalGetIndex();
+ }
+
+ @Override
+ public boolean valid() {
+ return pos < data.length;
+ }
+
+ @Override
+ public void advance() {
+ pos++;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @deprecated use {@link #doubleDistance}!
+ */
+ @Override
+ @Deprecated
+ public DoubleDistance getDistance() {
+ return get(pos).getDistance();
+ }
+
+ @Override
+ public double doubleDistance() {
+ return get(pos).doubleDistance();
+ }
+
+ @Override
+ public DoubleDistanceDBIDPair getDistancePair() {
+ return get(pos);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/GenericDistanceDBIDList.java b/src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/GenericDistanceDBIDList.java
new file mode 100644
index 00000000..05e4e687
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/GenericDistanceDBIDList.java
@@ -0,0 +1,163 @@
+package de.lmu.ifi.dbs.elki.distance.distanceresultlist;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.ArrayList;
+
+import de.lmu.ifi.dbs.elki.database.ids.DBIDFactory;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
+import de.lmu.ifi.dbs.elki.database.ids.DistanceDBIDPair;
+import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
+
+/**
+ * Default class to keep a list of distance-object pairs.
+ *
+ * @author Erich Schubert
+ *
+ * @param <D> Distance type
+ */
+public class GenericDistanceDBIDList<D extends Distance<D>> implements ModifiableDistanceDBIDResult<D> {
+ /**
+ * Actual storage.
+ */
+ final ArrayList<DistanceDBIDPair<D>> storage;
+
+ /**
+ * Constructor.
+ */
+ public GenericDistanceDBIDList() {
+ super();
+ storage = new ArrayList<DistanceDBIDPair<D>>();
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param initialCapacity Capacity
+ */
+ public GenericDistanceDBIDList(int initialCapacity) {
+ super();
+ storage = new ArrayList<DistanceDBIDPair<D>>(initialCapacity);
+ }
+
+ @Override
+ public void add(D dist, DBIDRef id) {
+ storage.add(DBIDFactory.FACTORY.newDistancePair(dist, id));
+ }
+
+ /**
+ * Add a prepared pair.
+ *
+ * @param pair Pair to add
+ */
+ public void add(DistanceDBIDPair<D> pair) {
+ storage.add(pair);
+ }
+
+ @Override
+ public void sort() {
+ DistanceDBIDResultUtil.sortByDistance(storage);
+ }
+
+ @Override
+ public int size() {
+ return storage.size();
+ }
+
+ @Override
+ public DistanceDBIDPair<D> get(int off) {
+ return storage.get(off);
+ }
+
+ @Override
+ public DistanceDBIDResultIter<D> iter() {
+ return new Iter();
+ }
+
+ @Override
+ public boolean contains(DBIDRef o) {
+ for(DBIDIter iter = iter(); iter.valid(); iter.advance()) {
+ if(DBIDUtil.equal(iter, o)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return size() == 0;
+ }
+
+ @Override
+ public String toString() {
+ return DistanceDBIDResultUtil.toString(this);
+ }
+
+ /**
+ * Iterator class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ protected class Iter implements DistanceDBIDResultIter<D> {
+ /**
+ * Iterator position.
+ */
+ int pos = 0;
+
+ @Override
+ public int internalGetIndex() {
+ return get(pos).internalGetIndex();
+ }
+
+ @Override
+ public boolean valid() {
+ return pos < size();
+ }
+
+ @Override
+ public void advance() {
+ pos++;
+ }
+
+ @Override
+ public D getDistance() {
+ return get(pos).getDistance();
+ }
+
+ @Override
+ public DistanceDBIDPair<D> getDistancePair() {
+ return get(pos);
+ }
+
+ @Override
+ public String toString() {
+ return valid() ? getDistancePair().toString() : "null";
+ }
+ }
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/GenericKNNHeap.java b/src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/GenericKNNHeap.java
new file mode 100644
index 00000000..c13887be
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/GenericKNNHeap.java
@@ -0,0 +1,106 @@
+package de.lmu.ifi.dbs.elki.distance.distanceresultlist;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import de.lmu.ifi.dbs.elki.database.ids.DBIDFactory;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.database.ids.DistanceDBIDPair;
+import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
+
+/**
+ * Heap for collecting kNN candiates with arbitrary distance types.
+ *
+ * For double distances, see {@link DoubleDistanceKNNHeap}
+ *
+ * <b>To instantiate, use {@link KNNUtil#newHeap} instead!</b>
+ *
+ * @author Erich Schubert
+ *
+ * @param <D> Distance type
+ */
+class GenericKNNHeap<D extends Distance<D>> extends AbstractKNNHeap<DistanceDBIDPair<D>, D> {
+ /**
+ * Cached distance to k nearest neighbor (to avoid going through {@link #peek}
+ * each time).
+ */
+ protected D knndistance = null;
+
+ /**
+ * Constructor.
+ *
+ * <b>To instantiate, use {@link KNNUtil#newHeap} instead!</b>
+ *
+ * @param k Heap size
+ */
+ protected GenericKNNHeap(int k) {
+ super(k);
+ }
+
+ /**
+ * Serialize to a {@link GenericKNNList}. This empties the heap!
+ *
+ * @return KNNList with the heaps contents.
+ */
+ @Override
+ public GenericKNNList<D> toKNNList() {
+ return new GenericKNNList<D>(this);
+ }
+
+ @Override
+ public void add(D distance, DBIDRef id) {
+ if (size() < getK()) {
+ heap.add(DBIDFactory.FACTORY.newDistancePair(distance, id));
+ heapModified();
+ return;
+ }
+ // size >= maxsize. Insert only when necessary.
+ if (knndistance.compareTo(distance) >= 0) {
+ // Replace worst element.
+ heap.add(DBIDFactory.FACTORY.newDistancePair(distance, id));
+ heapModified();
+ }
+ }
+
+ @Override
+ public void add(DistanceDBIDPair<D> pair) {
+ if (size() < getK() || knndistance.compareTo(pair.getDistance()) >= 0) {
+ heap.add(pair);
+ heapModified();
+ }
+ }
+
+ // @Override
+ protected void heapModified() {
+ // super.heapModified();
+ // Update threshold.
+ if (size() >= getK()) {
+ knndistance = heap.peek().getDistance();
+ }
+ }
+
+ @Override
+ public D getKNNDistance() {
+ return knndistance;
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/KNNList.java b/src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/GenericKNNList.java
index 3e6ecc9d..71217b23 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/KNNList.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/GenericKNNList.java
@@ -1,4 +1,4 @@
-package de.lmu.ifi.dbs.elki.utilities.datastructures.heap;
+package de.lmu.ifi.dbs.elki.distance.distanceresultlist;
/*
This file is part of ELKI:
@@ -23,16 +23,12 @@ package de.lmu.ifi.dbs.elki.utilities.datastructures.heap;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.util.AbstractList;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Queue;
-
-import de.lmu.ifi.dbs.elki.database.ids.ArrayDBIDs;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
-import de.lmu.ifi.dbs.elki.database.query.knn.KNNResult;
-import de.lmu.ifi.dbs.elki.database.query.knn.KNNUtil;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
+import de.lmu.ifi.dbs.elki.database.ids.DistanceDBIDPair;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
+import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.Heap;
/**
* Finalized KNN List.
@@ -41,7 +37,7 @@ import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
*
* @param <D> Distance type
*/
-public class KNNList<D extends Distance<D>> extends AbstractList<DistanceResultPair<D>> implements KNNResult<D> {
+public class GenericKNNList<D extends Distance<D>> implements KNNResult<D> {
/**
* The value of k this was materialized for.
*/
@@ -58,14 +54,14 @@ public class KNNList<D extends Distance<D>> extends AbstractList<DistanceResultP
*
* @param heap Calling heap
*/
- protected KNNList(KNNHeap<D> heap) {
+ protected GenericKNNList(KNNHeap<D> heap) {
super();
this.data = new Object[heap.size()];
this.k = heap.getK();
- assert(heap.size() >= this.k) : "Heap doesn't contain enough objects!";
+ assert (heap.size() >= this.k) : "Heap doesn't contain enough objects!";
// Get sorted data from heap; but in reverse.
int i = heap.size();
- while(!heap.isEmpty()) {
+ while(heap.size() > 0) {
i--;
assert (i >= 0);
data[i] = heap.poll();
@@ -80,11 +76,11 @@ public class KNNList<D extends Distance<D>> extends AbstractList<DistanceResultP
* @param heap Calling heap
* @param k K value
*/
- public KNNList(Queue<D> heap, int k) {
+ public GenericKNNList(Heap<? extends DistanceDBIDPair<D>> heap, int k) {
super();
this.data = new Object[heap.size()];
this.k = k;
- assert(heap.size() >= this.k) : "Heap doesn't contain enough objects!";
+ assert (heap.size() >= this.k) : "Heap doesn't contain enough objects!";
// Get sorted data from heap; but in reverse.
int i = heap.size();
while(!heap.isEmpty()) {
@@ -107,39 +103,28 @@ public class KNNList<D extends Distance<D>> extends AbstractList<DistanceResultP
}
@Override
- public ArrayDBIDs asDBIDs() {
- return KNNUtil.asDBIDs(this);
- }
-
- @Override
- public List<D> asDistanceList() {
- return KNNUtil.asDistanceList(this);
- }
-
- @Override
public String toString() {
- StringBuffer buf = new StringBuffer();
+ StringBuilder buf = new StringBuilder();
buf.append("kNNList[");
- Iterator<DistanceResultPair<D>> iter = this.iterator();
- while(iter.hasNext()) {
- DistanceResultPair<D> pair = iter.next();
- buf.append(pair.getDistance()).append(":").append(pair.getDBID());
- if(iter.hasNext()) {
- buf.append(",");
+ for(DistanceDBIDResultIter<D> iter = this.iter(); iter.valid();) {
+ buf.append(iter.getDistance()).append(':').append(DBIDUtil.toString(iter));
+ iter.advance();
+ if(iter.valid()) {
+ buf.append(',');
}
}
- buf.append("]");
+ buf.append(']');
return buf.toString();
}
@SuppressWarnings("unchecked")
@Override
- public DistanceResultPair<D> get(int index) {
- return (DistanceResultPair<D>) data[index];
+ public DistanceDBIDPair<D> get(int index) {
+ return (DistanceDBIDPair<D>) data[index];
}
@Override
- public Iterator<DistanceResultPair<D>> iterator() {
+ public DistanceDBIDResultIter<D> iter() {
return new Itr();
}
@@ -148,34 +133,57 @@ public class KNNList<D extends Distance<D>> extends AbstractList<DistanceResultP
return data.length;
}
+ @Override
+ public boolean contains(DBIDRef o) {
+ for(DBIDIter iter = iter(); iter.valid(); iter.advance()) {
+ if(DBIDUtil.equal(iter, o)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return size() == 0;
+ }
+
/**
- * Iterator
+ * Iterator.
*
* @author Erich Schubert
*
* @apiviz.exclude
*/
- private class Itr implements Iterator<DistanceResultPair<D>> {
+ private class Itr implements DistanceDBIDResultIter<D> {
/**
- * Cursor position
+ * Cursor position.
*/
- private int pos = -1;
+ private int pos = 0;
@Override
- public boolean hasNext() {
- return pos + 1 < data.length;
+ public int internalGetIndex() {
+ return get(pos).internalGetIndex();
}
- @SuppressWarnings("unchecked")
@Override
- public DistanceResultPair<D> next() {
+ public boolean valid() {
+ return pos < data.length;
+ }
+
+ @Override
+ public void advance() {
pos++;
- return (DistanceResultPair<D>) data[pos];
}
@Override
- public void remove() {
- throw new UnsupportedOperationException("kNN results are unmodifiable.");
+ public D getDistance() {
+ return get(pos).getDistance();
+ }
+
+ @Override
+ public DistanceDBIDPair<D> getDistancePair() {
+ return get(pos);
}
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/KNNHeap.java b/src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/KNNHeap.java
new file mode 100644
index 00000000..cdcb7d98
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/KNNHeap.java
@@ -0,0 +1,113 @@
+package de.lmu.ifi.dbs.elki.distance.distanceresultlist;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.database.ids.DistanceDBIDPair;
+import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
+
+/**
+ * Interface for kNN heaps.
+ *
+ * To instantiate, use: {@link KNNUtil#newHeap}!
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.landmark
+ *
+ * @apiviz.uses GenericKNNList - - «serializes to»
+ * @apiviz.uses DoubleDistanceKNNList - - «serializes to»
+ * @apiviz.composedOf DistanceDBIDPair
+ *
+ * @param <D> Distance function
+ */
+public interface KNNHeap<D extends Distance<D>> {
+ /**
+ * Serialize to a {@link GenericKNNList}. This empties the heap!
+ *
+ * @return KNNList with the heaps contents.
+ */
+ KNNResult<D> toKNNList();
+
+ /**
+ * Get the K parameter ("maxsize" internally).
+ *
+ * @return K
+ */
+ int getK();
+
+ /**
+ * Get the distance to the k nearest neighbor, or maxdist otherwise.
+ *
+ * @return Maximum distance
+ */
+ D getKNNDistance();
+
+ /**
+ * Add a distance-id pair to the heap unless the distance is too large.
+ *
+ * Compared to the super.add() method, this often saves the pair construction.
+ *
+ * @param distance Distance value
+ * @param id ID number
+ */
+ void add(D distance, DBIDRef id);
+
+ /**
+ * Current size of heap.
+ *
+ * @return Heap size
+ */
+ int size();
+
+ /**
+ * Test if the heap is empty.
+ *
+ * @return true when empty.
+ */
+ boolean isEmpty();
+
+ /**
+ * Clear the heap.
+ */
+ void clear();
+
+ /**
+ * Poll the <em>largest</em> element from the heap.
+ *
+ * This is in descending order because of the heap structure. For a convenient
+ * way to serialize the heap into a list that you can iterate in ascending
+ * order, see {@link #toKNNList()}.
+ *
+ * @return largest element
+ */
+ DistanceDBIDPair<D> poll();
+
+ /**
+ * Peek at the <em>largest</em> element in the heap.
+ *
+ * @return The current largest element.
+ */
+ DistanceDBIDPair<D> peek();
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/database/query/knn/KNNResult.java b/src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/KNNResult.java
index e7612fcc..785e5b8a 100644
--- a/src/de/lmu/ifi/dbs/elki/database/query/knn/KNNResult.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/KNNResult.java
@@ -1,4 +1,4 @@
-package de.lmu.ifi.dbs.elki.database.query.knn;
+package de.lmu.ifi.dbs.elki.distance.distanceresultlist;
/*
This file is part of ELKI:
@@ -23,21 +23,42 @@ package de.lmu.ifi.dbs.elki.database.query.knn;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.util.List;
-
-import de.lmu.ifi.dbs.elki.database.ids.ArrayDBIDs;
-import de.lmu.ifi.dbs.elki.database.query.DistanceDBIDResult;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
+import de.lmu.ifi.dbs.elki.database.ids.DistanceDBIDPair;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
/**
- * Interface for kNN results - List<> like.
+ * Interface for kNN results.
+ *
+ * To iterate over the results, use the following code:
+ *
+ * <pre>
+ * {@code
+ * for (DistanceDBIDResultIter<D> iter = result.iter(); iter.valid(); iter.advance()) {
+ * // You can get the distance via: iter.getDistance();
+ * // Or use iter just like any other DBIDRef
+ * }
+ * }
+ * </pre>
+ *
+ * If you are only interested in the IDs of the objects, the following is also
+ * sufficient:
+ *
+ * <pre>
+ * {@code
+ * for (DBIDIter<D> iter = result.iter(); iter.valid(); iter.advance()) {
+ * // Use iter just like any other DBIDRef
+ * }
+ * }
+ * </pre>
*
* @author Erich Schubert
*
- * @param <D> Distance type
+ * @apiviz.landmark
*
- * @apiviz.composedOf DistanceResultPair
+ * @apiviz.composedOf DistanceDBIDPair
+ * @apiviz.has DistanceDBIDResultIter
+ *
+ * @param <D> Distance type
*/
public interface KNNResult<D extends Distance<D>> extends DistanceDBIDResult<D> {
/**
@@ -59,7 +80,7 @@ public interface KNNResult<D extends Distance<D>> extends DistanceDBIDResult<D>
* @param index
*/
@Override
- public DistanceResultPair<D> get(int index);
+ public DistanceDBIDPair<D> get(int index);
/**
* Get the distance to the k nearest neighbor, or maxdist otherwise.
@@ -67,18 +88,4 @@ public interface KNNResult<D extends Distance<D>> extends DistanceDBIDResult<D>
* @return Maximum distance
*/
public D getKNNDistance();
-
- /**
- * View as ArrayDBIDs
- *
- * @return Static DBIDs
- */
- public ArrayDBIDs asDBIDs();
-
- /**
- * View as list of distances
- *
- * @return List of distances view
- */
- public List<D> asDistanceList();
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/KNNUtil.java b/src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/KNNUtil.java
new file mode 100644
index 00000000..fa658941
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/KNNUtil.java
@@ -0,0 +1,372 @@
+package de.lmu.ifi.dbs.elki.distance.distanceresultlist;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.AbstractList;
+import java.util.Iterator;
+import java.util.List;
+
+import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
+import de.lmu.ifi.dbs.elki.database.ids.DistanceDBIDPair;
+import de.lmu.ifi.dbs.elki.database.ids.DoubleDistanceDBIDPair;
+import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
+import de.lmu.ifi.dbs.elki.distance.DistanceUtil;
+import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
+import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
+
+/**
+ * Helper classes for kNN results.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.landmark
+ *
+ * @apiviz.has KNNResult oneway - - «processes»
+ * @apiviz.has KNNHeap oneway - - «creates»
+ * @apiviz.has DistanceView
+ * @apiviz.has KNNSubList
+ */
+public final class KNNUtil {
+ /**
+ * Fake constructor: do not instantiate.
+ */
+ private KNNUtil() {
+ // Empty.
+ }
+
+ /**
+ * Create an appropriate heap for the distance function.
+ *
+ * This will use a double heap if appropriate.
+ *
+ * @param df Distance function
+ * @param k K value
+ * @param <D> distance type
+ * @return New heap of size k, appropriate for this distance function.
+ */
+ @SuppressWarnings("unchecked")
+ public static <D extends Distance<D>> KNNHeap<D> newHeap(DistanceFunction<?, D> df, int k) {
+ if (DistanceUtil.isDoubleDistanceFunction(df)) {
+ return (KNNHeap<D>) new DoubleDistanceKNNHeap(k);
+ }
+ return new GenericKNNHeap<D>(k);
+ }
+
+ /**
+ * Create an appropriate heap for the distance function.
+ *
+ * This will use a double heap if appropriate.
+ *
+ * @param df Distance function
+ * @param k K value
+ * @param <D> distance type
+ * @return New heap of size k, appropriate for this distance function.
+ */
+ @SuppressWarnings("unchecked")
+ public static <D extends Distance<D>> KNNHeap<D> newHeap(DistanceQuery<?, D> df, int k) {
+ if (DistanceUtil.isDoubleDistanceFunction(df)) {
+ return (KNNHeap<D>) new DoubleDistanceKNNHeap(k);
+ }
+ return new GenericKNNHeap<D>(k);
+ }
+
+ /**
+ * Create an appropriate heap for the distance function.
+ *
+ * This will use a double heap if appropriate.
+ *
+ * @param factory distance prototype
+ * @param k K value
+ * @param <D> distance type
+ * @return New heap of size k, appropriate for this distance type.
+ */
+ @SuppressWarnings("unchecked")
+ public static <D extends Distance<D>> KNNHeap<D> newHeap(D factory, int k) {
+ if (factory instanceof DoubleDistance) {
+ return (KNNHeap<D>) new DoubleDistanceKNNHeap(k);
+ }
+ return new GenericKNNHeap<D>(k);
+ }
+
+ /**
+ * Build a new heap from a given list.
+ *
+ * @param exist Existing result
+ * @param <D> Distance type
+ * @return New heap
+ */
+ @SuppressWarnings("unchecked")
+ public static <D extends Distance<D>> KNNHeap<D> newHeap(KNNResult<D> exist) {
+ if (exist instanceof DoubleDistanceKNNList) {
+ DoubleDistanceKNNHeap heap = new DoubleDistanceKNNHeap(exist.getK());
+ // Insert backwards, as this will produce a proper heap
+ for (int i = exist.size() - 1; i >= 0; i--) {
+ heap.add((DoubleDistanceDBIDPair) exist.get(i));
+ }
+ return (KNNHeap<D>) heap;
+ } else {
+ GenericKNNHeap<D> heap = new GenericKNNHeap<D>(exist.getK());
+ // Insert backwards, as this will produce a proper heap
+ for (int i = exist.size() - 1; i >= 0; i--) {
+ heap.add(exist.get(i));
+ }
+ return heap;
+ }
+ }
+
+ /**
+ * Sublist of an existing result to contain only the first k elements.
+ *
+ * @author Erich Schubert
+ *
+ * @param <D> Distance
+ */
+ protected static class KNNSubList<D extends Distance<D>> implements KNNResult<D> {
+ /**
+ * Parameter k.
+ */
+ private final int k;
+
+ /**
+ * Actual size, including ties.
+ */
+ private final int size;
+
+ /**
+ * Wrapped inner result.
+ */
+ private final KNNResult<D> inner;
+
+ /**
+ * Constructor.
+ *
+ * @param inner Inner instance
+ * @param k k value
+ */
+ public KNNSubList(KNNResult<D> inner, int k) {
+ this.inner = inner;
+ this.k = k;
+ // Compute list size
+ // TODO: optimize for double distances.
+ {
+ DistanceDBIDPair<D> dist = inner.get(k);
+ int i = k;
+ while (i + 1 < inner.size()) {
+ if (dist.compareByDistance(inner.get(i + 1)) < 0) {
+ break;
+ }
+ i++;
+ }
+ size = i;
+ }
+ }
+
+ @Override
+ public int getK() {
+ return k;
+ }
+
+ @Override
+ public DistanceDBIDPair<D> get(int index) {
+ assert (index < size) : "Access beyond design size of list.";
+ return inner.get(index);
+ }
+
+ @Override
+ public D getKNNDistance() {
+ return inner.get(k).getDistance();
+ }
+
+ @Override
+ public DistanceDBIDResultIter<D> iter() {
+ return new Itr();
+ }
+
+ @Override
+ public boolean contains(DBIDRef o) {
+ for (DBIDIter iter = iter(); iter.valid(); iter.advance()) {
+ if (DBIDUtil.equal(iter, o)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return size == 0;
+ }
+
+ @Override
+ public int size() {
+ return size;
+ }
+
+ /**
+ * Iterator for the sublist.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ private class Itr implements DistanceDBIDResultIter<D> {
+ /**
+ * Current position.
+ */
+ private int pos = 0;
+
+ @Override
+ public boolean valid() {
+ return pos < size;
+ }
+
+ @Override
+ public void advance() {
+ pos++;
+ }
+
+ @Override
+ public D getDistance() {
+ return inner.get(pos).getDistance();
+ }
+
+ @Override
+ public DistanceDBIDPair<D> getDistancePair() {
+ return inner.get(pos);
+ }
+
+ @Override
+ public int internalGetIndex() {
+ return inner.get(pos).internalGetIndex();
+ }
+ }
+ }
+
+ /**
+ * Proxy iterator for accessing DBIDs.
+ *
+ * @author Erich Schubert
+ */
+ protected static class DistanceItr<D extends Distance<D>> implements Iterator<D> {
+ /**
+ * The real iterator.
+ */
+ DistanceDBIDResultIter<D> itr;
+
+ /**
+ * Constructor.
+ *
+ * @param distanceDBIDResultIter Iterator
+ */
+ protected DistanceItr(DistanceDBIDResultIter<D> distanceDBIDResultIter) {
+ super();
+ this.itr = distanceDBIDResultIter;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return itr.valid();
+ }
+
+ @Override
+ public D next() {
+ D dist = itr.getDistance();
+ itr.advance();
+ return dist;
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ /**
+ * A view on the Distances of the result.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.composedOf DistanceItr
+ */
+ protected static class DistanceView<D extends Distance<D>> extends AbstractList<D> implements List<D> {
+ /**
+ * The true list.
+ */
+ final KNNResult<D> parent;
+
+ /**
+ * Constructor.
+ *
+ * @param parent Owner
+ */
+ public DistanceView(KNNResult<D> parent) {
+ super();
+ this.parent = parent;
+ }
+
+ @Override
+ public D get(int i) {
+ return parent.get(i).getDistance();
+ }
+
+ @Override
+ public Iterator<D> iterator() {
+ return new DistanceItr<D>(parent.iter());
+ }
+
+ @Override
+ public int size() {
+ return parent.size();
+ }
+ }
+
+ /**
+ * View as list of distances.
+ *
+ * @param list Result to proxy
+ * @param <D> distance type
+ * @return List of distances view
+ */
+ public static <D extends Distance<D>> List<D> asDistanceList(KNNResult<D> list) {
+ return new DistanceView<D>(list);
+ }
+
+ /**
+ * Get a subset of the KNN result.
+ *
+ * @param list Existing list
+ * @param k k
+ * @param <D> distance type
+ * @return Subset
+ */
+ public static <D extends Distance<D>> KNNResult<D> subList(KNNResult<D> list, int k) {
+ if (k >= list.size()) {
+ return list;
+ }
+ return new KNNSubList<D>(list, k);
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/ModifiableDistanceDBIDResult.java b/src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/ModifiableDistanceDBIDResult.java
new file mode 100644
index 00000000..52889652
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/ModifiableDistanceDBIDResult.java
@@ -0,0 +1,48 @@
+package de.lmu.ifi.dbs.elki.distance.distanceresultlist;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
+/**
+ * Modifiable API for Distance-DBID results
+ *
+ * @author Erich Schubert
+ *
+ * @param <D> Distance type
+ */
+public interface ModifiableDistanceDBIDResult<D extends Distance<D>> extends DistanceDBIDResult<D> {
+ /**
+ * Add an object to this result.
+ *
+ * @param distance Distance to add
+ * @param id DBID to add
+ */
+ public void add(D distance, DBIDRef id);
+
+ /**
+ * Sort the result in ascending order
+ */
+ public void sort();
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/package-info.java b/src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/package-info.java
new file mode 100644
index 00000000..3d2a4b3b
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/distance/distanceresultlist/package-info.java
@@ -0,0 +1,53 @@
+/**
+ * <p>Classes for building and storing the results of distance-based queries</p>
+ *
+ * <p>The classes in this package essentially form three groups:
+ * <ol>
+ * <li>{@link de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNHeap} for <b>building kNN results</b>.
+ * It allows adding new candidates (and loses old candidates automatically), but it is not iterable.<br />
+ * To get an instance, use {@link de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNUtil#newHeap}!
+ * </li>
+ * <li>{@link de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult} is the <b>final kNN result</b>
+ * obtained by serializing a heap via {@link de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNHeap#toKNNList}.
+ * It is iterable and totally ordered, but can no longer be modified (unless you call
+ * {@link de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNUtil#newHeap}!</li>
+ * <li>{@link de.lmu.ifi.dbs.elki.distance.distanceresultlist.GenericDistanceDBIDList} and the optimized
+ * counterpart {@link de.lmu.ifi.dbs.elki.distance.distanceresultlist.DoubleDistanceDBIDList}, are
+ * <b>modifiable, but not necessarily sorted</b> lists of neighbors, useful for example for range queries.</li>
+ * </ol>
+ * </p>
+ *
+ * <p>Try to choose the most appropriate one! Heaps are optimized for updates but bad for reading,
+ * KNNResult is optimized for reading but unmodifiable, and the lists are easy to modify,
+ * but less efficient than heaps.</p>
+ *
+ * @apiviz.exclude java.util.*
+ * @apiviz.exclude elki.database.query.*
+ * @apiviz.exclude elki.database.ids.DBIDIter
+ * @apiviz.exclude elki.database.ids.DBIDs
+ * @apiviz.exclude KNNUtil.DistanceItr
+ * @apiviz.exclude DoubleDistance
+ */
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package de.lmu.ifi.dbs.elki.distance.distanceresultlist; \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancevalue/BitDistance.java b/src/de/lmu/ifi/dbs/elki/distance/distancevalue/BitDistance.java
index 24e33d35..9c81188f 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancevalue/BitDistance.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancevalue/BitDistance.java
@@ -40,7 +40,7 @@ public class BitDistance extends NumberDistance<BitDistance, Bit> {
/**
* The static factory instance
*/
- public final static BitDistance FACTORY = new BitDistance();
+ public static final BitDistance FACTORY = new BitDistance();
/**
* The distance value
@@ -108,7 +108,7 @@ public class BitDistance extends NumberDistance<BitDistance, Bit> {
*/
@Override
public void writeExternal(ObjectOutput out) throws IOException {
- out.writeBoolean(this.bitValue());
+ out.writeBoolean(value);
}
/**
@@ -116,7 +116,7 @@ public class BitDistance extends NumberDistance<BitDistance, Bit> {
*/
@Override
public void readExternal(ObjectInput in) throws IOException {
- setValue(new Bit(in.readBoolean()));
+ value = in.readBoolean();
}
/**
@@ -131,16 +131,6 @@ public class BitDistance extends NumberDistance<BitDistance, Bit> {
}
@Override
- public Bit getValue() {
- return new Bit(this.value);
- }
-
- @Override
- void setValue(Bit value) {
- this.value = value.bitValue();
- }
-
- @Override
public double doubleValue() {
return value ? 1.0 : 0.0;
}
@@ -162,10 +152,9 @@ public class BitDistance extends NumberDistance<BitDistance, Bit> {
@Override
public BitDistance parseString(String val) throws IllegalArgumentException {
- if(testInputPattern(val)) {
+ if (testInputPattern(val)) {
return new BitDistance(Bit.valueOf(val).bitValue());
- }
- else {
+ } else {
throw new IllegalArgumentException("Given pattern \"" + val + "\" does not match required pattern \"" + requiredInputPattern() + "\"");
}
}
@@ -197,11 +186,36 @@ public class BitDistance extends NumberDistance<BitDistance, Bit> {
@Override
public boolean isNullDistance() {
- return (value == false);
+ return !value;
}
@Override
public boolean isUndefinedDistance() {
return false;
}
-} \ No newline at end of file
+
+ @Override
+ public String toString() {
+ return Boolean.toString(value);
+ }
+
+ @Override
+ public int hashCode() {
+ return (value ? 1231 : 1237);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!super.equals(obj)) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ BitDistance other = (BitDistance) obj;
+ return (value == other.value);
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancevalue/CorrelationDistance.java b/src/de/lmu/ifi/dbs/elki/distance/distancevalue/CorrelationDistance.java
index f1203887..080f13c9 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancevalue/CorrelationDistance.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancevalue/CorrelationDistance.java
@@ -43,13 +43,13 @@ public abstract class CorrelationDistance<D extends CorrelationDistance<D>> exte
*
* Note: Do NOT use regular expression syntax characters!
*/
- public final static String SEPARATOR = "x";
+ public static final String SEPARATOR = "x";
/**
* The pattern used for correlation distances
*/
- public final static Pattern CORRELATION_DISTANCE_PATTERN = Pattern.compile("\\d+" + Pattern.quote(SEPARATOR) + "\\d+(\\.\\d+)?([eE][-]?\\d+)?");
-
+ public static final Pattern CORRELATION_DISTANCE_PATTERN = Pattern.compile("\\d+" + Pattern.quote(SEPARATOR) + "\\d+(\\.\\d+)?([eE][-]?\\d+)?");
+
/**
* Generated SerialVersionUID.
*/
@@ -110,11 +110,10 @@ public abstract class CorrelationDistance<D extends CorrelationDistance<D>> exte
*/
@Override
public int compareTo(D other) {
- int compare = new Integer(this.correlationValue).compareTo(other.getCorrelationValue());
- if(compare != 0) {
+ int compare = (this.correlationValue < other.getCorrelationValue()) ? -1 : (this.correlationValue > other.getCorrelationValue()) ? +1 : 0;
+ if (compare != 0) {
return compare;
- }
- else {
+ } else {
return Double.compare(this.euclideanValue, other.getEuclideanValue());
}
}
@@ -124,7 +123,7 @@ public abstract class CorrelationDistance<D extends CorrelationDistance<D>> exte
int result;
long temp;
result = correlationValue;
- temp = euclideanValue != +0.0d ? Double.doubleToLongBits(euclideanValue) : 0l;
+ temp = euclideanValue >= Double.MIN_NORMAL ? Double.doubleToLongBits(euclideanValue) : 0L;
result = 29 * result + (int) (temp ^ (temp >>> 32));
return result;
}
@@ -132,17 +131,17 @@ public abstract class CorrelationDistance<D extends CorrelationDistance<D>> exte
@SuppressWarnings("unchecked")
@Override
public boolean equals(Object obj) {
- if(obj == null) {
+ if (obj == null) {
return false;
}
- if(getClass() != obj.getClass()) {
+ if (getClass() != obj.getClass()) {
return false;
}
final CorrelationDistance<D> other = (CorrelationDistance<D>) obj;
- if(this.correlationValue != other.correlationValue) {
+ if (this.correlationValue != other.correlationValue) {
return false;
}
- if(this.euclideanValue != other.euclideanValue) {
+ if (this.euclideanValue != other.euclideanValue) {
return false;
}
return true;
@@ -196,4 +195,4 @@ public abstract class CorrelationDistance<D extends CorrelationDistance<D>> exte
public int externalizableSize() {
return 12;
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancevalue/DoubleDistance.java b/src/de/lmu/ifi/dbs/elki/distance/distancevalue/DoubleDistance.java
index ca84fbfd..acf72c8d 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancevalue/DoubleDistance.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancevalue/DoubleDistance.java
@@ -28,6 +28,8 @@ import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.regex.Pattern;
+import de.lmu.ifi.dbs.elki.utilities.FormatUtil;
+
/**
* Provides a Distance for a double-valued distance.
*
@@ -35,9 +37,24 @@ import java.util.regex.Pattern;
*/
public class DoubleDistance extends NumberDistance<DoubleDistance, Double> {
/**
+ * Zero distance constant
+ */
+ public static final DoubleDistance ZERO_DISTANCE = new DoubleDistance(0.0);
+
+ /**
+ * Infinite distance constant
+ */
+ public static final DoubleDistance INFINITE_DISTANCE = new DoubleDistance(Double.POSITIVE_INFINITY);
+
+ /**
+ * Undefined distance constant
+ */
+ public static final DoubleDistance UNDEFINED_DISTANCE = new DoubleDistance(Double.NaN);
+
+ /**
* The static factory instance
*/
- public final static DoubleDistance FACTORY = new DoubleDistance();
+ public static final DoubleDistance FACTORY = UNDEFINED_DISTANCE;
/**
* The actual value.
@@ -118,7 +135,7 @@ public class DoubleDistance extends NumberDistance<DoubleDistance, Double> {
*/
@Override
public void readExternal(ObjectInput in) throws IOException {
- setValue(in.readDouble());
+ this.value = in.readDouble();
}
/**
@@ -133,16 +150,6 @@ public class DoubleDistance extends NumberDistance<DoubleDistance, Double> {
}
@Override
- public Double getValue() {
- return this.value;
- }
-
- @Override
- void setValue(Double value) {
- this.value = value;
- }
-
- @Override
public double doubleValue() {
return value;
}
@@ -157,25 +164,13 @@ public class DoubleDistance extends NumberDistance<DoubleDistance, Double> {
return Double.compare(this.value, other.value);
}
- @Override
- public boolean equals(Object o) {
- if(this == o) {
- return true;
- }
- if(o == null || getClass() != o.getClass()) {
- return false;
- }
- double delta = Math.abs(value - ((DoubleDistance) o).value);
- return delta < Double.MIN_NORMAL;
- }
-
/**
* An infinite DoubleDistance is based on {@link Double#POSITIVE_INFINITY
* Double.POSITIVE_INFINITY}.
*/
@Override
public DoubleDistance infiniteDistance() {
- return new DoubleDistance(Double.POSITIVE_INFINITY);
+ return INFINITE_DISTANCE;
}
/**
@@ -183,7 +178,7 @@ public class DoubleDistance extends NumberDistance<DoubleDistance, Double> {
*/
@Override
public DoubleDistance nullDistance() {
- return new DoubleDistance(0.0);
+ return ZERO_DISTANCE;
}
/**
@@ -191,7 +186,7 @@ public class DoubleDistance extends NumberDistance<DoubleDistance, Double> {
*/
@Override
public DoubleDistance undefinedDistance() {
- return new DoubleDistance(Double.NaN);
+ return UNDEFINED_DISTANCE;
}
/**
@@ -199,13 +194,12 @@ public class DoubleDistance extends NumberDistance<DoubleDistance, Double> {
*/
@Override
public DoubleDistance parseString(String val) throws IllegalArgumentException {
- if(val.equals(INFINITY_PATTERN)) {
+ if (val.equals(INFINITY_PATTERN)) {
return infiniteDistance();
}
- if(testInputPattern(val)) {
+ if (testInputPattern(val)) {
return new DoubleDistance(Double.parseDouble(val));
- }
- else {
+ } else {
throw new IllegalArgumentException("Given pattern \"" + val + "\" does not match required pattern \"" + requiredInputPattern() + "\"");
}
}
@@ -217,7 +211,7 @@ public class DoubleDistance extends NumberDistance<DoubleDistance, Double> {
@Override
public boolean isNullDistance() {
- return (value == 0.0);
+ return (value <= 0.0);
}
@Override
@@ -231,8 +225,31 @@ public class DoubleDistance extends NumberDistance<DoubleDistance, Double> {
}
@Override
+ public String toString() {
+ return FormatUtil.NF8.format(value);
+ }
+
+ @Override
public int hashCode() {
- long bits = Double.doubleToLongBits(value);
+ final long bits = Double.doubleToLongBits(value);
return (int) (bits ^ (bits >>> 32));
}
-} \ No newline at end of file
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!super.equals(obj)) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ DoubleDistance other = (DoubleDistance) obj;
+ if (Double.doubleToLongBits(value) != Double.doubleToLongBits(other.value)) {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancevalue/FloatDistance.java b/src/de/lmu/ifi/dbs/elki/distance/distancevalue/FloatDistance.java
index adb951ae..2e88b147 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancevalue/FloatDistance.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancevalue/FloatDistance.java
@@ -28,6 +28,8 @@ import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.regex.Pattern;
+import de.lmu.ifi.dbs.elki.utilities.FormatUtil;
+
/**
* Provides a Distance for a float-valued distance.
*
@@ -37,7 +39,7 @@ public class FloatDistance extends NumberDistance<FloatDistance, Float> {
/**
* The static factory instance
*/
- public final static FloatDistance FACTORY = new FloatDistance();
+ public static final FloatDistance FACTORY = new FloatDistance();
/**
* The distance value.
@@ -50,6 +52,21 @@ public class FloatDistance extends NumberDistance<FloatDistance, Float> {
private static final long serialVersionUID = -5702250266358369075L;
/**
+ * Infinite distance.
+ */
+ public static final FloatDistance INFINITE_DISTANCE = new FloatDistance(Float.POSITIVE_INFINITY);
+
+ /**
+ * Zero distance.
+ */
+ public static final FloatDistance ZERO_DISTANCE = new FloatDistance(0.0F);
+
+ /**
+ * Undefined distance.
+ */
+ public static final FloatDistance UNDEFINED_DISTANCE = new FloatDistance(Float.NaN);
+
+ /**
* Empty constructor for serialization purposes.
*/
public FloatDistance() {
@@ -73,12 +90,12 @@ public class FloatDistance extends NumberDistance<FloatDistance, Float> {
@Override
public FloatDistance plus(FloatDistance distance) {
- return new FloatDistance(this.getValue() + distance.getValue());
+ return new FloatDistance(value + distance.value);
}
@Override
public FloatDistance minus(FloatDistance distance) {
- return new FloatDistance(this.getValue() - distance.getValue());
+ return new FloatDistance(value - distance.value);
}
/**
@@ -90,7 +107,7 @@ public class FloatDistance extends NumberDistance<FloatDistance, Float> {
* distance
*/
public FloatDistance times(FloatDistance distance) {
- return new FloatDistance(this.getValue() * distance.getValue());
+ return new FloatDistance(value * distance.value);
}
/**
@@ -102,7 +119,7 @@ public class FloatDistance extends NumberDistance<FloatDistance, Float> {
* value
*/
public FloatDistance times(float lambda) {
- return new FloatDistance(this.getValue() * lambda);
+ return new FloatDistance(value * lambda);
}
/**
@@ -110,7 +127,7 @@ public class FloatDistance extends NumberDistance<FloatDistance, Float> {
*/
@Override
public void writeExternal(ObjectOutput out) throws IOException {
- out.writeFloat(getValue());
+ out.writeFloat(value);
}
/**
@@ -118,7 +135,7 @@ public class FloatDistance extends NumberDistance<FloatDistance, Float> {
*/
@Override
public void readExternal(ObjectInput in) throws IOException {
- setValue(in.readFloat());
+ value = in.readFloat();
}
/**
@@ -133,16 +150,6 @@ public class FloatDistance extends NumberDistance<FloatDistance, Float> {
}
@Override
- public Float getValue() {
- return this.value;
- }
-
- @Override
- void setValue(Float value) {
- this.value = value;
- }
-
- @Override
public double doubleValue() {
return value;
}
@@ -162,25 +169,13 @@ public class FloatDistance extends NumberDistance<FloatDistance, Float> {
return Float.compare(this.value, other.value);
}
- @Override
- public boolean equals(Object o) {
- if(this == o) {
- return true;
- }
- if(o == null || getClass() != o.getClass()) {
- return false;
- }
- float delta = Math.abs(value - ((FloatDistance) o).value);
- return delta < Float.MIN_NORMAL;
- }
-
/**
* An infinite FloatDistance is based on {@link Float#POSITIVE_INFINITY
* Float.POSITIVE_INFINITY}.
*/
@Override
public FloatDistance infiniteDistance() {
- return new FloatDistance(Float.POSITIVE_INFINITY);
+ return INFINITE_DISTANCE;
}
/**
@@ -188,7 +183,7 @@ public class FloatDistance extends NumberDistance<FloatDistance, Float> {
*/
@Override
public FloatDistance nullDistance() {
- return new FloatDistance(0.0F);
+ return ZERO_DISTANCE;
}
/**
@@ -196,7 +191,7 @@ public class FloatDistance extends NumberDistance<FloatDistance, Float> {
*/
@Override
public FloatDistance undefinedDistance() {
- return new FloatDistance(Float.NaN);
+ return UNDEFINED_DISTANCE;
}
/**
@@ -204,35 +199,62 @@ public class FloatDistance extends NumberDistance<FloatDistance, Float> {
*/
@Override
public FloatDistance parseString(String val) throws IllegalArgumentException {
- if(val.equals(INFINITY_PATTERN)) {
+ if (val.equals(INFINITY_PATTERN)) {
return infiniteDistance();
}
- if(DoubleDistance.DOUBLE_PATTERN.matcher(val).matches()) {
+ if (DoubleDistance.DOUBLE_PATTERN.matcher(val).matches()) {
return new FloatDistance(Float.parseFloat(val));
- }
- else {
+ } else {
throw new IllegalArgumentException("Given pattern \"" + val + "\" does not match required pattern \"" + requiredInputPattern() + "\"");
}
}
@Override
public boolean isInfiniteDistance() {
- return Double.isInfinite(value);
+ return Float.isInfinite(value);
}
@Override
public boolean isNullDistance() {
- return (value == 0.0);
+ return (value <= 0.0);
}
@Override
public boolean isUndefinedDistance() {
- return Double.isNaN(value);
+ return Float.isNaN(value);
}
@Override
public Pattern getPattern() {
return DOUBLE_PATTERN;
}
-} \ No newline at end of file
+
+ @Override
+ public String toString() {
+ return FormatUtil.NF8.format(value);
+ }
+
+ @Override
+ public int hashCode() {
+ return Float.floatToIntBits(value);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!super.equals(obj)) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ FloatDistance other = (FloatDistance) obj;
+ if (Float.floatToIntBits(value) != Float.floatToIntBits(other.value)) {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancevalue/IntegerDistance.java b/src/de/lmu/ifi/dbs/elki/distance/distancevalue/IntegerDistance.java
index b99b88c3..d776b3ae 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancevalue/IntegerDistance.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancevalue/IntegerDistance.java
@@ -39,7 +39,7 @@ public class IntegerDistance extends NumberDistance<IntegerDistance, Integer> {
/**
* The static factory instance
*/
- public final static IntegerDistance FACTORY = new IntegerDistance();
+ public static final IntegerDistance FACTORY = new IntegerDistance();
/**
* The distance value
@@ -75,12 +75,12 @@ public class IntegerDistance extends NumberDistance<IntegerDistance, Integer> {
@Override
public IntegerDistance minus(IntegerDistance distance) {
- return new IntegerDistance(this.getValue() - distance.getValue());
+ return new IntegerDistance(this.value - distance.value);
}
@Override
public IntegerDistance plus(IntegerDistance distance) {
- return new IntegerDistance(this.getValue() + distance.getValue());
+ return new IntegerDistance(this.value + distance.value);
}
/**
@@ -88,7 +88,7 @@ public class IntegerDistance extends NumberDistance<IntegerDistance, Integer> {
*/
@Override
public void writeExternal(ObjectOutput out) throws IOException {
- out.writeInt(getValue());
+ out.writeInt(value);
}
/**
@@ -96,7 +96,7 @@ public class IntegerDistance extends NumberDistance<IntegerDistance, Integer> {
*/
@Override
public void readExternal(ObjectInput in) throws IOException {
- setValue(in.readInt());
+ value = in.readInt();
}
/**
@@ -111,16 +111,6 @@ public class IntegerDistance extends NumberDistance<IntegerDistance, Integer> {
}
@Override
- public Integer getValue() {
- return this.value;
- }
-
- @Override
- void setValue(Integer value) {
- this.value = value;
- }
-
- @Override
public double doubleValue() {
return value;
}
@@ -172,16 +162,43 @@ public class IntegerDistance extends NumberDistance<IntegerDistance, Integer> {
@Override
public IntegerDistance parseString(String val) throws IllegalArgumentException {
- if(testInputPattern(val)) {
+ if (testInputPattern(val)) {
return new IntegerDistance(Integer.parseInt(val));
- }
- else {
+ } else {
throw new IllegalArgumentException("Given pattern \"" + val + "\" does not match required pattern \"" + requiredInputPattern() + "\"");
}
}
@Override
+ public String toString() {
+ return Integer.toString(value);
+ }
+
+ @Override
+ public int hashCode() {
+ return value;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!super.equals(obj)) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ IntegerDistance other = (IntegerDistance) obj;
+ if (value != other.value) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
public Pattern getPattern() {
return INTEGER_PATTERN;
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancevalue/NumberDistance.java b/src/de/lmu/ifi/dbs/elki/distance/distancevalue/NumberDistance.java
index a3aff609..9a915c2b 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancevalue/NumberDistance.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancevalue/NumberDistance.java
@@ -23,7 +23,6 @@ package de.lmu.ifi.dbs.elki.distance.distancevalue;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-
/**
* Provides a Distance for a number-valued distance.
*
@@ -42,7 +41,7 @@ public abstract class NumberDistance<D extends NumberDistance<D, N>, N extends N
public NumberDistance() {
super();
}
-
+
/**
* Build a new instance from a double value.
*
@@ -52,66 +51,12 @@ public abstract class NumberDistance<D extends NumberDistance<D, N>, N extends N
public abstract D fromDouble(double val);
/**
- * Returns the hash code for this NumberDistance, which is the hash code of
- * its value.
- *
- * @return the hash code of the value
- */
- @Override
- public int hashCode() {
- return getValue().hashCode();
- }
-
- /**
- * Compares this NumberDistance with the given NumberDistance wrt the
- * represented value.
- * <p/>
- * <code>d1.compareTo(d2)</code> is the same as
- * {@link Double#compare(double,double) Double.compare(d1.value.doubleValue(),
- * d2.value.doubleValue())}. Subclasses may need to overwrite this method if
- * necessary.
- *
- * @param other Other object
- * @return a negative integer, zero, or a positive integer as the value of
- * this NumberDistance is less than, equal to, or greater than the
- * value of the specified NumberDistance.
- */
- @Override
- public int compareTo(D other) {
- return Double.compare(this.doubleValue(), other.doubleValue());
- }
-
- /**
- * Returns a string representation of this NumberDistance.
- *
- * @return the value of this NumberDistance.
- */
- @Override
- public final String toString() {
- return getValue().toString();
- }
-
- /**
- * Returns the value of this NumberDistance.
- *
- * @return the value of this NumberDistance
- */
- public abstract N getValue();
-
- /**
- * Sets the value of this NumberDistance.
- *
- * @param value the value to be set
- */
- abstract void setValue(N value);
-
- /**
* Get the value as double.
*
* @return same result as getValue().doubleValue() but may be more efficient.
*/
public abstract double doubleValue();
-
+
/**
* Get the value as float.
*
@@ -120,7 +65,7 @@ public abstract class NumberDistance<D extends NumberDistance<D, N>, N extends N
public float floatValue() {
return (float) doubleValue();
}
-
+
/**
* Get the value as int.
*
@@ -129,14 +74,14 @@ public abstract class NumberDistance<D extends NumberDistance<D, N>, N extends N
public int intValue() {
return (int) longValue();
}
-
+
/**
* Get the value as long.
*
* @return same result as getValue().longValue() but may be more efficient.
*/
public abstract long longValue();
-
+
/**
* Get the value as short.
*
@@ -145,7 +90,7 @@ public abstract class NumberDistance<D extends NumberDistance<D, N>, N extends N
public short shortValue() {
return (short) longValue();
}
-
+
/**
* Get the value as byte.
*
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancevalue/PCACorrelationDistance.java b/src/de/lmu/ifi/dbs/elki/distance/distancevalue/PCACorrelationDistance.java
index c2e2da6d..79f27055 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancevalue/PCACorrelationDistance.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancevalue/PCACorrelationDistance.java
@@ -38,7 +38,7 @@ public class PCACorrelationDistance extends CorrelationDistance<PCACorrelationDi
/**
* The static factory instance
*/
- public final static PCACorrelationDistance FACTORY = new PCACorrelationDistance();
+ public static final PCACorrelationDistance FACTORY = new PCACorrelationDistance();
/**
* Serial
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancevalue/PreferenceVectorBasedCorrelationDistance.java b/src/de/lmu/ifi/dbs/elki/distance/distancevalue/PreferenceVectorBasedCorrelationDistance.java
index c62cacc4..a5617372 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancevalue/PreferenceVectorBasedCorrelationDistance.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancevalue/PreferenceVectorBasedCorrelationDistance.java
@@ -40,7 +40,7 @@ public class PreferenceVectorBasedCorrelationDistance extends CorrelationDistanc
/**
* The static factory instance
*/
- public final static PreferenceVectorBasedCorrelationDistance FACTORY = new PreferenceVectorBasedCorrelationDistance();
+ public static final PreferenceVectorBasedCorrelationDistance FACTORY = new PreferenceVectorBasedCorrelationDistance();
/**
* Serial version
@@ -202,7 +202,7 @@ public class PreferenceVectorBasedCorrelationDistance extends CorrelationDistanc
*/
@Override
public int externalizableSize() {
- return super.externalizableSize() + 4 + dimensionality * 4;
+ return super.externalizableSize() + 4 + dimensionality << 2;
}
@Override
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancevalue/SubspaceDistance.java b/src/de/lmu/ifi/dbs/elki/distance/distancevalue/SubspaceDistance.java
index 3d4b7f4c..094961b4 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancevalue/SubspaceDistance.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancevalue/SubspaceDistance.java
@@ -41,8 +41,8 @@ public class SubspaceDistance extends AbstractDistance<SubspaceDistance> {
/**
* The static factory instance
*/
- public final static SubspaceDistance FACTORY = new SubspaceDistance();
-
+ public static final SubspaceDistance FACTORY = new SubspaceDistance();
+
/**
* Serial version number.
*/
@@ -124,25 +124,46 @@ public class SubspaceDistance extends AbstractDistance<SubspaceDistance> {
@Override
public int compareTo(SubspaceDistance other) {
int compare = Double.compare(this.subspaceDistance, other.subspaceDistance);
- if(compare != 0) {
+ if (compare != 0) {
return compare;
- }
- else {
+ } else {
return Double.compare(this.affineDistance, other.affineDistance);
}
}
@Override
public int hashCode() {
- int result;
+ final int prime = 31;
+ int result = 1;
long temp;
- temp = subspaceDistance != 0.0d ? Double.doubleToLongBits(subspaceDistance) : 0L;
- result = (int) (temp ^ (temp >>> 32));
- temp = affineDistance != 0.0d ? Double.doubleToLongBits(affineDistance) : 0L;
- result = 29 * result + (int) (temp ^ (temp >>> 32));
+ temp = Double.doubleToLongBits(subspaceDistance);
+ result = prime * result + (int) (temp ^ (temp >>> 32));
+ temp = Double.doubleToLongBits(affineDistance);
+ result = prime * result + (int) (temp ^ (temp >>> 32));
return result;
}
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!super.equals(obj)) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ SubspaceDistance other = (SubspaceDistance) obj;
+ if (Double.doubleToLongBits(affineDistance) != Double.doubleToLongBits(other.affineDistance)) {
+ return false;
+ }
+ if (Double.doubleToLongBits(subspaceDistance) != Double.doubleToLongBits(other.subspaceDistance)) {
+ return false;
+ }
+ return true;
+ }
+
/**
* Returns the value of the subspace distance.
*
@@ -199,14 +220,13 @@ public class SubspaceDistance extends AbstractDistance<SubspaceDistance> {
@Override
public SubspaceDistance parseString(String val) throws IllegalArgumentException {
- if(val.equals(INFINITY_PATTERN)) {
+ if (val.equals(INFINITY_PATTERN)) {
return infiniteDistance();
}
- if(testInputPattern(val)) {
+ if (testInputPattern(val)) {
String[] values = SEPARATOR.split(val);
return new SubspaceDistance(Double.parseDouble(values[0]), Double.parseDouble(values[1]));
- }
- else {
+ } else {
throw new IllegalArgumentException("Given pattern \"" + val + "\" does not match required pattern \"" + requiredInputPattern() + "\"");
}
}
@@ -225,4 +245,4 @@ public class SubspaceDistance extends AbstractDistance<SubspaceDistance> {
public SubspaceDistance undefinedDistance() {
return new SubspaceDistance(Double.NaN, Double.NaN);
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/distance/distancevalue/package-info.java b/src/de/lmu/ifi/dbs/elki/distance/distancevalue/package-info.java
index 30fcdf35..f302b854 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/distancevalue/package-info.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/distancevalue/package-info.java
@@ -5,6 +5,9 @@
* <p>Distances follow a factory pattern. Usually, a class will have a static instance
* called <code>FACTORY</code> that can be used to obtain e.g. infinity or zero distances
* as well as parse a string value into a new distance value.</p>
+ *
+ * @apiviz.exclude java.io.*
+ * @apiviz.exclude java.lang.*
*/
/*
This file is part of ELKI:
@@ -28,4 +31,4 @@ GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package de.lmu.ifi.dbs.elki.distance.distancevalue; \ No newline at end of file
+package de.lmu.ifi.dbs.elki.distance.distancevalue;
diff --git a/src/de/lmu/ifi/dbs/elki/distance/similarityfunction/AbstractIndexBasedSimilarityFunction.java b/src/de/lmu/ifi/dbs/elki/distance/similarityfunction/AbstractIndexBasedSimilarityFunction.java
index 72f44e4d..039f7f2b 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/similarityfunction/AbstractIndexBasedSimilarityFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/similarityfunction/AbstractIndexBasedSimilarityFunction.java
@@ -53,7 +53,7 @@ public abstract class AbstractIndexBasedSimilarityFunction<O, I extends Index, R
* Key: {@code -similarityfunction.preprocessor}
* </p>
*/
- public static final OptionID INDEX_ID = OptionID.getOrCreateOptionID("similarityfunction.preprocessor", "Preprocessor to use.");
+ public static final OptionID INDEX_ID = new OptionID("similarityfunction.preprocessor", "Preprocessor to use.");
/**
* Parameter to specify the preprocessor to be used.
@@ -127,7 +127,7 @@ public abstract class AbstractIndexBasedSimilarityFunction<O, I extends Index, R
*
* @apiviz.exclude
*/
- public static abstract class Parameterizer<F extends IndexFactory<?, ?>> extends AbstractParameterizer {
+ public abstract static class Parameterizer<F extends IndexFactory<?, ?>> extends AbstractParameterizer {
/**
* The index factory we use.
*/
diff --git a/src/de/lmu/ifi/dbs/elki/distance/similarityfunction/FractionalSharedNearestNeighborSimilarityFunction.java b/src/de/lmu/ifi/dbs/elki/distance/similarityfunction/FractionalSharedNearestNeighborSimilarityFunction.java
index 32653173..a28790b9 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/similarityfunction/FractionalSharedNearestNeighborSimilarityFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/similarityfunction/FractionalSharedNearestNeighborSimilarityFunction.java
@@ -26,6 +26,7 @@ package de.lmu.ifi.dbs.elki.distance.similarityfunction;
import de.lmu.ifi.dbs.elki.database.ids.ArrayDBIDs;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
@@ -96,7 +97,7 @@ public class FractionalSharedNearestNeighborSimilarityFunction<O> extends Abstra
DBIDIter iter1 = neighbors1.iter();
DBIDIter iter2 = neighbors2.iter();
while(iter1.valid() && iter2.valid()) {
- final int comp = iter1.compareDBID(iter2);
+ final int comp = DBIDUtil.compare(iter1, iter2);
if(comp == 0) {
intersection++;
iter1.advance();
diff --git a/src/de/lmu/ifi/dbs/elki/distance/similarityfunction/InvertedDistanceSimilarityFunction.java b/src/de/lmu/ifi/dbs/elki/distance/similarityfunction/InvertedDistanceSimilarityFunction.java
index ad193d9e..2a97a5a3 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/similarityfunction/InvertedDistanceSimilarityFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/similarityfunction/InvertedDistanceSimilarityFunction.java
@@ -49,7 +49,7 @@ public class InvertedDistanceSimilarityFunction<O> extends AbstractPrimitiveSimi
* {@link de.lmu.ifi.dbs.elki.distance.distancefunction.EuclideanDistanceFunction}
* </p>
*/
- public static final OptionID DISTANCE_FUNCTION_ID = OptionID.getOrCreateOptionID("adapter.distancefunction", "Distance function to derive the similarity between database objects from.");
+ public static final OptionID DISTANCE_FUNCTION_ID = new OptionID("adapter.distancefunction", "Distance function to derive the similarity between database objects from.");
/**
* Holds the similarity function.
diff --git a/src/de/lmu/ifi/dbs/elki/distance/similarityfunction/SharedNearestNeighborSimilarityFunction.java b/src/de/lmu/ifi/dbs/elki/distance/similarityfunction/SharedNearestNeighborSimilarityFunction.java
index 98117008..3e757778 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/similarityfunction/SharedNearestNeighborSimilarityFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/similarityfunction/SharedNearestNeighborSimilarityFunction.java
@@ -25,6 +25,7 @@ package de.lmu.ifi.dbs.elki.distance.similarityfunction;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.ids.SetDBIDs;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
@@ -72,7 +73,7 @@ public class SharedNearestNeighborSimilarityFunction<O> extends AbstractIndexBas
DBIDIter iter1 = neighbors1.iter();
DBIDIter iter2 = neighbors2.iter();
while(iter1.valid() && iter2.valid()) {
- final int comp = iter1.compareDBID(iter2);
+ final int comp = DBIDUtil.compare(iter1, iter2);
if(comp == 0) {
intersection++;
iter1.advance();
diff --git a/src/de/lmu/ifi/dbs/elki/distance/similarityfunction/kernel/FooKernelFunction.java b/src/de/lmu/ifi/dbs/elki/distance/similarityfunction/kernel/FooKernelFunction.java
index 093cf652..be9ba959 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/similarityfunction/kernel/FooKernelFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/similarityfunction/kernel/FooKernelFunction.java
@@ -43,7 +43,7 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
*
* @author Simon Paradies
*/
-public class FooKernelFunction extends AbstractPrimitiveDistanceFunction<NumberVector<?, ?>, DoubleDistance> implements PrimitiveSimilarityFunction<NumberVector<?, ?>, DoubleDistance> {
+public class FooKernelFunction extends AbstractPrimitiveDistanceFunction<NumberVector<?>, DoubleDistance> implements PrimitiveSimilarityFunction<NumberVector<?>, DoubleDistance> {
/**
* The default max_degree.
*/
@@ -52,7 +52,7 @@ public class FooKernelFunction extends AbstractPrimitiveDistanceFunction<NumberV
/**
* Parameter for the maximum degree
*/
- public static final OptionID MAX_DEGREE_ID = OptionID.getOrCreateOptionID("fookernel.max_degree", "The max degree of the" + FooKernelFunction.class.getSimpleName() + ". Default: " + DEFAULT_MAX_DEGREE);
+ public static final OptionID MAX_DEGREE_ID = new OptionID("fookernel.max_degree", "The max degree of the" + FooKernelFunction.class.getSimpleName() + ". Default: " + DEFAULT_MAX_DEGREE);
/**
* Degree of the polynomial kernel function
@@ -78,25 +78,25 @@ public class FooKernelFunction extends AbstractPrimitiveDistanceFunction<NumberV
* an instance of {@link DoubleDistance DoubleDistance}.
*/
@Override
- public DoubleDistance similarity(final NumberVector<?, ?> o1, final NumberVector<?, ?> o2) {
+ public DoubleDistance similarity(final NumberVector<?> o1, final NumberVector<?> o2) {
if(o1.getDimensionality() != o2.getDimensionality()) {
throw new IllegalArgumentException("Different dimensionality of FeatureVectors\n first argument: " + o1.toString() + "\n second argument: " + o2.toString());
}
double sim = 0.0;
// iterate over differently powered dimensions
- for(int degree = 1; degree <= max_degree; degree++) {
+ for(int degree = 0; degree < max_degree; degree++) {
sim += Math.pow(o1.doubleValue(degree) * o2.doubleValue(degree), degree);
}
return new DoubleDistance(sim);
}
@Override
- public DoubleDistance distance(final NumberVector<?, ?> fv1, final NumberVector<?, ?> fv2) {
+ public DoubleDistance distance(final NumberVector<?> fv1, final NumberVector<?> fv2) {
return new DoubleDistance(Math.sqrt(similarity(fv1, fv1).doubleValue() + similarity(fv2, fv2).doubleValue() - 2 * similarity(fv1, fv2).doubleValue()));
}
@Override
- public VectorFieldTypeInformation<? super NumberVector<?, ?>> getInputTypeRestriction() {
+ public VectorFieldTypeInformation<? super NumberVector<?>> getInputTypeRestriction() {
return TypeUtil.NUMBER_VECTOR_FIELD;
}
@@ -106,7 +106,7 @@ public class FooKernelFunction extends AbstractPrimitiveDistanceFunction<NumberV
}
@Override
- public <T extends NumberVector<?, ?>> DistanceSimilarityQuery<T, DoubleDistance> instantiate(Relation<T> database) {
+ public <T extends NumberVector<?>> DistanceSimilarityQuery<T, DoubleDistance> instantiate(Relation<T> database) {
return new PrimitiveDistanceSimilarityQuery<T, DoubleDistance>(database, this, this);
}
diff --git a/src/de/lmu/ifi/dbs/elki/distance/similarityfunction/kernel/KernelMatrix.java b/src/de/lmu/ifi/dbs/elki/distance/similarityfunction/kernel/KernelMatrix.java
index a8b6d8fc..50dd0b4f 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/similarityfunction/kernel/KernelMatrix.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/similarityfunction/kernel/KernelMatrix.java
@@ -69,7 +69,7 @@ public class KernelMatrix {
* @deprecated ID mapping is not reliable!
*/
@Deprecated
- public <O extends FeatureVector<O, ?>> KernelMatrix(final PrimitiveSimilarityFunction<? super O, DoubleDistance> kernelFunction, final Relation<? extends O> database) {
+ public <O extends FeatureVector<?>> KernelMatrix(final PrimitiveSimilarityFunction<? super O, DoubleDistance> kernelFunction, final Relation<? extends O> database) {
this(kernelFunction, database, DBIDUtil.ensureArray(database.getDBIDs()));
}
@@ -80,7 +80,7 @@ public class KernelMatrix {
* @param database the database that holds the objects
* @param ids the IDs of those objects for which the kernel matrix is computed
*/
- public <O extends FeatureVector<O, ?>> KernelMatrix(final PrimitiveSimilarityFunction<? super O, DoubleDistance> kernelFunction, final Relation<? extends O> database, final ArrayDBIDs ids) {
+ public <O extends FeatureVector<?>> KernelMatrix(final PrimitiveSimilarityFunction<? super O, DoubleDistance> kernelFunction, final Relation<? extends O> database, final ArrayDBIDs ids) {
LoggingUtil.logExpensive(Level.FINER, "Computing kernel matrix");
kernel = new Matrix(ids.size(), ids.size());
double value;
diff --git a/src/de/lmu/ifi/dbs/elki/distance/similarityfunction/kernel/LinearKernelFunction.java b/src/de/lmu/ifi/dbs/elki/distance/similarityfunction/kernel/LinearKernelFunction.java
index 6cde55a6..ffd2d0e3 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/similarityfunction/kernel/LinearKernelFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/similarityfunction/kernel/LinearKernelFunction.java
@@ -40,7 +40,7 @@ import de.lmu.ifi.dbs.elki.distance.similarityfunction.AbstractPrimitiveSimilari
* @author Simon Paradies
* @param <O> vector type
*/
-public class LinearKernelFunction<O extends NumberVector<?, ?>> extends AbstractPrimitiveSimilarityFunction<O, DoubleDistance> implements PrimitiveDistanceFunction<O, DoubleDistance> {
+public class LinearKernelFunction<O extends NumberVector<?>> extends AbstractPrimitiveSimilarityFunction<O, DoubleDistance> implements PrimitiveDistanceFunction<O, DoubleDistance> {
/**
* Provides a linear Kernel function that computes a similarity between the
* two vectors V1 and V2 defined by V1^T*V2.
@@ -51,7 +51,7 @@ public class LinearKernelFunction<O extends NumberVector<?, ?>> extends Abstract
/**
* Provides a linear Kernel function that computes a similarity between the
- * two feature vectors V1 and V2 definded by V1^T*V2
+ * two feature vectors V1 and V2 definded by V1^T*V2.
*
* @param o1 first feature vector
* @param o2 second feature vector
@@ -64,7 +64,7 @@ public class LinearKernelFunction<O extends NumberVector<?, ?>> extends Abstract
throw new IllegalArgumentException("Different dimensionality of Feature-Vectors" + "\n first argument: " + o1.toString() + "\n second argument: " + o2.toString());
}
double sim = 0;
- for(int i = 1; i <= o1.getDimensionality(); i++) {
+ for(int i = 0; i < o1.getDimensionality(); i++) {
sim += o1.doubleValue(i) * o2.doubleValue(i);
}
return new DoubleDistance(sim);
diff --git a/src/de/lmu/ifi/dbs/elki/distance/similarityfunction/kernel/PolynomialKernelFunction.java b/src/de/lmu/ifi/dbs/elki/distance/similarityfunction/kernel/PolynomialKernelFunction.java
index e32f5b45..1a7f97f5 100644
--- a/src/de/lmu/ifi/dbs/elki/distance/similarityfunction/kernel/PolynomialKernelFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/distance/similarityfunction/kernel/PolynomialKernelFunction.java
@@ -43,19 +43,19 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.DoubleParameter;
*
* @author Simon Paradies
*/
-public class PolynomialKernelFunction extends AbstractPrimitiveDistanceFunction<NumberVector<?, ?>, DoubleDistance> implements PrimitiveSimilarityFunction<NumberVector<?, ?>, DoubleDistance> {
+public class PolynomialKernelFunction extends AbstractPrimitiveDistanceFunction<NumberVector<?>, DoubleDistance> implements PrimitiveSimilarityFunction<NumberVector<?>, DoubleDistance> {
/**
* The default degree.
*/
public static final double DEFAULT_DEGREE = 2.0;
/**
- * Degree parameter
+ * Degree parameter.
*/
- public static final OptionID DEGREE_ID = OptionID.getOrCreateOptionID("kernel.degree", "The degree of the polynomial kernel function. Default: " + DEFAULT_DEGREE);
+ public static final OptionID DEGREE_ID = new OptionID("kernel.degree", "The degree of the polynomial kernel function. Default: " + DEFAULT_DEGREE);
/**
- * Degree of the polynomial kernel function
+ * Degree of the polynomial kernel function.
*/
private double degree = 0.0;
@@ -78,25 +78,25 @@ public class PolynomialKernelFunction extends AbstractPrimitiveDistanceFunction<
* instance of {@link DoubleDistance DoubleDistance}.
*/
@Override
- public DoubleDistance similarity(NumberVector<?, ?> o1, NumberVector<?, ?> o2) {
+ public DoubleDistance similarity(NumberVector<?> o1, NumberVector<?> o2) {
if(o1.getDimensionality() != o2.getDimensionality()) {
throw new IllegalArgumentException("Different dimensionality of Feature-Vectors" + "\n first argument: " + o1.toString() + "\n second argument: " + o2.toString());
}
double sim = 0;
- for(int i = 1; i <= o1.getDimensionality(); i++) {
+ for(int i = 0; i < o1.getDimensionality(); i++) {
sim += o1.doubleValue(i) * o2.doubleValue(i);
}
return new DoubleDistance(Math.pow(sim, degree));
}
@Override
- public DoubleDistance distance(final NumberVector<?, ?> fv1, final NumberVector<?, ?> fv2) {
+ public DoubleDistance distance(final NumberVector<?> fv1, final NumberVector<?> fv2) {
return new DoubleDistance(Math.sqrt(similarity(fv1, fv1).doubleValue() + similarity(fv2, fv2).doubleValue() - 2 * similarity(fv1, fv2).doubleValue()));
}
@Override
- public VectorFieldTypeInformation<? super NumberVector<?, ?>> getInputTypeRestriction() {
+ public VectorFieldTypeInformation<? super NumberVector<?>> getInputTypeRestriction() {
return TypeUtil.NUMBER_VECTOR_FIELD;
}
@@ -106,7 +106,7 @@ public class PolynomialKernelFunction extends AbstractPrimitiveDistanceFunction<
}
@Override
- public <T extends NumberVector<?, ?>> DistanceSimilarityQuery<T, DoubleDistance> instantiate(Relation<T> database) {
+ public <T extends NumberVector<?>> DistanceSimilarityQuery<T, DoubleDistance> instantiate(Relation<T> database) {
return new PrimitiveDistanceSimilarityQuery<T, DoubleDistance>(database, this, this);
}
@@ -118,6 +118,9 @@ public class PolynomialKernelFunction extends AbstractPrimitiveDistanceFunction<
* @apiviz.exclude
*/
public static class Parameterizer extends AbstractParameterizer {
+ /**
+ * Degree of the polynomial kernel function.
+ */
protected double degree = 0;
@Override
diff --git a/src/de/lmu/ifi/dbs/elki/evaluation/AutomaticEvaluation.java b/src/de/lmu/ifi/dbs/elki/evaluation/AutomaticEvaluation.java
index 888777a5..65a4771e 100644
--- a/src/de/lmu/ifi/dbs/elki/evaluation/AutomaticEvaluation.java
+++ b/src/de/lmu/ifi/dbs/elki/evaluation/AutomaticEvaluation.java
@@ -48,6 +48,8 @@ import de.lmu.ifi.dbs.elki.utilities.scaling.LinearScaling;
*
* @author Erich Schubert
*
+ * @apiviz.landmark
+ *
* @apiviz.uses OutlierResult
* @apiviz.uses Clustering
* @apiviz.composedOf OutlierROCCurve
@@ -60,7 +62,7 @@ public class AutomaticEvaluation implements Evaluator {
/**
* Class logger
*/
- private static final Logging logger = Logging.getLogger(AutomaticEvaluation.class);
+ private static final Logging LOG = Logging.getLogger(AutomaticEvaluation.class);
@Override
public void processNewResult(HierarchicalResult baseResult, Result newResult) {
@@ -70,14 +72,14 @@ public class AutomaticEvaluation implements Evaluator {
protected void autoEvaluateOutliers(HierarchicalResult baseResult, Result newResult) {
Collection<OutlierResult> outliers = ResultUtil.filterResults(newResult, OutlierResult.class);
- if(logger.isDebugging()) {
- logger.debug("Number of new outlier results: " + outliers.size());
+ if(LOG.isDebugging()) {
+ LOG.debug("Number of new outlier results: " + outliers.size());
}
if(outliers.size() > 0) {
ResultUtil.ensureClusteringResult(ResultUtil.findDatabase(baseResult), baseResult);
Collection<Clustering<?>> clusterings = ResultUtil.filterResults(baseResult, Clustering.class);
if(clusterings.size() == 0) {
- logger.warning("Could not find a clustering result, even after running 'ensureClusteringResult'?!?");
+ LOG.warning("Could not find a clustering result, even after running 'ensureClusteringResult'?!?");
return;
}
Clustering<?> basec = clusterings.iterator().next();
@@ -96,21 +98,21 @@ public class AutomaticEvaluation implements Evaluator {
}
}
if(label == null) {
- logger.warning("Could not evaluate outlier results, as I could not find a minority label.");
+ LOG.warning("Could not evaluate outlier results, as I could not find a minority label.");
return;
}
if(min == 1) {
- logger.warning("The minority class label had a single object. Try using 'ClassLabelFilter' to identify the class label column.");
+ LOG.warning("The minority class label had a single object. Try using 'ClassLabelFilter' to identify the class label column.");
}
if(min > 0.05 * total) {
- logger.warning("The minority class I discovered (labeled '" + label + "') has " + (min * 100. / total) + "% of objects. Outlier classes should be more rare!");
+ LOG.warning("The minority class I discovered (labeled '" + label + "') has " + (min * 100. / total) + "% of objects. Outlier classes should be more rare!");
}
- logger.verbose("Evaluating using minority class: " + label);
+ LOG.verbose("Evaluating using minority class: " + label);
Pattern pat = Pattern.compile("^" + Pattern.quote(label) + "$");
// Compute ROC curve
new OutlierROCCurve(pat).processNewResult(baseResult, newResult);
// Compute Precision at k
- new OutlierPrecisionAtKCurve(pat, min * 2).processNewResult(baseResult, newResult);
+ new OutlierPrecisionAtKCurve(pat, min << 1).processNewResult(baseResult, newResult);
// Compute ROC curve
new OutlierPrecisionRecallCurve(pat).processNewResult(baseResult, newResult);
// Compute outlier histogram
@@ -120,8 +122,8 @@ public class AutomaticEvaluation implements Evaluator {
protected void autoEvaluateClusterings(HierarchicalResult baseResult, Result newResult) {
Collection<Clustering<?>> clusterings = ResultUtil.filterResults(newResult, Clustering.class);
- if(logger.isDebugging()) {
- logger.warning("Number of new clustering results: " + clusterings.size());
+ if(LOG.isDebugging()) {
+ LOG.warning("Number of new clustering results: " + clusterings.size());
}
for (Iterator<Clustering<?>> c = clusterings.iterator(); c.hasNext();) {
Clustering<?> test = c.next();
diff --git a/src/de/lmu/ifi/dbs/elki/evaluation/NoAutomaticEvaluation.java b/src/de/lmu/ifi/dbs/elki/evaluation/NoAutomaticEvaluation.java
index 453a337f..efe3d1ac 100644
--- a/src/de/lmu/ifi/dbs/elki/evaluation/NoAutomaticEvaluation.java
+++ b/src/de/lmu/ifi/dbs/elki/evaluation/NoAutomaticEvaluation.java
@@ -37,7 +37,7 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
public class NoAutomaticEvaluation implements Evaluator {
@Override
public void processNewResult(HierarchicalResult baseResult, Result newResult) {
- return;
+ // Noop.
}
/**
diff --git a/src/de/lmu/ifi/dbs/elki/evaluation/clustering/ClusterContingencyTable.java b/src/de/lmu/ifi/dbs/elki/evaluation/clustering/ClusterContingencyTable.java
index 70f6e6ce..c77c7438 100644
--- a/src/de/lmu/ifi/dbs/elki/evaluation/clustering/ClusterContingencyTable.java
+++ b/src/de/lmu/ifi/dbs/elki/evaluation/clustering/ClusterContingencyTable.java
@@ -166,7 +166,7 @@ public class ClusterContingencyTable {
final Cluster<?> c2 = it2.next();
int count = 0;
for(DBIDIter iter = c2.getIDs().iter(); iter.valid(); iter.advance()) {
- if(ids.contains(iter.getDBID())) {
+ if(ids.contains(iter)) {
count++;
}
}
@@ -181,7 +181,7 @@ public class ClusterContingencyTable {
@Override
public String toString() {
- StringBuffer buf = new StringBuffer();
+ StringBuilder buf = new StringBuilder();
if(contingency != null) {
for(int i1 = 0; i1 < size1 + 2; i1++) {
if(i1 >= size1) {
@@ -191,9 +191,9 @@ public class ClusterContingencyTable {
if(i2 >= size2) {
buf.append("| ");
}
- buf.append(contingency[i1][i2]).append(" ");
+ buf.append(contingency[i1][i2]).append(' ');
}
- buf.append("\n");
+ buf.append('\n');
}
}
// if(pairconfuse != null) {
diff --git a/src/de/lmu/ifi/dbs/elki/evaluation/clustering/EvaluateClustering.java b/src/de/lmu/ifi/dbs/elki/evaluation/clustering/EvaluateClustering.java
index a57fa188..2cbd1024 100644
--- a/src/de/lmu/ifi/dbs/elki/evaluation/clustering/EvaluateClustering.java
+++ b/src/de/lmu/ifi/dbs/elki/evaluation/clustering/EvaluateClustering.java
@@ -57,23 +57,23 @@ public class EvaluateClustering implements Evaluator {
/**
* Logger for debug output.
*/
- protected static final Logging logger = Logging.getLogger(EvaluateClustering.class);
+ private static final Logging LOG = Logging.getLogger(EvaluateClustering.class);
/**
* Parameter to obtain the reference clustering. Defaults to a flat label
* clustering.
*/
- public static final OptionID REFERENCE_ID = OptionID.getOrCreateOptionID("paircounting.reference", "Reference clustering to compare with. Defaults to a by-label clustering.");
+ public static final OptionID REFERENCE_ID = new OptionID("paircounting.reference", "Reference clustering to compare with. Defaults to a by-label clustering.");
/**
* Parameter flag for special noise handling.
*/
- public static final OptionID NOISE_ID = OptionID.getOrCreateOptionID("paircounting.noisespecial", "Use special handling for noise clusters.");
+ public static final OptionID NOISE_ID = new OptionID("paircounting.noisespecial", "Use special handling for noise clusters.");
/**
* Parameter flag to disable self-pairing
*/
- public static final OptionID SELFPAIR_ID = OptionID.getOrCreateOptionID("paircounting.selfpair", "Enable self-pairing for cluster comparison.");
+ public static final OptionID SELFPAIR_ID = new OptionID("paircounting.selfpair", "Enable self-pairing for cluster comparison.");
/**
* Reference algorithm.
@@ -114,7 +114,7 @@ public class EvaluateClustering implements Evaluator {
// Compute the reference clustering
Clustering<?> refc = null;
// Try to find an existing reference clustering (globally)
- if(refc == null) {
+ {
Collection<Clustering<?>> cs = ResultUtil.filterResults(baseResult, Clustering.class);
for(Clustering<?> test : cs) {
if(isReferenceResult(test)) {
@@ -134,20 +134,20 @@ public class EvaluateClustering implements Evaluator {
}
}
if(refc == null) {
- logger.debug("Generating a new reference clustering.");
+ LOG.debug("Generating a new reference clustering.");
Result refres = referencealg.run(db);
List<Clustering<?>> refcrs = ResultUtil.getClusteringResults(refres);
if(refcrs.size() == 0) {
- logger.warning("Reference algorithm did not return a clustering result!");
+ LOG.warning("Reference algorithm did not return a clustering result!");
return;
}
if(refcrs.size() > 1) {
- logger.warning("Reference algorithm returned more than one result!");
+ LOG.warning("Reference algorithm returned more than one result!");
}
refc = refcrs.get(0);
}
else {
- logger.debug("Using existing clustering: " + refc.getLongName() + " " + refc.getShortName());
+ LOG.debug("Using existing clustering: " + refc.getLongName() + " " + refc.getShortName());
}
for(Clustering<?> c : crs) {
if(c == refc) {
diff --git a/src/de/lmu/ifi/dbs/elki/evaluation/clustering/PairCounting.java b/src/de/lmu/ifi/dbs/elki/evaluation/clustering/PairCounting.java
index 500588fa..903a0bee 100644
--- a/src/de/lmu/ifi/dbs/elki/evaluation/clustering/PairCounting.java
+++ b/src/de/lmu/ifi/dbs/elki/evaluation/clustering/PairCounting.java
@@ -1,4 +1,5 @@
package de.lmu.ifi.dbs.elki.evaluation.clustering;
+
/*
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
@@ -32,6 +33,14 @@ import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
*/
public class PairCounting {
/**
+ * This is the maximum size this implementation can support.
+ *
+ * Note: this is approximately sqrt(2) * Integer.MAX_VALUE as long = 63 bits
+ * (+unused sign bit), int = 31 bits (+unused sign bit)
+ */
+ public static final long MAX_SIZE = (long) Math.floor(Math.sqrt(Long.MAX_VALUE));
+
+ /**
* Pair counting confusion matrix (flat: inBoth, inFirst, inSecond, inNone)
*/
protected long[] pairconfuse = null;
@@ -103,9 +112,9 @@ public class PairCounting {
// The official sum
int tsize = table.contingency[table.size1][table.size2];
if(table.contingency[table.size1][table.size2 + 1] != tsize || table.contingency[table.size1 + 1][table.size2] != tsize) {
- LoggingUtil.warning("PairCounting F-Measure is not well defined for overlapping and incomplete clusterings.");
+ LoggingUtil.warning("PairCounting F-Measure is not well defined for overlapping and incomplete clusterings. The number of elements are: " + table.contingency[table.size1][table.size2 + 1] + " != " + table.contingency[table.size1 + 1][table.size2] + " elements.");
}
- if(tsize >= Math.sqrt(Long.MAX_VALUE)) {
+ if(tsize < 0 || tsize >= MAX_SIZE) {
LoggingUtil.warning("Your data set size probably is too big for this implementation, which uses only long precision.");
}
if(table.selfPairing) {
@@ -222,4 +231,4 @@ public class PairCounting {
public long mirkin() {
return 2 * (pairconfuse[1] + pairconfuse[2]);
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/evaluation/clustering/SetMatchingPurity.java b/src/de/lmu/ifi/dbs/elki/evaluation/clustering/SetMatchingPurity.java
index 1691068a..0cc79b45 100644
--- a/src/de/lmu/ifi/dbs/elki/evaluation/clustering/SetMatchingPurity.java
+++ b/src/de/lmu/ifi/dbs/elki/evaluation/clustering/SetMatchingPurity.java
@@ -76,7 +76,7 @@ public class SetMatchingPurity {
// / numobj));
}
smPurity += (precisionMax / numobj);
- smFFirst += (table.contingency[i1][table.size2] / table.contingency[table.size1][table.size2]) * fMax;
+ smFFirst += (table.contingency[i1][table.size2] / (double) table.contingency[table.size1][table.size2]) * fMax;
// * contingency[i1][size2]/numobj;
}
}
@@ -93,7 +93,7 @@ public class SetMatchingPurity {
// / numobj));
}
smInversePurity += (recallMax / numobj);
- smFSecond += (table.contingency[table.size1][i2] / table.contingency[table.size1][table.size2]) * fMax;
+ smFSecond += (table.contingency[table.size1][i2] / (double) table.contingency[table.size1][table.size2]) * fMax;
// * contingency[i1][size2]/numobj;
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/evaluation/clustering/pairsegments/Segment.java b/src/de/lmu/ifi/dbs/elki/evaluation/clustering/pairsegments/Segment.java
index 00700385..23621d83 100644
--- a/src/de/lmu/ifi/dbs/elki/evaluation/clustering/pairsegments/Segment.java
+++ b/src/de/lmu/ifi/dbs/elki/evaluation/clustering/pairsegments/Segment.java
@@ -1,4 +1,5 @@
package de.lmu.ifi.dbs.elki.evaluation.clustering.pairsegments;
+
/*
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
@@ -88,8 +89,8 @@ public class Segment implements Comparable<Segment> {
* @return true when unclustered in at least one dimension.
*/
public boolean isUnpaired() {
- for(int id : clusterIds) {
- if(id == UNCLUSTERED) {
+ for (int id : clusterIds) {
+ if (id == UNCLUSTERED) {
return true;
}
}
@@ -103,8 +104,8 @@ public class Segment implements Comparable<Segment> {
* @return true when unclustered everywhere
*/
public boolean isNone() {
- for(int id : clusterIds) {
- if(id != UNCLUSTERED) {
+ for (int id : clusterIds) {
+ if (id != UNCLUSTERED) {
return false;
}
}
@@ -118,8 +119,8 @@ public class Segment implements Comparable<Segment> {
* @return clustering id or -1
*/
public int getUnpairedClusteringIndex() {
- for(int index = 0; index < clusterIds.length; index++) {
- if(clusterIds[index] == UNCLUSTERED) {
+ for (int index = 0; index < clusterIds.length; index++) {
+ if (clusterIds[index] == UNCLUSTERED) {
return index;
}
}
@@ -137,7 +138,10 @@ public class Segment implements Comparable<Segment> {
@Override
public boolean equals(Object obj) {
- if(!(Segment.class.isInstance(obj))) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(Segment.class.isInstance(obj))) {
return false;
}
Segment other = (Segment) obj;
@@ -151,16 +155,15 @@ public class Segment implements Comparable<Segment> {
@Override
public int compareTo(Segment sid) {
- for(int i = 0; i < clusterIds.length; i++) {
+ for (int i = 0; i < clusterIds.length; i++) {
final int a = this.clusterIds[i];
final int b = sid.clusterIds[i];
- if(a != b) {
- if(a * b > 0) {
+ if (a != b) {
+ if (a * b > 0) {
// Regular comparison
return (a < b) ? -1 : +1;
// return (a < b) ? +1 : -1;
- }
- else {
+ } else {
// Inverse, to sort negative last
return (a < b) ? +1 : -1;
}
@@ -168,4 +171,4 @@ public class Segment implements Comparable<Segment> {
}
return 0;
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/evaluation/clustering/pairsegments/Segments.java b/src/de/lmu/ifi/dbs/elki/evaluation/clustering/pairsegments/Segments.java
index ca352367..507c2ebb 100644
--- a/src/de/lmu/ifi/dbs/elki/evaluation/clustering/pairsegments/Segments.java
+++ b/src/de/lmu/ifi/dbs/elki/evaluation/clustering/pairsegments/Segments.java
@@ -31,7 +31,6 @@ import java.util.TreeMap;
import de.lmu.ifi.dbs.elki.data.Cluster;
import de.lmu.ifi.dbs.elki.data.Clustering;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
@@ -69,17 +68,23 @@ import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
* In: Proc. 28th International Conference on Data Engineering (ICDE) 2012
* </p>
*
+ * <p>
+ * Details on the experimental setup can be found at: <a
+ * href="http://elki.dbs.ifi.lmu.de/wiki/Examples/ClusterEvaluation"
+ * >wiki/Examples/ClusterEvaluation</a>
+ * </p>
+ *
* @author Sascha Goldhofer
* @author Erich Schubert
*
* @apiviz.composedOf Segment
*/
-@Reference(title = "Evaluation of Clusterings – Metrics and Visual Support", authors = "Elke Achtert, Sascha Goldhofer, Hans-Peter Kriegel, Erich Schubert, Arthur Zimek", booktitle = "Proc. 28th International Conference on Data Engineering (ICDE) 2012", url = "http://elki.dbs.ifi.lmu.de/wiki/PairSegments")
+@Reference(title = "Evaluation of Clusterings – Metrics and Visual Support", authors = "Elke Achtert, Sascha Goldhofer, Hans-Peter Kriegel, Erich Schubert, Arthur Zimek", booktitle = "Proc. 28th International Conference on Data Engineering (ICDE) 2012", url = "http://dx.doi.org/10.1109/ICDE.2012.128")
public class Segments extends BasicResult implements Iterable<Segment> {
/**
* Class logger
*/
- private static final Logging logger = Logging.getLogger(Segments.class);
+ private static final Logging LOG = Logging.getLogger(Segments.class);
/**
* Clusterings
@@ -181,15 +186,14 @@ public class Segments extends BasicResult implements Iterable<Segment> {
HashSetModifiableDBIDs ndelta2 = DBIDUtil.newHashSet();
HashSetModifiableDBIDs nsecond = DBIDUtil.newHashSet(second.size());
for(DBIDIter iter2 = clust.getIDs().iter(); iter2.valid(); iter2.advance()) {
- DBID id = iter2.getDBID();
- if(ndelta1.remove(id)) {
- nfirstp.add(id);
+ if(ndelta1.remove(iter2)) {
+ nfirstp.add(iter2);
}
else {
- ndelta2.add(id);
+ ndelta2.add(iter2);
}
- if(second.contains(id)) {
- nsecond.add(id);
+ if(second.contains(iter2)) {
+ nsecond.add(iter2);
}
}
if(nsecond.size() <= 0) {
@@ -203,7 +207,7 @@ public class Segments extends BasicResult implements Iterable<Segment> {
else {
// Add to results.
// In fact, nfirstp should equal nsecond here
- int selfpairs = DBIDUtil.intersection(nfirstp, nsecond).size();
+ int selfpairs = DBIDUtil.intersectionSize(nfirstp, nsecond);
if(objectsegment) {
makeOrUpdateSegment(path, nfirstp, (nfirstp.size() * nsecond.size()) - selfpairs);
}
@@ -250,7 +254,7 @@ public class Segments extends BasicResult implements Iterable<Segment> {
}
if(ids != null) {
if(seg.getDBIDs() != null) {
- logger.warning("Expected segment to not have IDs.");
+ LOG.warning("Expected segment to not have IDs.");
}
seg.objIDs = ids;
}
diff --git a/src/de/lmu/ifi/dbs/elki/evaluation/histogram/ComputeOutlierHistogram.java b/src/de/lmu/ifi/dbs/elki/evaluation/histogram/ComputeOutlierHistogram.java
index 2a2bb069..a49db68b 100644
--- a/src/de/lmu/ifi/dbs/elki/evaluation/histogram/ComputeOutlierHistogram.java
+++ b/src/de/lmu/ifi/dbs/elki/evaluation/histogram/ComputeOutlierHistogram.java
@@ -36,15 +36,15 @@ import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.ids.ModifiableDBIDs;
import de.lmu.ifi.dbs.elki.distance.distancefunction.EuclideanDistanceFunction;
import de.lmu.ifi.dbs.elki.evaluation.Evaluator;
-import de.lmu.ifi.dbs.elki.logging.Logging;
-import de.lmu.ifi.dbs.elki.math.histograms.AggregatingHistogram;
-import de.lmu.ifi.dbs.elki.math.histograms.FlexiHistogram;
import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
import de.lmu.ifi.dbs.elki.result.HistogramResult;
import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
import de.lmu.ifi.dbs.elki.result.outlier.OutlierResult;
import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
+import de.lmu.ifi.dbs.elki.utilities.datastructures.histogram.AbstractObjDynamicHistogram;
+import de.lmu.ifi.dbs.elki.utilities.datastructures.histogram.AbstractObjStaticHistogram;
+import de.lmu.ifi.dbs.elki.utilities.datastructures.histogram.ObjHistogram;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterConstraint;
@@ -54,7 +54,6 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ObjectParameter;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.PatternParameter;
import de.lmu.ifi.dbs.elki.utilities.pairs.DoubleDoublePair;
-import de.lmu.ifi.dbs.elki.utilities.pairs.DoubleObjPair;
import de.lmu.ifi.dbs.elki.utilities.scaling.IdentityScaling;
import de.lmu.ifi.dbs.elki.utilities.scaling.ScalingFunction;
import de.lmu.ifi.dbs.elki.utilities.scaling.outlier.OutlierScalingFunction;
@@ -75,17 +74,12 @@ import de.lmu.ifi.dbs.elki.utilities.scaling.outlier.OutlierScalingFunction;
*/
public class ComputeOutlierHistogram implements Evaluator {
/**
- * Logger for debugging.
- */
- protected static final Logging logger = Logging.getLogger(ComputeOutlierHistogram.class);
-
- /**
* The object pattern to identify positive classes
* <p>
* Key: {@code -comphist.positive}
* </p>
*/
- public static final OptionID POSITIVE_CLASS_NAME_ID = OptionID.getOrCreateOptionID("comphist.positive", "Class label for the 'positive' class.");
+ public static final OptionID POSITIVE_CLASS_NAME_ID = new OptionID("comphist.positive", "Class label for the 'positive' class.");
/**
* number of bins for the histogram
@@ -96,7 +90,7 @@ public class ComputeOutlierHistogram implements Evaluator {
* Key: {@code -comphist.bins}
* </p>
*/
- public static final OptionID BINS_ID = OptionID.getOrCreateOptionID("comphist.bins", "number of bins");
+ public static final OptionID BINS_ID = new OptionID("comphist.bins", "number of bins");
/**
* Parameter to specify a scaling function to use.
@@ -104,7 +98,7 @@ public class ComputeOutlierHistogram implements Evaluator {
* Key: {@code -comphist.scaling}
* </p>
*/
- public static final OptionID SCALING_ID = OptionID.getOrCreateOptionID("comphist.scaling", "Class to use as scaling function.");
+ public static final OptionID SCALING_ID = new OptionID("comphist.scaling", "Class to use as scaling function.");
/**
* Flag to count frequencies of outliers and non-outliers separately
@@ -112,7 +106,7 @@ public class ComputeOutlierHistogram implements Evaluator {
* Key: {@code -histogram.splitfreq}
* </p>
*/
- public static final OptionID SPLITFREQ_ID = OptionID.getOrCreateOptionID("histogram.splitfreq", "Use separate frequencies for outliers and non-outliers.");
+ public static final OptionID SPLITFREQ_ID = new OptionID("histogram.splitfreq", "Use separate frequencies for outliers and non-outliers.");
/**
* Stores the "positive" class.
@@ -158,7 +152,7 @@ public class ComputeOutlierHistogram implements Evaluator {
* @return Result
*/
public HistogramResult<DoubleVector> evaluateOutlierResult(Database database, OutlierResult or) {
- if(scaling instanceof OutlierScalingFunction) {
+ if (scaling instanceof OutlierScalingFunction) {
OutlierScalingFunction oscaling = (OutlierScalingFunction) scaling;
oscaling.prepare(or);
}
@@ -166,45 +160,83 @@ public class ComputeOutlierHistogram implements Evaluator {
ModifiableDBIDs ids = DBIDUtil.newHashSet(or.getScores().getDBIDs());
DBIDs outlierIds = DatabaseUtil.getObjectsByLabelMatch(database, positiveClassName);
// first value for outliers, second for each object
- final AggregatingHistogram<DoubleDoublePair, DoubleDoublePair> hist;
// If we have useful (finite) min/max, use these for binning.
double min = scaling.getMin();
double max = scaling.getMax();
- if(Double.isInfinite(min) || Double.isNaN(min) || Double.isInfinite(max) || Double.isNaN(max)) {
- hist = FlexiHistogram.DoubleSumDoubleSumHistogram(bins);
- }
- else {
- hist = AggregatingHistogram.DoubleSumDoubleSumHistogram(bins, min, max);
+ final ObjHistogram<DoubleDoublePair> hist;
+ if (Double.isInfinite(min) || Double.isNaN(min) || Double.isInfinite(max) || Double.isNaN(max)) {
+ hist = new AbstractObjDynamicHistogram<DoubleDoublePair>(bins) {
+ @Override
+ public DoubleDoublePair aggregate(DoubleDoublePair first, DoubleDoublePair second) {
+ first.first += second.first;
+ first.second += second.second;
+ return first;
+ }
+
+ @Override
+ protected DoubleDoublePair makeObject() {
+ return new DoubleDoublePair(0., 0.);
+ }
+
+ @Override
+ protected DoubleDoublePair cloneForCache(DoubleDoublePair data) {
+ return new DoubleDoublePair(data.first, data.second);
+ }
+
+ @Override
+ protected DoubleDoublePair downsample(Object[] data, int start, int end, int size) {
+ DoubleDoublePair sum = new DoubleDoublePair(0, 0);
+ for (int i = start; i < end; i++) {
+ DoubleDoublePair p = (DoubleDoublePair) data[i];
+ if (p != null) {
+ sum.first += p.first;
+ sum.second += p.second;
+ }
+ }
+ return sum;
+ }
+ };
+ } else {
+ hist = new AbstractObjStaticHistogram<DoubleDoublePair>(bins, min, max) {
+ @Override
+ protected DoubleDoublePair makeObject() {
+ return new DoubleDoublePair(0., 0.);
+ }
+
+ @Override
+ public void putData(double coord, DoubleDoublePair data) {
+ DoubleDoublePair exist = get(coord);
+ exist.first += data.first;
+ exist.second += data.second;
+ }
+ };
}
+
// first fill histogram only with values of outliers
- DoubleDoublePair positive, negative;
- if(!splitfreq) {
- positive = new DoubleDoublePair(0., 1. / ids.size());
- negative = new DoubleDoublePair(1. / ids.size(), 0.);
- }
- else {
- positive = new DoubleDoublePair(0., 1. / outlierIds.size());
- negative = new DoubleDoublePair(1. / (ids.size() - outlierIds.size()), 0.);
+ DoubleDoublePair negative, positive;
+ if (!splitfreq) {
+ negative = new DoubleDoublePair(1. / ids.size(), 0);
+ positive = new DoubleDoublePair(0, 1. / ids.size());
+ } else {
+ negative = new DoubleDoublePair(1. / (ids.size() - outlierIds.size()), 0);
+ positive = new DoubleDoublePair(0, 1. / outlierIds.size());
}
ids.removeDBIDs(outlierIds);
// fill histogram with values of each object
- for(DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
+ for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
double result = or.getScores().get(iter);
result = scaling.getScaled(result);
- hist.aggregate(result, negative);
+ hist.putData(result, negative);
}
- for(DBIDIter iter = outlierIds.iter(); iter.valid(); iter.advance()) {
+ for (DBIDIter iter = outlierIds.iter(); iter.valid(); iter.advance()) {
double result = or.getScores().get(iter);
result = scaling.getScaled(result);
- hist.aggregate(result, positive);
+ hist.putData(result, positive);
}
-
- // turn into Collection
-
Collection<DoubleVector> collHist = new ArrayList<DoubleVector>(hist.getNumBins());
- for(DoubleObjPair<DoubleDoublePair> ppair : hist) {
- DoubleDoublePair data = ppair.getSecond();
- DoubleVector row = new DoubleVector(new double[] { ppair.first, data.first, data.second });
+ for (ObjHistogram.Iter<DoubleDoublePair> iter = hist.iter(); iter.valid(); iter.advance()) {
+ DoubleDoublePair data = iter.getValue();
+ DoubleVector row = new DoubleVector(new double[] { iter.getCenter(), data.first, data.second });
collHist.add(row);
}
return new HistogramResult<DoubleVector>("Outlier Score Histogram", "outlier-histogram", collHist);
@@ -214,12 +246,12 @@ public class ComputeOutlierHistogram implements Evaluator {
public void processNewResult(HierarchicalResult baseResult, Result result) {
final Database db = ResultUtil.findDatabase(baseResult);
List<OutlierResult> ors = ResultUtil.filterResults(result, OutlierResult.class);
- if(ors == null || ors.size() <= 0) {
+ if (ors == null || ors.size() <= 0) {
// logger.warning("No outlier results found for "+ComputeOutlierHistogram.class.getSimpleName());
return;
}
- for(OutlierResult or : ors) {
+ for (OutlierResult or : ors) {
db.getHierarchy().add(or, evaluateOutlierResult(db, or));
}
}
@@ -255,23 +287,25 @@ public class ComputeOutlierHistogram implements Evaluator {
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- PatternParameter positiveClassNameP = new PatternParameter(POSITIVE_CLASS_NAME_ID, true);
- if(config.grab(positiveClassNameP)) {
+ PatternParameter positiveClassNameP = new PatternParameter(POSITIVE_CLASS_NAME_ID);
+ positiveClassNameP.setOptional(true);
+ if (config.grab(positiveClassNameP)) {
positiveClassName = positiveClassNameP.getValue();
}
- IntParameter binsP = new IntParameter(BINS_ID, new GreaterConstraint(1), 50);
- if(config.grab(binsP)) {
+ IntParameter binsP = new IntParameter(BINS_ID, 50);
+ binsP.addConstraint(new GreaterConstraint(1));
+ if (config.grab(binsP)) {
bins = binsP.getValue();
}
ObjectParameter<ScalingFunction> scalingP = new ObjectParameter<ScalingFunction>(SCALING_ID, ScalingFunction.class, IdentityScaling.class);
- if(config.grab(scalingP)) {
+ if (config.grab(scalingP)) {
scaling = scalingP.instantiateClass(config);
}
Flag splitfreqF = new Flag(SPLITFREQ_ID);
- if(config.grab(splitfreqF)) {
+ if (config.grab(splitfreqF)) {
splitfreq = splitfreqF.getValue();
}
@@ -282,4 +316,4 @@ public class ComputeOutlierHistogram implements Evaluator {
return new ComputeOutlierHistogram(positiveClassName, bins, scaling, splitfreq);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/evaluation/index/IndexPurity.java b/src/de/lmu/ifi/dbs/elki/evaluation/index/IndexPurity.java
index c28c22d3..3fc928a9 100644
--- a/src/de/lmu/ifi/dbs/elki/evaluation/index/IndexPurity.java
+++ b/src/de/lmu/ifi/dbs/elki/evaluation/index/IndexPurity.java
@@ -96,7 +96,7 @@ public class IndexPurity implements Evaluator {
}
mv.put(gini);
}
- Collection<DoubleVector> col = new java.util.Vector<DoubleVector>();
+ Collection<DoubleVector> col = new ArrayList<DoubleVector>();
col.add(new DoubleVector(new double[] { mv.getMean(), mv.getSampleStddev() }));
database.getHierarchy().add((Result) index, new CollectionResult<DoubleVector>("Gini coefficient of index", "index-gini", col));
}
diff --git a/src/de/lmu/ifi/dbs/elki/evaluation/index/IndexStatistics.java b/src/de/lmu/ifi/dbs/elki/evaluation/index/IndexStatistics.java
index 8d7aa0d8..cce2c79f 100644
--- a/src/de/lmu/ifi/dbs/elki/evaluation/index/IndexStatistics.java
+++ b/src/de/lmu/ifi/dbs/elki/evaluation/index/IndexStatistics.java
@@ -61,10 +61,10 @@ public class IndexStatistics implements Evaluator {
return;
}
for(IndexTree<?, ?> index : indexes) {
- header = new java.util.Vector<String>();
+ header = new ArrayList<String>();
header.add(index.toString());
}
- Collection<Pair<String, String>> col = new java.util.Vector<Pair<String, String>>();
+ Collection<Pair<String, String>> col = new ArrayList<Pair<String, String>>();
IndexMetaResult analysis = new IndexMetaResult(col, header);
db.getHierarchy().add(db, analysis);
}
diff --git a/src/de/lmu/ifi/dbs/elki/evaluation/outlier/JudgeOutlierScores.java b/src/de/lmu/ifi/dbs/elki/evaluation/outlier/JudgeOutlierScores.java
index 49775b1a..2ffe4e05 100644
--- a/src/de/lmu/ifi/dbs/elki/evaluation/outlier/JudgeOutlierScores.java
+++ b/src/de/lmu/ifi/dbs/elki/evaluation/outlier/JudgeOutlierScores.java
@@ -1,26 +1,27 @@
package de.lmu.ifi.dbs.elki.evaluation.outlier;
-/*
-This file is part of ELKI:
-Environment for Developing KDD-Applications Supported by Index-Structures
-
-Copyright (C) 2012
-Ludwig-Maximilians-Universität München
-Lehr- und Forschungseinheit für Datenbanksysteme
-ELKI Development Team
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
import java.util.ArrayList;
import java.util.Collection;
@@ -67,7 +68,7 @@ public class JudgeOutlierScores implements Evaluator {
/**
* Logger for debug output.
*/
- protected static final Logging logger = Logging.getLogger(JudgeOutlierScores.class);
+ private static final Logging LOG = Logging.getLogger(JudgeOutlierScores.class);
/**
* The distance function to determine the reachability distance between
@@ -79,7 +80,7 @@ public class JudgeOutlierScores implements Evaluator {
* Key: {@code -comphist.positive}
* </p>
*/
- public static final OptionID POSITIVE_CLASS_NAME_ID = OptionID.getOrCreateOptionID("comphist.positive", "Class label for the 'positive' class.");
+ public static final OptionID POSITIVE_CLASS_NAME_ID = new OptionID("comphist.positive", "Class label for the 'positive' class.");
/**
* Parameter to specify a scaling function to use.
@@ -87,7 +88,7 @@ public class JudgeOutlierScores implements Evaluator {
* Key: {@code -comphist.scaling}
* </p>
*/
- public static final OptionID SCALING_ID = OptionID.getOrCreateOptionID("comphist.scaling", "Class to use as scaling function.");
+ public static final OptionID SCALING_ID = new OptionID("comphist.scaling", "Class to use as scaling function.");
/**
* Stores the "positive" class.
@@ -121,7 +122,7 @@ public class JudgeOutlierScores implements Evaluator {
* @throws IllegalStateException
*/
protected ScoreResult computeScore(DBIDs ids, DBIDs outlierIds, OutlierResult or) throws IllegalStateException {
- if(scaling instanceof OutlierScalingFunction) {
+ if (scaling instanceof OutlierScalingFunction) {
OutlierScalingFunction oscaling = (OutlierScalingFunction) scaling;
oscaling.prepare(or);
}
@@ -130,16 +131,14 @@ public class JudgeOutlierScores implements Evaluator {
// If we have useful (finite) min/max, use these for binning.
double min = scaling.getMin();
double max = scaling.getMax();
- if(Double.isInfinite(min) || Double.isNaN(min) || Double.isInfinite(max) || Double.isNaN(max)) {
+ if (Double.isInfinite(min) || Double.isNaN(min) || Double.isInfinite(max) || Double.isNaN(max)) {
innerScaling = new IdentityScaling();
// TODO: does the outlier score give us this guarantee?
- logger.warning("JudgeOutlierScores expects values between 0.0 and 1.0, but we don't have such a guarantee by the scaling function: min:" + min + " max:" + max);
- }
- else {
- if(min == 0.0 && max == 1.0) {
+ LOG.warning("JudgeOutlierScores expects values between 0.0 and 1.0, but we don't have such a guarantee by the scaling function: min:" + min + " max:" + max);
+ } else {
+ if (min == 0.0 && max == 1.0) {
innerScaling = new IdentityScaling();
- }
- else {
+ } else {
innerScaling = new LinearScaling(1.0 / (max - min), -min);
}
}
@@ -160,10 +159,10 @@ public class JudgeOutlierScores implements Evaluator {
posscore /= ids.size();
negscore /= outlierIds.size();
- logger.verbose("Scores: " + posscore + " " + negscore);
+ LOG.verbose("Scores: " + posscore + " " + negscore);
ArrayList<Vector> s = new ArrayList<Vector>(1);
- s.add(new Vector(new double[] { (posscore + negscore) / 2, posscore, negscore }));
+ s.add(new Vector(new double[] { (posscore + negscore) * .5, posscore, negscore }));
return new ScoreResult(s);
}
@@ -171,7 +170,7 @@ public class JudgeOutlierScores implements Evaluator {
public void processNewResult(HierarchicalResult baseResult, Result result) {
Database db = ResultUtil.findDatabase(baseResult);
List<OutlierResult> ors = ResultUtil.filterResults(result, OutlierResult.class);
- if(ors == null || ors.size() <= 0) {
+ if (ors == null || ors.size() <= 0) {
// logger.warning("No results found for "+JudgeOutlierScores.class.getSimpleName());
return;
}
@@ -180,7 +179,7 @@ public class JudgeOutlierScores implements Evaluator {
DBIDs outlierIds = DatabaseUtil.getObjectsByLabelMatch(db, positiveClassName);
ids.removeDBIDs(outlierIds);
- for(OutlierResult or : ors) {
+ for (OutlierResult or : ors) {
db.getHierarchy().add(or, computeScore(ids, outlierIds, or));
}
}
@@ -202,11 +201,11 @@ public class JudgeOutlierScores implements Evaluator {
}
/**
- * Parameterization class.
- *
- * @author Erich Schubert
- *
- * @apiviz.exclude
+ * Parameterization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
*/
public static class Parameterizer extends AbstractParameterizer {
/**
@@ -223,12 +222,12 @@ public class JudgeOutlierScores implements Evaluator {
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
PatternParameter positiveClassNameP = new PatternParameter(POSITIVE_CLASS_NAME_ID);
- if(config.grab(positiveClassNameP)) {
+ if (config.grab(positiveClassNameP)) {
positiveClassName = positiveClassNameP.getValue();
}
ObjectParameter<ScalingFunction> scalingP = new ObjectParameter<ScalingFunction>(SCALING_ID, ScalingFunction.class, IdentityScaling.class);
- if(config.grab(scalingP)) {
+ if (config.grab(scalingP)) {
scaling = scalingP.instantiateClass(config);
}
}
@@ -238,4 +237,4 @@ public class JudgeOutlierScores implements Evaluator {
return new JudgeOutlierScores(positiveClassName, scaling);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/evaluation/outlier/OutlierPrecisionAtKCurve.java b/src/de/lmu/ifi/dbs/elki/evaluation/outlier/OutlierPrecisionAtKCurve.java
index 6d65999e..11a98876 100644
--- a/src/de/lmu/ifi/dbs/elki/evaluation/outlier/OutlierPrecisionAtKCurve.java
+++ b/src/de/lmu/ifi/dbs/elki/evaluation/outlier/OutlierPrecisionAtKCurve.java
@@ -59,7 +59,7 @@ public class OutlierPrecisionAtKCurve implements Evaluator {
/**
* The logger.
*/
- private static final Logging logger = Logging.getLogger(OutlierPrecisionAtKCurve.class);
+ private static final Logging LOG = Logging.getLogger(OutlierPrecisionAtKCurve.class);
/**
* The pattern to identify positive classes.
@@ -68,7 +68,7 @@ public class OutlierPrecisionAtKCurve implements Evaluator {
* Key: {@code -precision.positive}
* </p>
*/
- public static final OptionID POSITIVE_CLASS_NAME_ID = OptionID.getOrCreateOptionID("precision.positive", "Class label for the 'positive' class.");
+ public static final OptionID POSITIVE_CLASS_NAME_ID = new OptionID("precision.positive", "Class label for the 'positive' class.");
/**
* Maximum value for k
@@ -77,7 +77,7 @@ public class OutlierPrecisionAtKCurve implements Evaluator {
* Key: {@code -precision.k}
* </p>
*/
- public static final OptionID MAX_K_ID = OptionID.getOrCreateOptionID("precision.maxk", "Maximum value of 'k' to compute the curve up to.");
+ public static final OptionID MAX_K_ID = new OptionID("precision.maxk", "Maximum value of 'k' to compute the curve up to.");
/**
* Stores the "positive" class.
@@ -108,7 +108,7 @@ public class OutlierPrecisionAtKCurve implements Evaluator {
SetDBIDs positiveids = DBIDUtil.ensureSet(DatabaseUtil.getObjectsByLabelMatch(db, positiveClassName));
if(positiveids.size() == 0) {
- logger.warning("Computing a ROC curve failed - no objects matched.");
+ LOG.warning("Computing a ROC curve failed - no objects matched.");
return;
}
@@ -140,13 +140,13 @@ public class OutlierPrecisionAtKCurve implements Evaluator {
int pos = 0;
DBIDIter i = order.iter();
for(int k = 1; k <= lastk; k++, i.advance()) {
- if(positiveids.contains(i.getDBID())) {
+ if(positiveids.contains(i)) {
pos++;
}
curve.addAndSimplify(k, pos / (double) k);
}
- if(logger.isVerbose()) {
- logger.verbose("Precision @ " + lastk + " " + ((pos * 1.0) / lastk));
+ if(LOG.isVerbose()) {
+ LOG.verbose("Precision @ " + lastk + " " + ((pos * 1.0) / lastk));
}
return curve;
}
@@ -213,7 +213,8 @@ public class OutlierPrecisionAtKCurve implements Evaluator {
if(config.grab(positiveClassNameP)) {
positiveClassName = positiveClassNameP.getValue();
}
- IntParameter maxkP = new IntParameter(MAX_K_ID, true);
+ IntParameter maxkP = new IntParameter(MAX_K_ID);
+ maxkP.setOptional(true);
if(config.grab(maxkP)) {
maxk = maxkP.getValue();
}
diff --git a/src/de/lmu/ifi/dbs/elki/evaluation/outlier/OutlierPrecisionRecallCurve.java b/src/de/lmu/ifi/dbs/elki/evaluation/outlier/OutlierPrecisionRecallCurve.java
index c839016c..bc58355b 100644
--- a/src/de/lmu/ifi/dbs/elki/evaluation/outlier/OutlierPrecisionRecallCurve.java
+++ b/src/de/lmu/ifi/dbs/elki/evaluation/outlier/OutlierPrecisionRecallCurve.java
@@ -27,7 +27,6 @@ import java.util.List;
import java.util.regex.Pattern;
import de.lmu.ifi.dbs.elki.database.Database;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
@@ -60,7 +59,7 @@ public class OutlierPrecisionRecallCurve implements Evaluator {
/**
* The logger.
*/
- private static final Logging logger = Logging.getLogger(OutlierPrecisionRecallCurve.class);
+ private static final Logging LOG = Logging.getLogger(OutlierPrecisionRecallCurve.class);
/**
* The pattern to identify positive classes.
@@ -69,7 +68,7 @@ public class OutlierPrecisionRecallCurve implements Evaluator {
* Key: {@code -precision.positive}
* </p>
*/
- public static final OptionID POSITIVE_CLASS_NAME_ID = OptionID.getOrCreateOptionID("precision.positive", "Class label for the 'positive' class.");
+ public static final OptionID POSITIVE_CLASS_NAME_ID = new OptionID("precision.positive", "Class label for the 'positive' class.");
/**
* Stores the "positive" class.
@@ -92,15 +91,15 @@ public class OutlierPrecisionRecallCurve implements Evaluator {
// Prepare
SetDBIDs positiveids = DBIDUtil.ensureSet(DatabaseUtil.getObjectsByLabelMatch(db, positiveClassName));
- if(positiveids.size() == 0) {
- logger.warning("Computing a ROC curve failed - no objects matched.");
+ if (positiveids.size() == 0) {
+ LOG.warning("Computing a ROC curve failed - no objects matched.");
return;
}
List<OutlierResult> oresults = ResultUtil.getOutlierResults(result);
List<OrderingResult> orderings = ResultUtil.getOrderingResults(result);
// Outlier results are the main use case.
- for(OutlierResult o : oresults) {
+ for (OutlierResult o : oresults) {
DBIDs sorted = o.getOrdering().iter(o.getOrdering().getDBIDs());
XYCurve curve = computePrecisionResult(o.getScores().size(), positiveids, sorted.iter(), o.getScores());
db.getHierarchy().add(o, curve);
@@ -110,7 +109,7 @@ public class OutlierPrecisionRecallCurve implements Evaluator {
// FIXME: find appropriate place to add the derived result
// otherwise apply an ordering to the database IDs.
- for(OrderingResult or : orderings) {
+ for (OrderingResult or : orderings) {
DBIDs sorted = or.iter(or.getDBIDs());
XYCurve curve = computePrecisionResult(or.getDBIDs().size(), positiveids, sorted.iter(), null);
db.getHierarchy().add(or, curve);
@@ -120,30 +119,29 @@ public class OutlierPrecisionRecallCurve implements Evaluator {
private XYCurve computePrecisionResult(int size, SetDBIDs ids, DBIDIter iter, Relation<Double> scores) {
final int postot = ids.size();
int poscnt = 0, total = 0;
- XYCurve curve = new PRCurve(postot + 2);
+ XYCurve curve = new PRCurve(postot + 2, postot);
double prevscore = Double.NaN;
- for(; iter.valid(); iter.advance()) {
+ for (; iter.valid(); iter.advance()) {
// Previous precision rate - y axis
final double curprec = ((double) poscnt) / total;
// Previous recall rate - x axis
final double curreca = ((double) poscnt) / postot;
// Analyze next point
- DBID cur = iter.getDBID();
// positive or negative match?
- if(ids.contains(cur)) {
+ if (ids.contains(iter)) {
poscnt += 1;
}
total += 1;
// First iteration ends here
- if(total == 1) {
+ if (total == 1) {
continue;
}
// defer calculation for ties
- if(scores != null) {
- double curscore = scores.get(cur);
- if(Double.compare(prevscore, curscore) == 0) {
+ if (scores != null) {
+ double curscore = scores.get(iter);
+ if (Double.compare(prevscore, curscore) == 0) {
continue;
}
prevscore = curscore;
@@ -173,12 +171,19 @@ public class OutlierPrecisionRecallCurve implements Evaluator {
double auc = Double.NaN;
/**
+ * Number of positive observations
+ */
+ int positive;
+
+ /**
* Constructor.
*
* @param size Size estimation
+ * @param positive Number of positive elements (for AUC correction)
*/
- public PRCurve(int size) {
+ public PRCurve(int size, int positive) {
super("Recall", "Precision", size);
+ this.positive = positive;
}
@Override
@@ -197,8 +202,9 @@ public class OutlierPrecisionRecallCurve implements Evaluator {
* @return AUC value
*/
public double getAUC() {
- if(Double.isNaN(auc)) {
- auc = areaUnderCurve(this);
+ if (Double.isNaN(auc)) {
+ double max = 1 - 1. / positive;
+ auc = areaUnderCurve(this) / max;
}
return auc;
}
@@ -225,7 +231,7 @@ public class OutlierPrecisionRecallCurve implements Evaluator {
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
PatternParameter positiveClassNameP = new PatternParameter(POSITIVE_CLASS_NAME_ID);
- if(config.grab(positiveClassNameP)) {
+ if (config.grab(positiveClassNameP)) {
positiveClassName = positiveClassNameP.getValue();
}
}
@@ -235,4 +241,4 @@ public class OutlierPrecisionRecallCurve implements Evaluator {
return new OutlierPrecisionRecallCurve(positiveClassName);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/evaluation/outlier/OutlierROCCurve.java b/src/de/lmu/ifi/dbs/elki/evaluation/outlier/OutlierROCCurve.java
index ec73d67f..3baa642b 100644
--- a/src/de/lmu/ifi/dbs/elki/evaluation/outlier/OutlierROCCurve.java
+++ b/src/de/lmu/ifi/dbs/elki/evaluation/outlier/OutlierROCCurve.java
@@ -75,7 +75,7 @@ public class OutlierROCCurve implements Evaluator {
/**
* The logger.
*/
- private static final Logging logger = Logging.getLogger(OutlierROCCurve.class);
+ private static final Logging LOG = Logging.getLogger(OutlierROCCurve.class);
/**
* The pattern to identify positive classes.
@@ -84,7 +84,7 @@ public class OutlierROCCurve implements Evaluator {
* Key: {@code -rocauc.positive}
* </p>
*/
- public static final OptionID POSITIVE_CLASS_NAME_ID = OptionID.getOrCreateOptionID("rocauc.positive", "Class label for the 'positive' class.");
+ public static final OptionID POSITIVE_CLASS_NAME_ID = new OptionID("rocauc.positive", "Class label for the 'positive' class.");
/**
* Stores the "positive" class.
@@ -107,8 +107,8 @@ public class OutlierROCCurve implements Evaluator {
}
XYCurve roccurve = ROC.materializeROC(size, positiveids, new ROC.SimpleAdapter(order.iter()));
double rocauc = XYCurve.areaUnderCurve(roccurve);
- if(logger.isVerbose()) {
- logger.verbose(ROCAUC_LABEL + ": " + rocauc);
+ if(LOG.isVerbose()) {
+ LOG.verbose(ROCAUC_LABEL + ": " + rocauc);
}
final ROCResult rocresult = new ROCResult(roccurve, rocauc);
@@ -119,8 +119,8 @@ public class OutlierROCCurve implements Evaluator {
private ROCResult computeROCResult(int size, SetDBIDs positiveids, OutlierResult or) {
XYCurve roccurve = ROC.materializeROC(size, positiveids, new ROC.OutlierScoreAdapter(or));
double rocauc = XYCurve.areaUnderCurve(roccurve);
- if(logger.isVerbose()) {
- logger.verbose(ROCAUC_LABEL + ": " + rocauc);
+ if(LOG.isVerbose()) {
+ LOG.verbose(ROCAUC_LABEL + ": " + rocauc);
}
final ROCResult rocresult = new ROCResult(roccurve, rocauc);
@@ -135,7 +135,7 @@ public class OutlierROCCurve implements Evaluator {
SetDBIDs positiveids = DBIDUtil.ensureSet(DatabaseUtil.getObjectsByLabelMatch(db, positiveClassName));
if(positiveids.size() == 0) {
- logger.warning("Computing a ROC curve failed - no objects matched.");
+ LOG.warning("Computing a ROC curve failed - no objects matched.");
return;
}
diff --git a/src/de/lmu/ifi/dbs/elki/evaluation/outlier/OutlierSmROCCurve.java b/src/de/lmu/ifi/dbs/elki/evaluation/outlier/OutlierSmROCCurve.java
index 928ac842..0cd3d9ea 100644
--- a/src/de/lmu/ifi/dbs/elki/evaluation/outlier/OutlierSmROCCurve.java
+++ b/src/de/lmu/ifi/dbs/elki/evaluation/outlier/OutlierSmROCCurve.java
@@ -27,7 +27,6 @@ import java.util.List;
import java.util.regex.Pattern;
import de.lmu.ifi.dbs.elki.database.Database;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.SetDBIDs;
@@ -84,7 +83,7 @@ public class OutlierSmROCCurve implements Evaluator {
/**
* The logger.
*/
- private static final Logging logger = Logging.getLogger(OutlierSmROCCurve.class);
+ private static final Logging LOG = Logging.getLogger(OutlierSmROCCurve.class);
/**
* Stores the "positive" class.
@@ -108,8 +107,7 @@ public class OutlierSmROCCurve implements Evaluator {
// Compute mean, for inversion
double mean = 0.0;
for(DBIDIter iditer = scores.iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
- mean += scores.get(id) / size;
+ mean += scores.get(iditer) / size;
}
SmROCResult curve = new SmROCResult(positiveids.size() + 2);
@@ -122,12 +120,11 @@ public class OutlierSmROCCurve implements Evaluator {
double x = 0, y = 0;
for (DBIDIter nei = or.getOrdering().iter(or.getOrdering().getDBIDs()).iter(); nei.valid(); nei.advance()) {
// Analyze next point
- final DBID curid = nei.getDBID();
- final double curscore = scores.get(curid);
+ final double curscore = scores.get(nei);
// defer calculation for ties
if(!Double.isNaN(prevscore) && (Double.compare(prevscore, curscore) == 0)) {
// positive or negative match?
- if(positiveids.contains(curid)) {
+ if(positiveids.contains(nei)) {
poscnt += 1;
}
else {
@@ -147,7 +144,7 @@ public class OutlierSmROCCurve implements Evaluator {
}
curve.addAndSimplify(x, y);
// positive or negative match?
- if(positiveids.contains(curid)) {
+ if(positiveids.contains(nei)) {
poscnt = 1;
negcnt = 0;
}
@@ -172,8 +169,8 @@ public class OutlierSmROCCurve implements Evaluator {
}
double rocauc = XYCurve.areaUnderCurve(curve) / (x * y);
- if(logger.isVerbose()) {
- logger.verbose(SMROCAUC_LABEL + ": " + rocauc);
+ if(LOG.isVerbose()) {
+ LOG.verbose(SMROCAUC_LABEL + ": " + rocauc);
}
curve.rocauc = rocauc;
@@ -187,7 +184,7 @@ public class OutlierSmROCCurve implements Evaluator {
SetDBIDs positiveids = DBIDUtil.ensureSet(DatabaseUtil.getObjectsByLabelMatch(db, positiveClassName));
if(positiveids.size() == 0) {
- logger.warning("Computing a ROC curve failed - no objects matched.");
+ LOG.warning("Computing a ROC curve failed - no objects matched.");
return;
}
diff --git a/src/de/lmu/ifi/dbs/elki/evaluation/outlier/OutlierThresholdClustering.java b/src/de/lmu/ifi/dbs/elki/evaluation/outlier/OutlierThresholdClustering.java
index e5d35424..5d5173ca 100644
--- a/src/de/lmu/ifi/dbs/elki/evaluation/outlier/OutlierThresholdClustering.java
+++ b/src/de/lmu/ifi/dbs/elki/evaluation/outlier/OutlierThresholdClustering.java
@@ -29,7 +29,6 @@ import java.util.List;
import de.lmu.ifi.dbs.elki.data.Cluster;
import de.lmu.ifi.dbs.elki.data.Clustering;
import de.lmu.ifi.dbs.elki.data.model.Model;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.ModifiableDBIDs;
@@ -98,8 +97,7 @@ public class OutlierThresholdClustering implements Evaluator {
idlists.add(DBIDUtil.newHashSet());
}
for(DBIDIter iter = scores.getDBIDs().iter(); iter.valid(); iter.advance()) {
- DBID id = iter.getDBID();
- double score = scores.get(id);
+ double score = scores.get(iter);
if(scaling != null) {
score = scaling.getScaled(score);
}
@@ -109,7 +107,7 @@ public class OutlierThresholdClustering implements Evaluator {
break;
}
}
- idlists.get(i).add(id);
+ idlists.get(i).add(iter);
}
Clustering<Model> c = new Clustering<Model>("Outlier threshold clustering", "threshold-clustering");
for(int i = 0; i <= threshold.length; i++) {
@@ -133,7 +131,7 @@ public class OutlierThresholdClustering implements Evaluator {
* Key: {@code -thresholdclust.scaling}
* </p>
*/
- public static final OptionID SCALING_ID = OptionID.getOrCreateOptionID("thresholdclust.scaling", "Class to use as scaling function.");
+ public static final OptionID SCALING_ID = new OptionID("thresholdclust.scaling", "Class to use as scaling function.");
/**
* Parameter to specify the threshold
@@ -141,7 +139,7 @@ public class OutlierThresholdClustering implements Evaluator {
* Key: {@code -thresholdclust.threshold}
* </p>
*/
- public static final OptionID THRESHOLD_ID = OptionID.getOrCreateOptionID("thresholdclust.threshold", "Threshold(s) to apply.");
+ public static final OptionID THRESHOLD_ID = new OptionID("thresholdclust.threshold", "Threshold(s) to apply.");
/**
* Scaling function to use
diff --git a/src/de/lmu/ifi/dbs/elki/evaluation/roc/ROC.java b/src/de/lmu/ifi/dbs/elki/evaluation/roc/ROC.java
index d85cb137..558dfb94 100644
--- a/src/de/lmu/ifi/dbs/elki/evaluation/roc/ROC.java
+++ b/src/de/lmu/ifi/dbs/elki/evaluation/roc/ROC.java
@@ -27,18 +27,20 @@ import java.util.Iterator;
import java.util.Set;
import de.lmu.ifi.dbs.elki.data.Cluster;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDPair;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
+import de.lmu.ifi.dbs.elki.database.ids.DistanceDBIDPair;
+import de.lmu.ifi.dbs.elki.database.ids.DoubleDBIDPair;
import de.lmu.ifi.dbs.elki.database.ids.SetDBIDs;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResultIter;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
import de.lmu.ifi.dbs.elki.math.geometry.XYCurve;
import de.lmu.ifi.dbs.elki.result.outlier.OutlierResult;
-import de.lmu.ifi.dbs.elki.utilities.pairs.DoubleObjPair;
import de.lmu.ifi.dbs.elki.utilities.pairs.Pair;
import de.lmu.ifi.dbs.elki.utilities.pairs.PairInterface;
@@ -83,7 +85,7 @@ public class ROC {
public static <C extends Comparable<? super C>, T> XYCurve materializeROC(int size, Set<? super T> ids, Iterator<? extends PairInterface<C, T>> nei) {
final int postot = ids.size(), negtot = size - postot;
int poscnt = 0, negcnt = 0;
- XYCurve curve = new XYCurve("True Negative Rate", "True Positive Rate", postot + 2);
+ XYCurve curve = new XYCurve("False Positive Rate", "True Positive Rate", postot + 2);
// start in bottom left
curve.add(0.0, 0.0);
@@ -125,10 +127,10 @@ public class ROC {
* 'same positions'.
* @return area under curve
*/
- public static <C extends Comparable<? super C>> XYCurve materializeROC(int size, SetDBIDs ids, Iterator<? extends PairInterface<C, DBID>> nei) {
+ public static <C extends Comparable<? super C>> XYCurve materializeROC(int size, SetDBIDs ids, Iterator<? extends PairInterface<C, ? extends DBIDRef>> nei) {
final int postot = ids.size(), negtot = size - postot;
int poscnt = 0, negcnt = 0;
- XYCurve curve = new XYCurve("True Negative Rate", "True Positive Rate", postot + 2);
+ XYCurve curve = new XYCurve("False Positive Rate", "True Positive Rate", postot + 2);
// start in bottom left
curve.add(0.0, 0.0);
@@ -139,7 +141,7 @@ public class ROC {
final double trueneg = negcnt / (double) negtot;
final double truepos = poscnt / (double) postot;
// Analyze next point
- PairInterface<C, DBID> cur = nei.next();
+ PairInterface<C, ? extends DBIDRef> cur = nei.next();
// positive or negative match?
if(ids.contains(cur.getSecond())) {
poscnt += 1;
@@ -216,31 +218,32 @@ public class ROC {
* @author Erich Schubert
* @param <D> Distance type
*/
- public static class DistanceResultAdapter<D extends Distance<D>> implements Iterator<Pair<D, DBID>> {
+ public static class DistanceResultAdapter<D extends Distance<D>> implements Iterator<Pair<D, DBIDRef>> {
/**
* Original Iterator
*/
- private Iterator<? extends DistanceResultPair<D>> iter;
+ private DistanceDBIDResultIter<D> iter;
/**
* Constructor
*
* @param iter Iterator for distance results
*/
- public DistanceResultAdapter(Iterator<? extends DistanceResultPair<D>> iter) {
+ public DistanceResultAdapter(DistanceDBIDResultIter<D> iter) {
super();
this.iter = iter;
}
@Override
public boolean hasNext() {
- return this.iter.hasNext();
+ return this.iter.valid();
}
@Override
- public Pair<D, DBID> next() {
- DistanceResultPair<D> d = this.iter.next();
- return new Pair<D, DBID>(d.getDistance(), d.getDBID());
+ public Pair<D, DBIDRef> next() {
+ DistanceDBIDPair<D> d = this.iter.getDistancePair();
+ this.iter.advance();
+ return new Pair<D, DBIDRef>(d.getDistance(), d);
}
@Override
@@ -259,7 +262,7 @@ public class ROC {
*
* @author Erich Schubert
*/
- public static class OutlierScoreAdapter implements Iterator<DoubleObjPair<DBID>> {
+ public static class OutlierScoreAdapter implements Iterator<DoubleDBIDPair> {
/**
* Original Iterator
*/
@@ -287,10 +290,10 @@ public class ROC {
}
@Override
- public DoubleObjPair<DBID> next() {
- DBID id = this.iter.getDBID();
+ public DoubleDBIDPair next() {
+ DoubleDBIDPair pair = DBIDUtil.newPair(scores.get(iter), iter);
iter.advance();
- return new DoubleObjPair<DBID>(scores.get(id), id);
+ return pair;
}
@Override
@@ -308,7 +311,7 @@ public class ROC {
* @param nei Query result
* @return area under curve
*/
- public static <D extends Distance<D>> double computeROCAUCDistanceResult(int size, Cluster<?> clus, Iterable<? extends DistanceResultPair<D>> nei) {
+ public static <D extends Distance<D>> double computeROCAUCDistanceResult(int size, Cluster<?> clus, DistanceDBIDResult<D> nei) {
// TODO: ensure the collection has efficient "contains".
return ROC.computeROCAUCDistanceResult(size, clus.getIDs(), nei);
}
@@ -322,9 +325,9 @@ public class ROC {
* @param nei Query Result
* @return area under curve
*/
- public static <D extends Distance<D>> double computeROCAUCDistanceResult(int size, DBIDs ids, Iterable<? extends DistanceResultPair<D>> nei) {
+ public static <D extends Distance<D>> double computeROCAUCDistanceResult(int size, DBIDs ids, DistanceDBIDResult<D> nei) {
// TODO: do not materialize the ROC, but introduce an iterator interface
- XYCurve roc = materializeROC(size, DBIDUtil.ensureSet(ids), new DistanceResultAdapter<D>(nei.iterator()));
+ XYCurve roc = materializeROC(size, DBIDUtil.ensureSet(ids), new DistanceResultAdapter<D>(nei.iter()));
return XYCurve.areaUnderCurve(roc);
}
diff --git a/src/de/lmu/ifi/dbs/elki/evaluation/similaritymatrix/ComputeSimilarityMatrixImage.java b/src/de/lmu/ifi/dbs/elki/evaluation/similaritymatrix/ComputeSimilarityMatrixImage.java
index 0e5c7a02..72173d30 100644
--- a/src/de/lmu/ifi/dbs/elki/evaluation/similaritymatrix/ComputeSimilarityMatrixImage.java
+++ b/src/de/lmu/ifi/dbs/elki/evaluation/similaritymatrix/ComputeSimilarityMatrixImage.java
@@ -35,7 +35,7 @@ import de.lmu.ifi.dbs.elki.algorithm.AbstractAlgorithm;
import de.lmu.ifi.dbs.elki.database.Database;
import de.lmu.ifi.dbs.elki.database.ids.ArrayDBIDs;
import de.lmu.ifi.dbs.elki.database.ids.ArrayModifiableDBIDs;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDArrayIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
@@ -75,17 +75,17 @@ public class ComputeSimilarityMatrixImage<O> implements Evaluator {
/**
* The logger.
*/
- static final Logging logger = Logging.getLogger(ComputeSimilarityMatrixImage.class);
+ private static final Logging LOG = Logging.getLogger(ComputeSimilarityMatrixImage.class);
/**
* OptionID for the scaling function to use
*/
- public static final OptionID SCALING_ID = OptionID.getOrCreateOptionID("simmatrix.scaling", "Class to use as scaling function.");
+ public static final OptionID SCALING_ID = new OptionID("simmatrix.scaling", "Class to use as scaling function.");
/**
* OptionID to skip zero values when plotting to increase contrast.
*/
- public static final OptionID SKIPZERO_ID = OptionID.getOrCreateOptionID("simmatrix.skipzero", "Skip zero values when computing the colors to increase contrast.");
+ public static final OptionID SKIPZERO_ID = new OptionID("simmatrix.skipzero", "Skip zero values when computing the colors to increase contrast.");
/**
* The distance function to use
@@ -126,13 +126,7 @@ public class ComputeSimilarityMatrixImage<O> implements Evaluator {
private SimilarityMatrix computeSimilarityMatrixImage(Relation<O> relation, DBIDIter iter) {
ArrayModifiableDBIDs order = DBIDUtil.newArray(relation.size());
for(; iter.valid(); iter.advance()) {
- Object o = iter.getDBID();
- if(!(o instanceof DBID)) {
- throw new IllegalStateException("Iterable result contained non-DBID - result didn't satisfy requirements");
- }
- else {
- order.add((DBID) o);
- }
+ order.add(iter);
}
if(order.size() != relation.size()) {
throw new IllegalStateException("Iterable result doesn't match database size - incomplete ordering?");
@@ -143,24 +137,27 @@ public class ComputeSimilarityMatrixImage<O> implements Evaluator {
// When the logging is in the outer loop, it's just 2*size (providing enough
// resolution)
final int ltotal = 2 * size; // size * (size + 1);
- FiniteProgress prog = logger.isVerbose() ? new FiniteProgress("Similarity Matrix Image", ltotal, logger) : null;
+ FiniteProgress prog = LOG.isVerbose() ? new FiniteProgress("Similarity Matrix Image", ltotal, LOG) : null;
// Note: we assume that we have an efficient distance cache available,
// since we are using 2*O(n*n) distance computations.
DoubleMinMax minmax = new DoubleMinMax();
- for(int x = 0; x < size; x++) {
- DBID id1 = order.get(x);
- for(int y = x; y < size; y++) {
- DBID id2 = order.get(y);
- final double dist = dq.distance(id1, id2).doubleValue();
- if(!Double.isNaN(dist) && !Double.isInfinite(dist) /* && dist > 0.0 */) {
- if(!skipzero || dist != 0.0) {
- minmax.put(dist);
+ {
+ DBIDArrayIter id1 = order.iter();
+ DBIDArrayIter id2 = order.iter();
+ for(; id1.valid(); id1.advance()) {
+ id2.seek(id1.getOffset());
+ for(; id2.valid(); id2.advance()) {
+ final double dist = dq.distance(id1, id2).doubleValue();
+ if(!Double.isNaN(dist) && !Double.isInfinite(dist) /* && dist > 0.0 */) {
+ if(!skipzero || dist > 0.0) {
+ minmax.put(dist);
+ }
}
}
- }
- if(prog != null) {
- prog.incrementProcessed(logger);
+ if(prog != null) {
+ prog.incrementProcessed(LOG);
+ }
}
}
@@ -170,29 +167,32 @@ public class ComputeSimilarityMatrixImage<O> implements Evaluator {
}
LinearScaling scale = new LinearScaling(zoom, -minmax.getMin() * zoom);
BufferedImage img = new BufferedImage(size, size, BufferedImage.TYPE_INT_RGB);
- for(int x = 0; x < size; x++) {
- DBID id1 = order.get(x);
- for(int y = x; y < size; y++) {
- DBID id2 = order.get(y);
- double ddist = dq.distance(id1, id2).doubleValue();
- if(ddist > 0.0) {
- ddist = scale.getScaled(ddist);
+ {
+ DBIDArrayIter id1 = order.iter();
+ DBIDArrayIter id2 = order.iter();
+ for(int x = 0; x < size && id1.valid(); x++, id1.advance()) {
+ id2.seek(id1.getOffset());
+ for(int y = x; y < size && id2.valid(); y++, id2.advance()) {
+ double ddist = dq.distance(id1, id2).doubleValue();
+ if(ddist > 0.0) {
+ ddist = scale.getScaled(ddist);
+ }
+ // Apply extra scaling
+ if(scaling != null) {
+ ddist = scaling.getScaled(ddist);
+ }
+ int dist = 0xFF & (int) (255 * ddist);
+ int col = 0xff000000 | (dist << 16) | (dist << 8) | dist;
+ img.setRGB(x, y, col);
+ img.setRGB(y, x, col);
}
- // Apply extra scaling
- if(scaling != null) {
- ddist = scaling.getScaled(ddist);
+ if(prog != null) {
+ prog.incrementProcessed(LOG);
}
- int dist = 0xFF & (int) (255 * ddist);
- int col = 0xff000000 | (dist << 16) | (dist << 8) | dist;
- img.setRGB(x, y, col);
- img.setRGB(y, x, col);
- }
- if(prog != null) {
- prog.incrementProcessed(logger);
}
}
if(prog != null) {
- prog.ensureCompleted(logger);
+ prog.ensureCompleted(LOG);
}
return new SimilarityMatrix(img, relation, order);
diff --git a/src/de/lmu/ifi/dbs/elki/gui/GUIUtil.java b/src/de/lmu/ifi/dbs/elki/gui/GUIUtil.java
new file mode 100644
index 00000000..11a2725f
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/gui/GUIUtil.java
@@ -0,0 +1,114 @@
+package de.lmu.ifi.dbs.elki.gui;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.awt.Toolkit;
+import java.lang.reflect.Method;
+
+import javax.swing.RepaintManager;
+import javax.swing.UIManager;
+
+import de.lmu.ifi.dbs.elki.logging.Logging;
+
+/**
+ * GUI utilities.
+ *
+ * @author Erich Schubert
+ */
+public final class GUIUtil {
+ /**
+ * Enable thread repaint debugging.
+ */
+ public static final boolean THREAD_REPAINT_DEBUG = false;
+
+ /**
+ * Whether to prefer the GTK look and feel on Unix.
+ */
+ public static final boolean PREFER_GTK = true;
+
+ /**
+ * Fake constructor. Do not instantiate.
+ */
+ private GUIUtil() {
+ // Static methods only - do not instantiate
+ }
+
+ /**
+ * Setup look at feel.
+ */
+ public static void setLookAndFeel() {
+ // If enabled, setup thread debugging.
+ if(THREAD_REPAINT_DEBUG) {
+ try {
+ Class<?> cls = ClassLoader.getSystemClassLoader().loadClass("org.jdesktop.swinghelper.debug.CheckThreadViolationRepaintManager");
+ RepaintManager.setCurrentManager((RepaintManager) cls.newInstance());
+ }
+ catch(Exception e) {
+ // ignore
+ }
+ }
+ if(PREFER_GTK) {
+ try {
+ Toolkit toolkit = Toolkit.getDefaultToolkit();
+ // Note: we don't want to *require* these classes
+ // But if they exist, we're going to try using them.
+ Class<?> suntoolkit = Class.forName("sun.awt.SunToolkit");
+ Method testm = suntoolkit.getMethod("isNativeGTKAvailable");
+ if(suntoolkit.isInstance(toolkit) && (Boolean) testm.invoke(toolkit)) {
+ UIManager.setLookAndFeel("com.sun.java.swing.plaf.gtk.GTKLookAndFeel");
+ return;
+ }
+ }
+ catch(Exception e) {
+ // ignore
+ }
+ }
+ try {
+ UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+ }
+ catch(Exception e) {
+ // ignore
+ }
+ }
+
+ /**
+ * Setup logging of uncaught exceptions.
+ *
+ * @param logger logger
+ */
+ public static void logUncaughtExceptions(final Logging logger) {
+ try {
+ Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
+ @Override
+ public void uncaughtException(Thread t, Throwable e) {
+ logger.exception(e);
+ }
+ });
+ }
+ catch(SecurityException e) {
+ logger.warning("Could not set the Default Uncaught Exception Handler", e);
+ }
+ }
+
+}
diff --git a/src/de/lmu/ifi/dbs/elki/gui/configurator/AbstractParameterConfigurator.java b/src/de/lmu/ifi/dbs/elki/gui/configurator/AbstractParameterConfigurator.java
index 2ce3bb08..977af410 100644
--- a/src/de/lmu/ifi/dbs/elki/gui/configurator/AbstractParameterConfigurator.java
+++ b/src/de/lmu/ifi/dbs/elki/gui/configurator/AbstractParameterConfigurator.java
@@ -48,7 +48,7 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.Parameter;
*
* @param <T> parameter type
*/
-public abstract class AbstractParameterConfigurator<T extends Parameter<?, ?>> implements ParameterConfigurator {
+public abstract class AbstractParameterConfigurator<T extends Parameter<?>> implements ParameterConfigurator {
/**
* The parameter to configure
*/
@@ -109,7 +109,7 @@ public abstract class AbstractParameterConfigurator<T extends Parameter<?, ?>> i
}
@Override
- public void addParameter(Object owner, Parameter<?, ?> param, TrackParameters track) {
+ public void addParameter(Object owner, Parameter<?> param, TrackParameters track) {
LoggingUtil.warning(this.getClass() + " does not support sub-parameters!");
}
diff --git a/src/de/lmu/ifi/dbs/elki/gui/configurator/AbstractSingleParameterConfigurator.java b/src/de/lmu/ifi/dbs/elki/gui/configurator/AbstractSingleParameterConfigurator.java
index 1d90f869..6d30ce25 100644
--- a/src/de/lmu/ifi/dbs/elki/gui/configurator/AbstractSingleParameterConfigurator.java
+++ b/src/de/lmu/ifi/dbs/elki/gui/configurator/AbstractSingleParameterConfigurator.java
@@ -30,9 +30,25 @@ import javax.swing.JLabel;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.Parameter;
-public abstract class AbstractSingleParameterConfigurator<T extends Parameter<?, ?>> extends AbstractParameterConfigurator<T> {
+/**
+ * Base class for MiniGUI input helpers
+ *
+ * @author Erich Schubert
+ *
+ * @param <T> Parameter type
+ */
+public abstract class AbstractSingleParameterConfigurator<T extends Parameter<?>> extends AbstractParameterConfigurator<T> {
+ /**
+ * Label
+ */
final JLabel label;
+ /**
+ * Constructor.
+ *
+ * @param param Parameter
+ * @param parent Parent edit control
+ */
public AbstractSingleParameterConfigurator(T param, JComponent parent) {
super(param, parent);
// Label
diff --git a/src/de/lmu/ifi/dbs/elki/gui/configurator/ClassListParameterConfigurator.java b/src/de/lmu/ifi/dbs/elki/gui/configurator/ClassListParameterConfigurator.java
index 65c93fb5..603a52a9 100644
--- a/src/de/lmu/ifi/dbs/elki/gui/configurator/ClassListParameterConfigurator.java
+++ b/src/de/lmu/ifi/dbs/elki/gui/configurator/ClassListParameterConfigurator.java
@@ -145,7 +145,7 @@ public class ClassListParameterConfigurator extends AbstractSingleParameterConfi
}
@Override
- public void addParameter(Object owner, Parameter<?, ?> param, TrackParameters track) {
+ public void addParameter(Object owner, Parameter<?> param, TrackParameters track) {
child.addParameter(owner, param, track);
}
diff --git a/src/de/lmu/ifi/dbs/elki/gui/configurator/ClassParameterConfigurator.java b/src/de/lmu/ifi/dbs/elki/gui/configurator/ClassParameterConfigurator.java
index 91913755..4fdcac10 100644
--- a/src/de/lmu/ifi/dbs/elki/gui/configurator/ClassParameterConfigurator.java
+++ b/src/de/lmu/ifi/dbs/elki/gui/configurator/ClassParameterConfigurator.java
@@ -99,7 +99,7 @@ public class ClassParameterConfigurator extends AbstractSingleParameterConfigura
}
@Override
- public void addParameter(Object owner, Parameter<?, ?> param, TrackParameters track) {
+ public void addParameter(Object owner, Parameter<?> param, TrackParameters track) {
child.addParameter(owner, param, track);
}
@@ -129,7 +129,7 @@ public class ClassParameterConfigurator extends AbstractSingleParameterConfigura
if(val.startsWith(DynamicParameters.STRING_USE_DEFAULT)) {
return null;
}
- if(val == DynamicParameters.STRING_OPTIONAL) {
+ if(DynamicParameters.STRING_OPTIONAL.equals(val)) {
return null;
}
return val;
diff --git a/src/de/lmu/ifi/dbs/elki/gui/configurator/ConfiguratorPanel.java b/src/de/lmu/ifi/dbs/elki/gui/configurator/ConfiguratorPanel.java
index d286be48..c21d11a4 100644
--- a/src/de/lmu/ifi/dbs/elki/gui/configurator/ConfiguratorPanel.java
+++ b/src/de/lmu/ifi/dbs/elki/gui/configurator/ConfiguratorPanel.java
@@ -24,6 +24,7 @@ package de.lmu.ifi.dbs.elki.gui.configurator;
*/
import java.awt.GridBagLayout;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
@@ -65,7 +66,7 @@ public class ConfiguratorPanel extends JPanel implements ChangeListener {
/**
* Child options
*/
- private java.util.Vector<ParameterConfigurator> children = new java.util.Vector<ParameterConfigurator>();
+ private ArrayList<ParameterConfigurator> children = new ArrayList<ParameterConfigurator>();
/**
* The event listeners for this panel.
@@ -85,12 +86,12 @@ public class ConfiguratorPanel extends JPanel implements ChangeListener {
* @param param Parameter to add
* @param track Parameter tracking object
*/
- public void addParameter(Object owner, Parameter<?, ?> param, TrackParameters track) {
+ public void addParameter(Object owner, Parameter<?> param, TrackParameters track) {
this.setBorder(new SoftBevelBorder(SoftBevelBorder.LOWERED));
ParameterConfigurator cfg = null;
- {
+ { // Find
Object cur = owner;
- while(cfg == null && cur != null) {
+ while(cur != null) {
cfg = childconfig.get(cur);
if(cfg != null) {
break;
@@ -109,7 +110,7 @@ public class ConfiguratorPanel extends JPanel implements ChangeListener {
}
}
- private ParameterConfigurator makeConfigurator(Parameter<?, ?> param) {
+ private ParameterConfigurator makeConfigurator(Parameter<?> param) {
if(param instanceof Flag) {
return new FlagParameterConfigurator((Flag) param, this);
}
diff --git a/src/de/lmu/ifi/dbs/elki/gui/configurator/EnumParameterConfigurator.java b/src/de/lmu/ifi/dbs/elki/gui/configurator/EnumParameterConfigurator.java
index 40748dcc..0e5740a8 100644
--- a/src/de/lmu/ifi/dbs/elki/gui/configurator/EnumParameterConfigurator.java
+++ b/src/de/lmu/ifi/dbs/elki/gui/configurator/EnumParameterConfigurator.java
@@ -96,7 +96,7 @@ public class EnumParameterConfigurator extends AbstractSingleParameterConfigurat
if(val.startsWith(DynamicParameters.STRING_USE_DEFAULT)) {
return null;
}
- if(val == DynamicParameters.STRING_OPTIONAL) {
+ if(DynamicParameters.STRING_OPTIONAL.equals(val)) {
return null;
}
return val;
diff --git a/src/de/lmu/ifi/dbs/elki/gui/configurator/FileParameterConfigurator.java b/src/de/lmu/ifi/dbs/elki/gui/configurator/FileParameterConfigurator.java
index bd34a995..f9355579 100644
--- a/src/de/lmu/ifi/dbs/elki/gui/configurator/FileParameterConfigurator.java
+++ b/src/de/lmu/ifi/dbs/elki/gui/configurator/FileParameterConfigurator.java
@@ -63,10 +63,11 @@ public class FileParameterConfigurator extends AbstractSingleParameterConfigurat
final JButton button;
/**
- * The actual file chooser
+ * Constructor.
+ *
+ * @param fp File parameter
+ * @param parent Component to attach to.
*/
- final JFileChooser fc = new JFileChooser();
-
public FileParameterConfigurator(FileParameter fp, JComponent parent) {
super(fp, parent);
// create components
@@ -78,17 +79,14 @@ public class FileParameterConfigurator extends AbstractSingleParameterConfigurat
button.addActionListener(this);
// fill with value
File f = null;
- if(fp.isDefined()) {
+ if (fp.isDefined()) {
f = fp.getValue();
}
- if(f != null) {
+ if (f != null) {
String fn = f.getPath();
textfield.setText(fn);
- fc.setSelectedFile(f);
- }
- else {
+ } else {
textfield.setText("");
- fc.setSelectedFile(null);
}
// make a panel
@@ -98,7 +96,7 @@ public class FileParameterConfigurator extends AbstractSingleParameterConfigurat
panel = new JPanel(new BorderLayout());
panel.add(textfield, BorderLayout.CENTER);
panel.add(button, BorderLayout.EAST);
-
+
parent.add(panel, constraints);
finishGridRow();
}
@@ -108,21 +106,24 @@ public class FileParameterConfigurator extends AbstractSingleParameterConfigurat
*/
@Override
public void actionPerformed(ActionEvent e) {
- if(e.getSource() == button) {
+ // Use a new JFileChooser. Inconsistent behaviour otherwise!
+ final JFileChooser fc = new JFileChooser(new File("."));
+ if (param.isDefined()) {
+ fc.setSelectedFile(param.getValue());
+ }
+
+ if (e.getSource() == button) {
int returnVal = fc.showOpenDialog(button);
- if(returnVal == JFileChooser.APPROVE_OPTION) {
+ if (returnVal == JFileChooser.APPROVE_OPTION) {
textfield.setText(fc.getSelectedFile().getPath());
fireValueChanged();
- }
- else {
+ } else {
// Do nothing on cancel.
}
- }
- else if(e.getSource() == textfield) {
+ } else if (e.getSource() == textfield) {
fireValueChanged();
- }
- else {
+ } else {
LoggingUtil.warning("actionPerformed triggered by unknown source: " + e.getSource());
}
}
@@ -131,4 +132,4 @@ public class FileParameterConfigurator extends AbstractSingleParameterConfigurat
public String getUserInput() {
return textfield.getText();
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/gui/configurator/FlagParameterConfigurator.java b/src/de/lmu/ifi/dbs/elki/gui/configurator/FlagParameterConfigurator.java
index 5c905e47..bec04b70 100644
--- a/src/de/lmu/ifi/dbs/elki/gui/configurator/FlagParameterConfigurator.java
+++ b/src/de/lmu/ifi/dbs/elki/gui/configurator/FlagParameterConfigurator.java
@@ -53,7 +53,7 @@ public class FlagParameterConfigurator extends AbstractParameterConfigurator<Fla
constraints.weightx = 1.0;
value = new JCheckBox(param.getName());
if(param.isDefined() && !param.tookDefaultValue()) {
- value.setSelected(param.getValue());
+ value.setSelected(param.isTrue());
}
value.setToolTipText(param.getShortDescription());
parent.add(value, constraints);
@@ -73,6 +73,6 @@ public class FlagParameterConfigurator extends AbstractParameterConfigurator<Fla
@Override
public Boolean getUserInput() {
- return value.isSelected() ? true : null;
+ return value.isSelected() ? Boolean.TRUE : null;
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/gui/configurator/ParameterConfigurator.java b/src/de/lmu/ifi/dbs/elki/gui/configurator/ParameterConfigurator.java
index 90c8f62f..9bd879f6 100644
--- a/src/de/lmu/ifi/dbs/elki/gui/configurator/ParameterConfigurator.java
+++ b/src/de/lmu/ifi/dbs/elki/gui/configurator/ParameterConfigurator.java
@@ -30,7 +30,7 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.TrackParame
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.Parameter;
public interface ParameterConfigurator {
- public void addParameter(Object owner, Parameter<?, ?> param, TrackParameters track);
+ public void addParameter(Object owner, Parameter<?> param, TrackParameters track);
public void addChangeListener(ChangeListener listener);
diff --git a/src/de/lmu/ifi/dbs/elki/gui/configurator/TextParameterConfigurator.java b/src/de/lmu/ifi/dbs/elki/gui/configurator/TextParameterConfigurator.java
index 8c011091..582381cb 100644
--- a/src/de/lmu/ifi/dbs/elki/gui/configurator/TextParameterConfigurator.java
+++ b/src/de/lmu/ifi/dbs/elki/gui/configurator/TextParameterConfigurator.java
@@ -43,10 +43,10 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.Parameter;
*/
// FIXME: update on focus loss?
// FIXME: restrictions for number input?
-public class TextParameterConfigurator extends AbstractSingleParameterConfigurator<Parameter<?, ?>> implements ActionListener {
+public class TextParameterConfigurator extends AbstractSingleParameterConfigurator<Parameter<?>> implements ActionListener {
final JTextField value;
- public TextParameterConfigurator(Parameter<?, ?> param, JComponent parent) {
+ public TextParameterConfigurator(Parameter<?> param, JComponent parent) {
super(param, parent);
// Input field
diff --git a/src/de/lmu/ifi/dbs/elki/gui/configurator/package-info.java b/src/de/lmu/ifi/dbs/elki/gui/configurator/package-info.java
index 004ad3da..5c8f2f4e 100644
--- a/src/de/lmu/ifi/dbs/elki/gui/configurator/package-info.java
+++ b/src/de/lmu/ifi/dbs/elki/gui/configurator/package-info.java
@@ -1,8 +1,8 @@
/**
* <p>Configurator components</p>
*
- * @apiviz.exclude javax.swing.event.*
- * @apiviz.exclude javax.awt.event.*
+ * @apiviz.exclude javax.swing.*
+ * @apiviz.exclude java.awt.event
*/
/*
This file is part of ELKI:
diff --git a/src/de/lmu/ifi/dbs/elki/gui/icons/StockIcon.java b/src/de/lmu/ifi/dbs/elki/gui/icons/StockIcon.java
index fc346331..4896bce0 100644
--- a/src/de/lmu/ifi/dbs/elki/gui/icons/StockIcon.java
+++ b/src/de/lmu/ifi/dbs/elki/gui/icons/StockIcon.java
@@ -40,55 +40,55 @@ import de.lmu.ifi.dbs.elki.logging.LoggingUtil;
* @apiviz.landmark
*/
public class StockIcon {
- public final static String DIALOG_ERROR = "dialog-error";
+ public static final String DIALOG_ERROR = "dialog-error";
- public final static String DIALOG_INFORMATION = "dialog-information";
+ public static final String DIALOG_INFORMATION = "dialog-information";
- public final static String DIALOG_WARNING = "dialog-warning";
+ public static final String DIALOG_WARNING = "dialog-warning";
- public final static String DOCUMENT_OPEN = "document-open";
+ public static final String DOCUMENT_OPEN = "document-open";
- public final static String DOCUMENT_PROPERTIES = "document-properties";
+ public static final String DOCUMENT_PROPERTIES = "document-properties";
- public final static String DOCUMENT_SAVE = "document-save";
+ public static final String DOCUMENT_SAVE = "document-save";
- public final static String EDIT_CLEAR = "edit-clear";
+ public static final String EDIT_CLEAR = "edit-clear";
- public final static String EDIT_REDO = "edit-redo";
+ public static final String EDIT_REDO = "edit-redo";
- public final static String EDIT_UNDO = "edit-undo";
+ public static final String EDIT_UNDO = "edit-undo";
- public final static String EMBLEM_IMPORTANT = "emblem-important";
+ public static final String EMBLEM_IMPORTANT = "emblem-important";
- public final static String GO_BOTTOM = "go-bottom";
+ public static final String GO_BOTTOM = "go-bottom";
- public final static String GO_DOWN = "go-down";
+ public static final String GO_DOWN = "go-down";
- public final static String GO_FIRST = "go-first";
+ public static final String GO_FIRST = "go-first";
- public final static String GO_HOME = "go-home";
+ public static final String GO_HOME = "go-home";
- public final static String GO_JUMP = "go-jump";
+ public static final String GO_JUMP = "go-jump";
- public final static String GO_LAST = "go-last";
+ public static final String GO_LAST = "go-last";
- public final static String GO_NEXT = "go-next";
+ public static final String GO_NEXT = "go-next";
- public final static String GO_PREVIOUS = "go-previous";
+ public static final String GO_PREVIOUS = "go-previous";
- public final static String GO_TOP = "go-top";
+ public static final String GO_TOP = "go-top";
- public final static String GO_UP = "go-up";
+ public static final String GO_UP = "go-up";
- public final static String HELP_BROWSER = "help-browser";
+ public static final String HELP_BROWSER = "help-browser";
- public final static String LIST_ADD = "list-add";
+ public static final String LIST_ADD = "list-add";
- public final static String LIST_REMOVE = "list-remove";
+ public static final String LIST_REMOVE = "list-remove";
- public final static String PROCESS_STOP = "process-stop";
+ public static final String PROCESS_STOP = "process-stop";
- private final static Map<String, SoftReference<Icon>> iconcache = new HashMap<String, SoftReference<Icon>>();
+ private static final Map<String, SoftReference<Icon>> iconcache = new HashMap<String, SoftReference<Icon>>();
/**
* Get a particular stock icon.
diff --git a/src/de/lmu/ifi/dbs/elki/gui/minigui/MiniGUI.java b/src/de/lmu/ifi/dbs/elki/gui/minigui/MiniGUI.java
index d9bf30c2..36010fc5 100644
--- a/src/de/lmu/ifi/dbs/elki/gui/minigui/MiniGUI.java
+++ b/src/de/lmu/ifi/dbs/elki/gui/minigui/MiniGUI.java
@@ -47,11 +47,12 @@ import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingWorker;
-import javax.swing.UIManager;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import de.lmu.ifi.dbs.elki.KDDTask;
+import de.lmu.ifi.dbs.elki.application.AbstractApplication;
+import de.lmu.ifi.dbs.elki.gui.GUIUtil;
import de.lmu.ifi.dbs.elki.gui.util.DynamicParameters;
import de.lmu.ifi.dbs.elki.gui.util.LogPanel;
import de.lmu.ifi.dbs.elki.gui.util.ParameterTable;
@@ -59,7 +60,9 @@ import de.lmu.ifi.dbs.elki.gui.util.ParametersModel;
import de.lmu.ifi.dbs.elki.gui.util.SavedSettingsFile;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.utilities.FormatUtil;
+import de.lmu.ifi.dbs.elki.utilities.exceptions.UnableToComplyException;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.ParameterException;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.UnspecifiedParameterException;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.SerializedParameterization;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.TrackParameters;
import de.lmu.ifi.dbs.elki.workflow.LoggingStep;
@@ -72,17 +75,12 @@ import de.lmu.ifi.dbs.elki.workflow.OutputStep;
*
* @apiviz.composedOf SettingsComboboxModel
* @apiviz.composedOf LoggingStep
- * @apiviz.owns de.lmu.ifi.dbs.elki.gui.util.ParameterTable
- * @apiviz.owns de.lmu.ifi.dbs.elki.gui.util.DynamicParameters
+ * @apiviz.owns ParameterTable
+ * @apiviz.owns DynamicParameters
*/
-public class MiniGUI extends JPanel {
+public class MiniGUI extends AbstractApplication {
/**
- * Serial version
- */
- private static final long serialVersionUID = 1L;
-
- /**
- * Filename for saved settings
+ * Filename for saved settings.
*/
public static final String SAVED_SETTINGS_FILENAME = "MiniGUI-saved-settings.txt";
@@ -92,9 +90,19 @@ public class MiniGUI extends JPanel {
public static final String NEWLINE = System.getProperty("line.separator");
/**
- * ELKI logger for the GUI
+ * ELKI logger for the GUI.
*/
- protected static final Logging logger = Logging.getLogger(MiniGUI.class);
+ private static final Logging LOG = Logging.getLogger(MiniGUI.class);
+
+ /**
+ * The frame
+ */
+ JFrame frame;
+
+ /**
+ * The main panel.
+ */
+ JPanel panel;
/**
* Logging output area.
@@ -102,27 +110,27 @@ public class MiniGUI extends JPanel {
protected LogPanel outputArea;
/**
- * The parameter table
+ * The parameter table.
*/
protected ParameterTable parameterTable;
/**
- * Parameter storage
+ * Parameter storage.
*/
protected DynamicParameters parameters;
/**
- * Settings storage
+ * Settings storage.
*/
protected SavedSettingsFile store = new SavedSettingsFile(SAVED_SETTINGS_FILENAME);
/**
- * Combo box for saved settings
+ * Combo box for saved settings.
*/
protected JComboBox savedCombo;
/**
- * Model to link the combobox with
+ * Model to link the combobox with.
*/
protected SettingsComboboxModel savedSettingsModel;
@@ -132,11 +140,23 @@ public class MiniGUI extends JPanel {
protected JButton runButton;
/**
- * Constructor
+ * Constructor.
*/
public MiniGUI() {
super();
- this.setLayout(new GridBagLayout());
+ // Create and set up the window.
+ frame = new JFrame("ELKI MiniGUI");
+ frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
+ try {
+ frame.setIconImage(new ImageIcon(KDDTask.class.getResource("elki-icon.png")).getImage());
+ }
+ catch(Exception e) {
+ // Ignore - icon not found is not fatal.
+ }
+
+ panel = new JPanel();
+ panel.setOpaque(true); // content panes must be opaque
+ panel.setLayout(new GridBagLayout());
{
// Button panel
@@ -180,7 +200,7 @@ public class MiniGUI extends JPanel {
store.save();
}
catch(IOException e1) {
- logger.exception(e1);
+ LOG.exception(e1);
}
savedSettingsModel.update();
}
@@ -198,7 +218,7 @@ public class MiniGUI extends JPanel {
store.save();
}
catch(IOException e1) {
- logger.exception(e1);
+ LOG.exception(e1);
}
savedCombo.setSelectedItem("[Saved Settings]");
savedSettingsModel.update();
@@ -223,7 +243,7 @@ public class MiniGUI extends JPanel {
constraints.gridy = 1;
constraints.weightx = 1.0;
constraints.weighty = 0.01;
- add(buttonPanel, constraints);
+ panel.add(buttonPanel, constraints);
}
{
@@ -250,7 +270,7 @@ public class MiniGUI extends JPanel {
constraints.gridy = 0;
constraints.weightx = 1;
constraints.weighty = 1;
- add(scrollPane, constraints);
+ panel.add(scrollPane, constraints);
}
{
@@ -268,16 +288,9 @@ public class MiniGUI extends JPanel {
constraints.gridy = 2;
constraints.weightx = 1;
constraints.weighty = 1;
- add(outputPane, constraints);
-
- // reconfigure logging
- outputArea.becomeDefaultLogger();
+ panel.add(outputPane, constraints);
}
- // refresh Parameters
- ArrayList<String> ps = new ArrayList<String>();
- doSetParameters(ps);
-
// load saved settings (we wanted to have the logger first!)
try {
store.load();
@@ -287,13 +300,16 @@ public class MiniGUI extends JPanel {
// Ignore - probably didn't save any settings yet.
}
catch(IOException e) {
- logger.exception(e);
+ LOG.exception(e);
}
+ // Finalize the frame.
+ frame.setContentPane(panel);
+ frame.pack();
}
/**
- * Serialize the parameter table and run setParameters()
+ * Serialize the parameter table and run setParameters().
*/
protected void updateParameterTable() {
parameterTable.setEnabled(false);
@@ -316,13 +332,11 @@ public class MiniGUI extends JPanel {
track.tryInstantiate(KDDTask.class);
config.logUnusedParameters();
// config.logAndClearReportedErrors();
- if(config.getErrors().size() > 0) {
+ final boolean hasErrors = (config.getErrors().size() > 0);
+ if(hasErrors && params.size() > 0) {
reportErrors(config);
- runButton.setEnabled(false);
- }
- else {
- runButton.setEnabled(true);
}
+ runButton.setEnabled(!hasErrors);
List<String> remainingParameters = config.getRemainingParameters();
@@ -336,7 +350,7 @@ public class MiniGUI extends JPanel {
remo.setValue(FormatUtil.format(remainingParameters, " "));
}
catch(ParameterException e) {
- logger.exception(e);
+ LOG.exception(e);
}
BitSet bits = new BitSet();
bits.set(DynamicParameters.BIT_INVALID);
@@ -344,6 +358,7 @@ public class MiniGUI extends JPanel {
parameters.addParameter(remo, remo.getValue(), bits, 0);
}
+ config.clearErrors();
parameterTable.revalidate();
parameterTable.setEnabled(true);
}
@@ -376,10 +391,10 @@ public class MiniGUI extends JPanel {
else {
reportErrors(config);
}
- logger.debug("Task completed successfully.");
+ LOG.debug("Task completed successfully.");
}
catch(Throwable e) {
- logger.exception("Task failed", e);
+ LOG.exception("Task failed", e);
}
return null;
}
@@ -399,52 +414,26 @@ public class MiniGUI extends JPanel {
* @param config Parameterization
*/
protected void reportErrors(SerializedParameterization config) {
- StringBuffer buf = new StringBuffer();
- buf.append("Could not run task because of configuration errors:" + NEWLINE + NEWLINE);
+ StringBuilder buf = new StringBuilder();
+ buf.append("Task is not completely configured:" + NEWLINE + NEWLINE);
for(ParameterException e : config.getErrors()) {
- buf.append(e.getMessage() + NEWLINE);
+ if(e instanceof UnspecifiedParameterException) {
+ buf.append("The parameter ");
+ buf.append(((UnspecifiedParameterException) e).getParameterName());
+ buf.append(" is required.").append(NEWLINE);
+ }
+ else {
+ buf.append(e.getMessage() + NEWLINE);
+ }
}
- logger.warning(buf.toString());
+ LOG.warning(buf.toString());
config.clearErrors();
}
- /**
- * Create the GUI and show it. For thread safety, this method should be
- * invoked from the event-dispatching thread.
- *
- * args Command line arguments
- */
- protected static void createAndShowGUI(String[] args) {
- // Create and set up the window.
- JFrame frame = new JFrame("ELKI MiniGUI");
- frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
- try {
- UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
- // Class<?> cls =
- // ClassLoader.getSystemClassLoader().loadClass("org.jdesktop.swinghelper.debug.CheckThreadViolationRepaintManager");
- // RepaintManager.setCurrentManager((RepaintManager) cls.newInstance());
- }
- catch(Exception e) {
- // ignore
- }
- try {
- frame.setIconImage(new ImageIcon(KDDTask.class.getResource("elki-icon.png")).getImage());
- }
- catch(Exception e) {
- // Ignore - icon not found is not fatal.
- }
-
- // Create and set up the content pane.
- MiniGUI newContentPane = new MiniGUI();
- if(args != null && args.length > 0) {
- newContentPane.doSetParameters(Arrays.asList(args));
- }
- newContentPane.setOpaque(true); // content panes must be opaque
- frame.setContentPane(newContentPane);
-
- // Display the window.
- frame.pack();
+ @Override
+ public void run() throws UnableToComplyException {
frame.setVisible(true);
+ outputArea.becomeDefaultLogger();
}
/**
@@ -453,17 +442,32 @@ public class MiniGUI extends JPanel {
* @param args command line parameters
*/
public static void main(final String[] args) {
+ GUIUtil.logUncaughtExceptions(LOG);
+ GUIUtil.setLookAndFeel();
OutputStep.setDefaultHandlerVisualizer();
+
javax.swing.SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
- createAndShowGUI(args);
+ try {
+ final MiniGUI gui = new MiniGUI();
+ gui.run();
+ if(args != null && args.length > 0) {
+ gui.doSetParameters(Arrays.asList(args));
+ }
+ else {
+ gui.doSetParameters(new ArrayList<String>());
+ }
+ }
+ catch(UnableToComplyException e) {
+ LOG.exception(e);
+ }
}
});
}
/**
- * Class to interface between the saved settings list and a JComboBox
+ * Class to interface between the saved settings list and a JComboBox.
*
* @author Erich Schubert
*
@@ -471,22 +475,22 @@ public class MiniGUI extends JPanel {
*/
class SettingsComboboxModel extends AbstractListModel implements ComboBoxModel {
/**
- * Serial version
+ * Serial version.
*/
private static final long serialVersionUID = 1L;
/**
- * Settings storage
+ * Settings storage.
*/
protected SavedSettingsFile store;
/**
- * Selected entry
+ * Selected entry.
*/
protected String selected = null;
/**
- * Constructor
+ * Constructor.
*
* @param store Store to access
*/
@@ -518,10 +522,10 @@ public class MiniGUI extends JPanel {
}
/**
- * Force an update
+ * Force an update.
*/
public void update() {
fireContentsChanged(this, 0, getSize() + 1);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/gui/multistep/MultiStepGUI.java b/src/de/lmu/ifi/dbs/elki/gui/multistep/MultiStepGUI.java
index 886612b7..820bc543 100644
--- a/src/de/lmu/ifi/dbs/elki/gui/multistep/MultiStepGUI.java
+++ b/src/de/lmu/ifi/dbs/elki/gui/multistep/MultiStepGUI.java
@@ -74,7 +74,7 @@ public class MultiStepGUI extends JPanel {
/**
* ELKI logger for the GUI
*/
- protected static final Logging logger = Logging.getLogger(MultiStepGUI.class);
+ private static final Logging LOG = Logging.getLogger(MultiStepGUI.class);
/**
* Logging output area.
@@ -142,11 +142,10 @@ public class MultiStepGUI extends JPanel {
settings.load();
}
catch(FileNotFoundException e) {
- logger.warning("Error loading saved settings.");
+ LOG.warning("Error loading saved settings.", e);
}
catch(IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
+ LOG.exception(e);
}
inputTab = new InputTabPanel();
diff --git a/src/de/lmu/ifi/dbs/elki/gui/multistep/panels/AlgorithmTabPanel.java b/src/de/lmu/ifi/dbs/elki/gui/multistep/panels/AlgorithmTabPanel.java
index c3a6f1d8..f6eb2955 100644
--- a/src/de/lmu/ifi/dbs/elki/gui/multistep/panels/AlgorithmTabPanel.java
+++ b/src/de/lmu/ifi/dbs/elki/gui/multistep/panels/AlgorithmTabPanel.java
@@ -92,23 +92,23 @@ public class AlgorithmTabPanel extends ParameterTabPanel implements Observer<Obj
}
@Override
- protected String getStatus() {
+ protected Status getStatus() {
if(algorithms == null) {
- return STATUS_UNCONFIGURED;
+ return Status.STATUS_UNCONFIGURED;
}
if(!input.canRun()) {
- return STATUS_CONFIGURED;
+ return Status.STATUS_CONFIGURED;
}
checkDependencies();
if(input.isComplete() && basedOnDatabase != null) {
if(algorithms.getResult() == null) {
- return STATUS_FAILED;
+ return Status.STATUS_FAILED;
}
else {
- return STATUS_COMPLETE;
+ return Status.STATUS_COMPLETE;
}
}
- return STATUS_READY;
+ return Status.STATUS_READY;
}
/**
diff --git a/src/de/lmu/ifi/dbs/elki/gui/multistep/panels/EvaluationTabPanel.java b/src/de/lmu/ifi/dbs/elki/gui/multistep/panels/EvaluationTabPanel.java
index c6d73133..9a74e250 100644
--- a/src/de/lmu/ifi/dbs/elki/gui/multistep/panels/EvaluationTabPanel.java
+++ b/src/de/lmu/ifi/dbs/elki/gui/multistep/panels/EvaluationTabPanel.java
@@ -79,7 +79,7 @@ public class EvaluationTabPanel extends ParameterTabPanel implements Observer<Ob
@Override
protected synchronized void configureStep(Parameterization config) {
evals = config.tryInstantiate(EvaluationStep.class);
- if(config.getErrors().size() > 0) {
+ if (config.getErrors().size() > 0) {
evals = null;
}
basedOnResult = null;
@@ -87,13 +87,13 @@ public class EvaluationTabPanel extends ParameterTabPanel implements Observer<Ob
@Override
protected void executeStep() {
- if(input.canRun() && !input.isComplete()) {
+ if (input.canRun() && !input.isComplete()) {
input.execute();
}
- if(algs.canRun() && !algs.isComplete()) {
+ if (algs.canRun() && !algs.isComplete()) {
algs.execute();
}
- if(!input.isComplete() || !algs.isComplete()) {
+ if (!input.isComplete() || !algs.isComplete()) {
throw new AbortException("Input data not available.");
}
// Get the database and run the algorithms
@@ -109,35 +109,35 @@ public class EvaluationTabPanel extends ParameterTabPanel implements Observer<Ob
* @return Evaluation step
*/
public EvaluationStep getEvaluationStep() {
- if(evals == null) {
+ if (evals == null) {
throw new AbortException("Evaluators not configured.");
}
return evals;
}
@Override
- protected String getStatus() {
- if(evals == null) {
- return STATUS_UNCONFIGURED;
+ protected Status getStatus() {
+ if (evals == null) {
+ return Status.STATUS_UNCONFIGURED;
}
- if(!input.canRun() || !algs.canRun()) {
- return STATUS_CONFIGURED;
+ if (!input.canRun() || !algs.canRun()) {
+ return Status.STATUS_CONFIGURED;
}
checkDependencies();
- if(input.isComplete() && algs.isComplete() && basedOnResult != null) {
- //if(evals.getResult() == null) {
- //return STATUS_FAILED;
- //}
- //else {
- return STATUS_COMPLETE;
- //}
+ if (input.isComplete() && algs.isComplete() && basedOnResult != null) {
+ // if(evals.getResult() == null) {
+ // return STATUS_FAILED;
+ // }
+ // else {
+ return Status.STATUS_COMPLETE;
+ // }
}
- return STATUS_READY;
+ return Status.STATUS_READY;
}
@Override
public void update(Object o) {
- if(o == input || o == algs) {
+ if (o == input || o == algs) {
checkDependencies();
updateStatus();
}
@@ -147,12 +147,12 @@ public class EvaluationTabPanel extends ParameterTabPanel implements Observer<Ob
* Test if the dependencies are still valid.
*/
private void checkDependencies() {
- if(basedOnResult != null) {
- if(!input.isComplete() || !algs.isComplete() || basedOnResult.get() != algs.getAlgorithmStep().getResult()) {
+ if (basedOnResult != null) {
+ if (!input.isComplete() || !algs.isComplete() || basedOnResult.get() != algs.getAlgorithmStep().getResult()) {
// We've become invalidated, notify.
basedOnResult = null;
observers.notifyObservers(this);
}
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/gui/multistep/panels/InputTabPanel.java b/src/de/lmu/ifi/dbs/elki/gui/multistep/panels/InputTabPanel.java
index fb6d9cae..9d88e900 100644
--- a/src/de/lmu/ifi/dbs/elki/gui/multistep/panels/InputTabPanel.java
+++ b/src/de/lmu/ifi/dbs/elki/gui/multistep/panels/InputTabPanel.java
@@ -77,16 +77,16 @@ public class InputTabPanel extends ParameterTabPanel {
}
@Override
- protected String getStatus() {
+ protected Status getStatus() {
if (input == null) {
- return STATUS_UNCONFIGURED;
+ return Status.STATUS_UNCONFIGURED;
}
if (executed) {
if (input.getDatabase() == null) {
- return "No database returned?";
+ return Status.STATUS_FAILED;
}
- return STATUS_COMPLETE;
+ return Status.STATUS_COMPLETE;
}
- return STATUS_READY;
+ return Status.STATUS_READY;
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/gui/multistep/panels/LoggingTabPanel.java b/src/de/lmu/ifi/dbs/elki/gui/multistep/panels/LoggingTabPanel.java
index 617008fc..5bf4c086 100644
--- a/src/de/lmu/ifi/dbs/elki/gui/multistep/panels/LoggingTabPanel.java
+++ b/src/de/lmu/ifi/dbs/elki/gui/multistep/panels/LoggingTabPanel.java
@@ -38,10 +38,10 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.StringParameter;
*/
public class LoggingTabPanel extends ParameterTabPanel {
/**
- * Serial version.
+ * Serial version.
*/
private static final long serialVersionUID = 1L;
-
+
/**
* Constructor.
*/
@@ -51,30 +51,30 @@ public class LoggingTabPanel extends ParameterTabPanel {
@Override
protected synchronized void configureStep(Parameterization config) {
- StringParameter debugParam = new StringParameter(OptionID.DEBUG, true);
+ StringParameter debugParam = new StringParameter(OptionID.DEBUG);
+ debugParam.setOptional(true);
Flag verboseFlag = new Flag(OptionID.VERBOSE_FLAG);
// Verbose mode is a lot simpler
- if (config.grab(verboseFlag) && verboseFlag.getValue()) {
+ if (config.grab(verboseFlag) && verboseFlag.isTrue()) {
LoggingConfiguration.setVerbose(true);
}
if (config.grab(debugParam)) {
try {
LoggingUtil.parseDebugParameter(debugParam);
- }
- catch(WrongParameterValueException e) {
+ } catch (WrongParameterValueException e) {
de.lmu.ifi.dbs.elki.logging.LoggingUtil.exception(e);
}
}
}
-
+
@Override
protected void executeStep() {
// Pass - we don't need to do anything
}
@Override
- protected String getStatus() {
+ protected Status getStatus() {
// We're always complete, too!
- return STATUS_COMPLETE;
+ return Status.STATUS_COMPLETE;
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/gui/multistep/panels/OutputTabPanel.java b/src/de/lmu/ifi/dbs/elki/gui/multistep/panels/OutputTabPanel.java
index 4cc84c8a..48ddc344 100644
--- a/src/de/lmu/ifi/dbs/elki/gui/multistep/panels/OutputTabPanel.java
+++ b/src/de/lmu/ifi/dbs/elki/gui/multistep/panels/OutputTabPanel.java
@@ -38,10 +38,10 @@ import de.lmu.ifi.dbs.elki.workflow.OutputStep;
*/
public class OutputTabPanel extends ParameterTabPanel implements Observer<Object> {
/**
- * Serial version.
+ * Serial version.
*/
private static final long serialVersionUID = 1L;
-
+
/**
* The data input configured
*/
@@ -56,12 +56,12 @@ public class OutputTabPanel extends ParameterTabPanel implements Observer<Object
* Input step to run on.
*/
private final InputTabPanel input;
-
+
/**
* Algorithm step to run on.
*/
private final EvaluationTabPanel evals;
-
+
/**
* Constructor. We depend on an input panel.
*
@@ -76,14 +76,14 @@ public class OutputTabPanel extends ParameterTabPanel implements Observer<Object
}
@Override
- protected synchronized void configureStep(Parameterization config) {
+ protected synchronized void configureStep(Parameterization config) {
outs = config.tryInstantiate(OutputStep.class);
if (config.getErrors().size() > 0) {
outs = null;
}
basedOnResult = null;
}
-
+
@Override
protected void executeStep() {
if (input.canRun() && !input.isComplete()) {
@@ -105,19 +105,19 @@ public class OutputTabPanel extends ParameterTabPanel implements Observer<Object
}
@Override
- protected String getStatus() {
+ protected Status getStatus() {
if (outs == null) {
- return STATUS_UNCONFIGURED;
+ return Status.STATUS_UNCONFIGURED;
}
if (!input.canRun() || !evals.canRun()) {
- return STATUS_CONFIGURED;
+ return Status.STATUS_CONFIGURED;
}
checkDependencies();
if (input.isComplete() && evals.isComplete() && basedOnResult != null) {
// TODO: is there a FAILED state here, too?
- return STATUS_COMPLETE;
+ return Status.STATUS_COMPLETE;
}
- return STATUS_READY;
+ return Status.STATUS_READY;
}
@Override
@@ -127,17 +127,17 @@ public class OutputTabPanel extends ParameterTabPanel implements Observer<Object
updateStatus();
}
}
-
+
/**
* Test if the dependencies are still valid.
*/
private void checkDependencies() {
- if(basedOnResult != null) {
- if(!input.isComplete() || !evals.isComplete() || basedOnResult.get() != evals.getEvaluationStep().getResult()) {
+ if (basedOnResult != null) {
+ if (!input.isComplete() || !evals.isComplete() || basedOnResult.get() != evals.getEvaluationStep().getResult()) {
// We've become invalidated, notify.
basedOnResult = null;
observers.notifyObservers(this);
}
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/gui/multistep/panels/ParameterTabPanel.java b/src/de/lmu/ifi/dbs/elki/gui/multistep/panels/ParameterTabPanel.java
index fd005de0..cb72e6cd 100644
--- a/src/de/lmu/ifi/dbs/elki/gui/multistep/panels/ParameterTabPanel.java
+++ b/src/de/lmu/ifi/dbs/elki/gui/multistep/panels/ParameterTabPanel.java
@@ -62,34 +62,58 @@ public abstract class ParameterTabPanel extends JPanel implements ChangeListener
private static final long serialVersionUID = 1L;
/**
- * Status to signal the step has been configured properly.
+ * Status code enumeration
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
*/
- public static final String STATUS_UNCONFIGURED = "unconfigured";
+ public enum Status {
+ /** Status to signal the step has been configured properly. */
+ STATUS_UNCONFIGURED {
+ @Override
+ public String toString() {
+ return "unconfigured";
+ }
+ },
- /**
- * Status to signal the step has been configured properly.
- */
- public static final String STATUS_CONFIGURED = "configured";
+ /** Status to signal the step has been configured properly. */
+ STATUS_CONFIGURED {
+ @Override
+ public String toString() {
+ return "configured";
+ }
+ },
- /**
- * Status to signal the step is ready to run
- */
- public static final String STATUS_READY = "ready to run";
+ /** Status to signal the step is ready to run */
+ STATUS_READY {
+ @Override
+ public String toString() {
+ return "ready to run";
+ }
+ },
- /**
- * Status to signal the step has been run completely.
- */
- public static final String STATUS_COMPLETE = "complete";
+ /** Status to signal the step has been run completely. */
+ STATUS_COMPLETE {
+ @Override
+ public String toString() {
+ return "complete";
+ }
+ },
- /**
- * Status to signal the step has failed somehow
- */
- public static final String STATUS_FAILED = "failed";
+ /** Status to signal the step has failed somehow */
+ STATUS_FAILED {
+ @Override
+ public String toString() {
+ return "failed";
+ }
+ }
+ }
/**
* ELKI logger for the GUI
*/
- protected static final Logging logger = Logging.getLogger(ParameterTabPanel.class);
+ private static final Logging LOG = Logging.getLogger(ParameterTabPanel.class);
/**
* The parameter table
@@ -154,7 +178,9 @@ public abstract class ParameterTabPanel extends JPanel implements ChangeListener
{
// Create parameter table
- parameterTable = new ConfiguratorPanel(); // new ParameterTable(parameterModel, parameters);
+ parameterTable = new ConfiguratorPanel(); // new
+ // ParameterTable(parameterModel,
+ // parameters);
parameterTable.addChangeListener(this);
// Create the scroll pane and add the table to it.
JScrollPane scrollPane = new JScrollPane(parameterTable);
@@ -179,7 +205,7 @@ public abstract class ParameterTabPanel extends JPanel implements ChangeListener
ListParameterization config = new ListParameterization();
parameterTable.appendParameters(config);
setParameters(config);
- if(config.getErrors().size() > 0) {
+ if (config.getErrors().size() > 0) {
reportErrors(config);
}
config.clearErrors();
@@ -198,12 +224,12 @@ public abstract class ParameterTabPanel extends JPanel implements ChangeListener
// update parameter table
{
parameterTable.setEnabled(false);
-
+
parameterTable.clear();
- for (Pair<Object, Parameter<?,?>> pair : track.getAllParameters()) {
+ for (Pair<Object, Parameter<?>> pair : track.getAllParameters()) {
parameterTable.addParameter(pair.first, pair.getSecond(), track);
}
- //parameters.updateFromTrackParameters(track);
+ // parameters.updateFromTrackParameters(track);
parameterTable.revalidate();
parameterTable.setEnabled(true);
@@ -213,7 +239,7 @@ public abstract class ParameterTabPanel extends JPanel implements ChangeListener
updateStatus();
observers.notifyObservers(this);
}
-
+
/**
* Collect parameters
*
@@ -229,17 +255,17 @@ public abstract class ParameterTabPanel extends JPanel implements ChangeListener
* @param config Parameterization
*/
protected void reportErrors(Parameterization config) {
- StringBuffer buf = new StringBuffer();
- for(ParameterException e : config.getErrors()) {
- if(e instanceof UnspecifiedParameterException) {
+ StringBuilder buf = new StringBuilder();
+ for (ParameterException e : config.getErrors()) {
+ if (e instanceof UnspecifiedParameterException) {
continue;
}
- buf.append(e.getMessage() + FormatUtil.NEWLINE);
+ buf.append(e.getMessage()).append(FormatUtil.NEWLINE);
}
- if(buf.length() > 0) {
- logger.warning("Configuration errors:" + FormatUtil.NEWLINE + FormatUtil.NEWLINE + buf.toString());
+ if (buf.length() > 0) {
+ LOG.warning("Configuration errors:" + FormatUtil.NEWLINE + FormatUtil.NEWLINE + buf.toString());
}
- //config.clearErrors();
+ // config.clearErrors();
}
/**
@@ -252,18 +278,17 @@ public abstract class ParameterTabPanel extends JPanel implements ChangeListener
try {
configureStep(config);
if (config.hasUnusedParameters()) {
- //List<Pair<OptionID, Object>> remainingParameters = config.getRemainingParameters();
- logger.warning("Unused parameters: "+"FIXME");
+ // List<Pair<OptionID, Object>> remainingParameters =
+ // config.getRemainingParameters();
+ LOG.warning("Unused parameters: " + "FIXME");
}
- if(config.getErrors().size() > 0) {
+ if (config.getErrors().size() > 0) {
reportErrors(config);
- }
- else {
+ } else {
executeStep();
}
- }
- catch(Exception e) {
- logger.exception(e);
+ } catch (Exception e) {
+ LOG.exception(e);
}
updateStatus();
observers.notifyObservers(this);
@@ -281,7 +306,7 @@ public abstract class ParameterTabPanel extends JPanel implements ChangeListener
*
* @return current status
*/
- protected abstract String getStatus();
+ protected abstract Status getStatus();
/**
* Execute the configured step.
@@ -294,8 +319,8 @@ public abstract class ParameterTabPanel extends JPanel implements ChangeListener
* @return can-run status
*/
public boolean canRun() {
- String status = getStatus();
- if(status == STATUS_READY || status == STATUS_COMPLETE) {
+ Status status = getStatus();
+ if (Status.STATUS_READY.equals(status) || Status.STATUS_COMPLETE.equals(status)) {
return true;
}
return false;
@@ -307,8 +332,8 @@ public abstract class ParameterTabPanel extends JPanel implements ChangeListener
* @return completeness status
*/
public boolean isComplete() {
- String status = getStatus();
- if(status == STATUS_COMPLETE) {
+ Status status = getStatus();
+ if (Status.STATUS_COMPLETE.equals(status)) {
return true;
}
return false;
@@ -318,7 +343,7 @@ public abstract class ParameterTabPanel extends JPanel implements ChangeListener
* Invoked to update the UI when the status could have changed.
*/
protected void updateStatus() {
- statusText.setText(getStatus());
+ statusText.setText(getStatus().toString());
runButton.setEnabled(canRun());
}
@@ -329,12 +354,12 @@ public abstract class ParameterTabPanel extends JPanel implements ChangeListener
public void removeObserver(Observer<? super ParameterTabPanel> o) {
observers.remove(o);
}
-
+
@Override
public void stateChanged(ChangeEvent e) {
if (e.getSource() == this.parameterTable) {
- //logger.warning("stateChanged!");
+ // logger.warning("stateChanged!");
updateParameterTable();
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/gui/multistep/panels/SavedSettingsTabPanel.java b/src/de/lmu/ifi/dbs/elki/gui/multistep/panels/SavedSettingsTabPanel.java
index 8777a95c..845df73c 100644
--- a/src/de/lmu/ifi/dbs/elki/gui/multistep/panels/SavedSettingsTabPanel.java
+++ b/src/de/lmu/ifi/dbs/elki/gui/multistep/panels/SavedSettingsTabPanel.java
@@ -58,7 +58,7 @@ public class SavedSettingsTabPanel extends JPanel {
/**
* Logger
*/
- protected static final Logging logger = Logging.getLogger(SavedSettingsTabPanel.class);
+ private static final Logging LOG = Logging.getLogger(SavedSettingsTabPanel.class);
/**
* The settings file to display.
@@ -139,7 +139,7 @@ public class SavedSettingsTabPanel extends JPanel {
store.save();
}
catch(IOException e1) {
- logger.exception(e1);
+ LOG.exception(e1);
}
savedSettingsModel.update();
}
@@ -157,7 +157,7 @@ public class SavedSettingsTabPanel extends JPanel {
store.save();
}
catch(IOException e1) {
- logger.exception(e1);
+ LOG.exception(e1);
}
savedCombo.setSelectedItem("[Saved Settings]");
savedSettingsModel.update();
diff --git a/src/de/lmu/ifi/dbs/elki/gui/util/DynamicParameters.java b/src/de/lmu/ifi/dbs/elki/gui/util/DynamicParameters.java
index dc442b07..e9c80da6 100644
--- a/src/de/lmu/ifi/dbs/elki/gui/util/DynamicParameters.java
+++ b/src/de/lmu/ifi/dbs/elki/gui/util/DynamicParameters.java
@@ -87,7 +87,7 @@ public class DynamicParameters {
* @apiviz.exclude
*/
public class Node {
- protected Parameter<?, ?> param;
+ protected Parameter<?> param;
protected String value;
@@ -103,7 +103,7 @@ public class DynamicParameters {
* @param flags Flags
* @param depth Depth (for tree representation)
*/
- public Node(Parameter<?, ?> param, String value, BitSet flags, int depth) {
+ public Node(Parameter<?> param, String value, BitSet flags, int depth) {
super();
this.param = param;
this.value = value;
@@ -141,54 +141,50 @@ public class DynamicParameters {
*/
public synchronized void updateFromTrackParameters(TrackParameters track) {
parameters.clear();
- for(Pair<Object, Parameter<?, ?>> p : track.getAllParameters()) {
- Parameter<?, ?> option = p.getSecond();
+ for (Pair<Object, Parameter<?>> p : track.getAllParameters()) {
+ Parameter<?> option = p.getSecond();
String value = null;
- if(option.isDefined()) {
- if(option.tookDefaultValue()) {
+ if (option.isDefined()) {
+ if (option.tookDefaultValue()) {
value = DynamicParameters.STRING_USE_DEFAULT + option.getDefaultValueAsString();
- }
- else {
+ } else {
value = option.getValueAsString();
}
}
- if(value == null) {
- if(option instanceof Flag) {
+ if (value == null) {
+ if (option instanceof Flag) {
value = Flag.NOT_SET;
- }
- else {
+ } else {
value = "";
}
}
BitSet bits = new BitSet();
- if(option.isOptional()) {
+ if (option.isOptional()) {
bits.set(BIT_OPTIONAL);
}
- if(option.hasDefaultValue() && option.tookDefaultValue()) {
+ if (option.hasDefaultValue() && option.tookDefaultValue()) {
bits.set(BIT_DEFAULT_VALUE);
}
- if(value == "") {
- if(!bits.get(BIT_DEFAULT_VALUE) && !bits.get(BIT_OPTIONAL)) {
+ if (value.length() <= 0) {
+ if (!bits.get(BIT_DEFAULT_VALUE) && !bits.get(BIT_OPTIONAL)) {
bits.set(BIT_INCOMPLETE);
}
- }
- if(value != "") {
+ } else {
try {
- if(!option.tookDefaultValue() && !option.isValid(value)) {
+ if (!option.tookDefaultValue() && !option.isValid(value)) {
bits.set(BIT_INVALID);
}
- }
- catch(ParameterException e) {
+ } catch (ParameterException e) {
bits.set(BIT_INVALID);
}
}
int depth = 0;
{
Object pos = track.getParent(option);
- while(pos != null) {
+ while (pos != null) {
pos = track.getParent(pos);
depth += 1;
- if(depth > 10) {
+ if (depth > 10) {
break;
}
}
@@ -206,7 +202,7 @@ public class DynamicParameters {
* @param bits Bits
* @param depth Depth
*/
- public synchronized void addParameter(Parameter<?, ?> option, String value, BitSet bits, int depth) {
+ public synchronized void addParameter(Parameter<?> option, String value, BitSet bits, int depth) {
Node t = new Node(option, value, bits, depth);
parameters.add(t);
}
@@ -218,22 +214,20 @@ public class DynamicParameters {
*/
public synchronized ArrayList<String> serializeParameters() {
ArrayList<String> p = new ArrayList<String>(2 * parameters.size());
- for(Node t : parameters) {
- if(t.param != null) {
- if(t.param instanceof RemainingOptions) {
- for(String str : t.value.split(" ")) {
- if(str.length() > 0) {
+ for (Node t : parameters) {
+ if (t.param != null) {
+ if (t.param instanceof RemainingOptions) {
+ for (String str : t.value.split(" ")) {
+ if (str.length() > 0) {
p.add(str);
}
}
- }
- else if(t.param instanceof Flag) {
- if(t.value == Flag.SET) {
+ } else if (t.param instanceof Flag) {
+ if (Flag.SET.equals(t.value)) {
p.add(SerializedParameterization.OPTION_PREFIX + t.param.getOptionID().getName());
}
- }
- else if(t.value != null && t.value.length() > 0) {
- if(!t.value.startsWith(STRING_USE_DEFAULT) && t.value != STRING_OPTIONAL) {
+ } else if (t.value != null && t.value.length() > 0) {
+ if (!t.value.startsWith(STRING_USE_DEFAULT) && !STRING_OPTIONAL.equals(t.value)) {
p.add(SerializedParameterization.OPTION_PREFIX + t.param.getOptionID().getName());
p.add(t.value);
}
@@ -256,7 +250,7 @@ public class DynamicParameters {
/**
* OptionID for unrecognized options.
*/
- protected static OptionID REMAINING_OPTIONS_ID = OptionID.getOrCreateOptionID("UNUSED", "Unrecognized options.");
+ protected static OptionID REMAINING_OPTIONS_ID = new OptionID("UNUSED", "Unrecognized options.");
/**
* Dummy option class that represents unhandled options
@@ -272,4 +266,4 @@ public class DynamicParameters {
super.setOptional(true);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/gui/util/LogPane.java b/src/de/lmu/ifi/dbs/elki/gui/util/LogPane.java
index 0c73dc98..dcc2cf5b 100644
--- a/src/de/lmu/ifi/dbs/elki/gui/util/LogPane.java
+++ b/src/de/lmu/ifi/dbs/elki/gui/util/LogPane.java
@@ -31,6 +31,7 @@ import java.util.logging.Level;
import java.util.logging.LogRecord;
import javax.swing.JTextPane;
+import javax.swing.text.BadLocationException;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
@@ -121,7 +122,7 @@ public class LogPane extends JTextPane {
try {
publish(new LogRecord(level, message));
}
- catch(Exception e) {
+ catch(BadLocationException e) {
throw new RuntimeException("Error writing a log-like message.", e);
}
}
@@ -132,7 +133,7 @@ public class LogPane extends JTextPane {
* @param record Log record
* @throws Exception
*/
- protected synchronized void publish(LogRecord record) throws Exception {
+ protected synchronized void publish(LogRecord record) throws BadLocationException {
// choose an appropriate formatter
final Formatter fmt;
final Style style;
diff --git a/src/de/lmu/ifi/dbs/elki/gui/util/ParameterTable.java b/src/de/lmu/ifi/dbs/elki/gui/util/ParameterTable.java
index e1f75bcf..a28d3f43 100644
--- a/src/de/lmu/ifi/dbs/elki/gui/util/ParameterTable.java
+++ b/src/de/lmu/ifi/dbs/elki/gui/util/ParameterTable.java
@@ -147,11 +147,11 @@ public class ParameterTable extends JTable {
return;
}
if(value instanceof DynamicParameters.Node) {
- Parameter<?, ?> o = ((DynamicParameters.Node) value).param;
+ Parameter<?> o = ((DynamicParameters.Node) value).param;
// Simulate a tree using indentation - there is no JTreeTable AFAICT
- StringBuffer buf = new StringBuffer();
+ StringBuilder buf = new StringBuilder();
for(int i = 1; i < ((DynamicParameters.Node) value).depth; i++) {
- buf.append(" ");
+ buf.append(' ');
}
buf.append(o.getOptionID().getName());
setText(buf.toString());
@@ -230,7 +230,7 @@ public class ParameterTable extends JTable {
comboBox.setSelectedIndex(0);
}
if(row < parameters.size()) {
- Parameter<?, ?> option = parameters.getNode(row).param;
+ Parameter<?> option = parameters.getNode(row).param;
// We can do dropdown choices for class parameters
if(option instanceof ClassParameter<?>) {
ClassParameter<?> cp = (ClassParameter<?>) option;
@@ -303,11 +303,6 @@ public class ParameterTable extends JTable {
final JButton button = new JButton("...");
/**
- * The actual file chooser
- */
- final JFileChooser fc = new JFileChooser();
-
- /**
* Constructor.
*/
public FileNameEditor() {
@@ -322,6 +317,11 @@ public class ParameterTable extends JTable {
*/
@Override
public void actionPerformed(ActionEvent e) {
+ final JFileChooser fc = new JFileChooser(new File("."));
+ final String curr = textfield.getText();
+ if (curr != null && curr.length() > 0) {
+ fc.setSelectedFile(new File(curr));
+ }
int returnVal = fc.showOpenDialog(button);
if(returnVal == JFileChooser.APPROVE_OPTION) {
@@ -347,7 +347,7 @@ public class ParameterTable extends JTable {
@Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
if(row < parameters.size()) {
- Parameter<?, ?> option = parameters.getNode(row).param;
+ Parameter<?> option = parameters.getNode(row).param;
if(option instanceof FileParameter) {
FileParameter fp = (FileParameter) option;
File f = null;
@@ -357,11 +357,9 @@ public class ParameterTable extends JTable {
if(f != null) {
String fn = f.getPath();
textfield.setText(fn);
- fc.setSelectedFile(f);
}
else {
textfield.setText("");
- fc.setSelectedFile(null);
}
}
}
@@ -519,7 +517,7 @@ public class ParameterTable extends JTable {
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
combo.removeAllItems();
if(row < parameters.size()) {
- Parameter<?, ?> option = parameters.getNode(row).param;
+ Parameter<?> option = parameters.getNode(row).param;
// We can do dropdown choices for class parameters
if(option instanceof ClassListParameter<?>) {
ClassListParameter<?> cp = (ClassListParameter<?>) option;
@@ -620,7 +618,7 @@ public class ParameterTable extends JTable {
}
}
if(row < parameters.size()) {
- Parameter<?, ?> option = parameters.getNode(row).param;
+ Parameter<?> option = parameters.getNode(row).param;
if(option instanceof Flag) {
activeEditor = dropdownEditor;
return dropdownEditor.getTableCellEditorComponent(table, value, isSelected, row, column);
diff --git a/src/de/lmu/ifi/dbs/elki/gui/util/ParametersModel.java b/src/de/lmu/ifi/dbs/elki/gui/util/ParametersModel.java
index bb899701..2f910977 100644
--- a/src/de/lmu/ifi/dbs/elki/gui/util/ParametersModel.java
+++ b/src/de/lmu/ifi/dbs/elki/gui/util/ParametersModel.java
@@ -42,7 +42,7 @@ public class ParametersModel extends AbstractTableModel {
/**
* Logger
*/
- private static final Logging logger = Logging.getLogger(ParametersModel.class);
+ private static final Logging LOG = Logging.getLogger(ParametersModel.class);
/**
* Parameter storage
@@ -131,7 +131,7 @@ public class ParametersModel extends AbstractTableModel {
}
}
else {
- logger.warning("Edited value is not a String!");
+ LOG.warning("Edited value is not a String!");
}
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/gui/util/SavedSettingsFile.java b/src/de/lmu/ifi/dbs/elki/gui/util/SavedSettingsFile.java
index daa2bab0..cf9f5d62 100644
--- a/src/de/lmu/ifi/dbs/elki/gui/util/SavedSettingsFile.java
+++ b/src/de/lmu/ifi/dbs/elki/gui/util/SavedSettingsFile.java
@@ -70,9 +70,9 @@ public class SavedSettingsFile implements Iterable<Pair<String, ArrayList<String
/**
* Save the current data to the given file.
*
- * @throws IOException thrown on output errors.
+ * @throws FileNotFoundException thrown on output errors.
*/
- public void save() throws IOException {
+ public void save() throws FileNotFoundException {
PrintStream p = new PrintStream(file);
p.println(COMMENT_PREFIX + "Saved ELKI settings. First line is title, remaining lines are parameters.");
for (Pair<String, ArrayList<String>> settings : store) {
diff --git a/src/de/lmu/ifi/dbs/elki/index/AbstractIndex.java b/src/de/lmu/ifi/dbs/elki/index/AbstractIndex.java
index 30966fd1..44418a3e 100644
--- a/src/de/lmu/ifi/dbs/elki/index/AbstractIndex.java
+++ b/src/de/lmu/ifi/dbs/elki/index/AbstractIndex.java
@@ -23,7 +23,7 @@ package de.lmu.ifi.dbs.elki.index;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.persistent.PageFileStatistics;
@@ -65,7 +65,7 @@ public abstract class AbstractIndex<O> implements Index {
}
@Override
- public void insert(DBID id) {
+ public void insert(DBIDRef id) {
throw new UnsupportedOperationException("This index does not allow dynamic updates.");
}
@@ -75,7 +75,7 @@ public abstract class AbstractIndex<O> implements Index {
}
@Override
- public boolean delete(DBID id) {
+ public boolean delete(DBIDRef id) {
throw new UnsupportedOperationException("This index does not allow dynamic updates.");
}
diff --git a/src/de/lmu/ifi/dbs/elki/index/AbstractRefiningIndex.java b/src/de/lmu/ifi/dbs/elki/index/AbstractRefiningIndex.java
index 1d42b7b3..33c9b341 100644
--- a/src/de/lmu/ifi/dbs/elki/index/AbstractRefiningIndex.java
+++ b/src/de/lmu/ifi/dbs/elki/index/AbstractRefiningIndex.java
@@ -23,21 +23,19 @@ package de.lmu.ifi.dbs.elki.index;
*/
import java.util.List;
-import java.util.Map;
import de.lmu.ifi.dbs.elki.database.ids.ArrayDBIDs;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
-import de.lmu.ifi.dbs.elki.database.query.DistanceDBIDResult;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.database.query.knn.AbstractDistanceKNNQuery;
-import de.lmu.ifi.dbs.elki.database.query.knn.KNNResult;
import de.lmu.ifi.dbs.elki.database.query.range.AbstractDistanceRangeQuery;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
import de.lmu.ifi.dbs.elki.persistent.PageFileStatistics;
-import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.KNNHeap;
/**
* Abstract base class for Filter-refinement indexes.
@@ -72,10 +70,10 @@ public abstract class AbstractRefiningIndex<O> extends AbstractIndex<O> implemen
* @param relation Relation to index
* @param ids database ids
*/
- abstract protected void initialize(Relation<O> relation, DBIDs ids);
+ protected abstract void initialize(Relation<O> relation, DBIDs ids);
/**
- * Refine a given object (and count the refinement!)
+ * Refine a given object (and count the refinement!).
*
* @param id Object id
* @return refined object
@@ -148,7 +146,7 @@ public abstract class AbstractRefiningIndex<O> extends AbstractIndex<O> implemen
* @param q Query object
* @return Distance
*/
- protected D refine(DBID id, O q) {
+ protected D refine(DBIDRef id, O q) {
AbstractRefiningIndex.this.refinements++;
return distanceQuery.distance(q, id);
}
@@ -168,7 +166,7 @@ public abstract class AbstractRefiningIndex<O> extends AbstractIndex<O> implemen
*
* @author Erich Schubert
*/
- abstract public class AbstractKNNQuery<D extends Distance<D>> extends AbstractDistanceKNNQuery<O, D> {
+ public abstract class AbstractKNNQuery<D extends Distance<D>> extends AbstractDistanceKNNQuery<O, D> {
/**
* Constructor.
*
@@ -184,11 +182,6 @@ public abstract class AbstractRefiningIndex<O> extends AbstractIndex<O> implemen
}
@Override
- public void getKNNForBulkHeaps(Map<DBID, KNNHeap<D>> heaps) {
- throw new UnsupportedOperationException("Not yet implemented.");
- }
-
- @Override
public KNNResult<D> getKNNForDBID(DBIDRef id, int k) {
return getKNNForObject(relation.get(id), k);
}
diff --git a/src/de/lmu/ifi/dbs/elki/index/Index.java b/src/de/lmu/ifi/dbs/elki/index/Index.java
index 9e77073c..1b866f5e 100644
--- a/src/de/lmu/ifi/dbs/elki/index/Index.java
+++ b/src/de/lmu/ifi/dbs/elki/index/Index.java
@@ -23,7 +23,7 @@ package de.lmu.ifi.dbs.elki.index;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.persistent.PageFileStatistics;
import de.lmu.ifi.dbs.elki.result.Result;
@@ -48,7 +48,7 @@ public interface Index extends Result {
*
* @param id the object to be inserted
*/
- public void insert(DBID id);
+ public void insert(DBIDRef id);
/**
* Inserts the specified objects into this index. If a bulk load mode is
@@ -64,7 +64,7 @@ public interface Index extends Result {
* @param id Object to remove
* @return true if this index did contain the object, false otherwise
*/
- public boolean delete(DBID id);
+ public boolean delete(DBIDRef id);
/**
* Deletes the specified objects from this index.
diff --git a/src/de/lmu/ifi/dbs/elki/index/package-info.java b/src/de/lmu/ifi/dbs/elki/index/package-info.java
index ca7dbe28..003a25bb 100644
--- a/src/de/lmu/ifi/dbs/elki/index/package-info.java
+++ b/src/de/lmu/ifi/dbs/elki/index/package-info.java
@@ -1,5 +1,8 @@
/**
* <p>Index structure implementations</p>
+ *
+ * @apiviz.exclude de.lmu.ifi.dbs.elki.index.*\.*\.Factory
+ * @apiviz.exclude de.lmu.ifi.dbs.elki.index.tree.TreeIndexFactory
*/
/*
This file is part of ELKI:
@@ -23,4 +26,4 @@
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package de.lmu.ifi.dbs.elki.index; \ No newline at end of file
+package de.lmu.ifi.dbs.elki.index;
diff --git a/src/de/lmu/ifi/dbs/elki/index/preprocessed/AbstractPreprocessorIndex.java b/src/de/lmu/ifi/dbs/elki/index/preprocessed/AbstractPreprocessorIndex.java
index 0160480f..95d0878d 100644
--- a/src/de/lmu/ifi/dbs/elki/index/preprocessed/AbstractPreprocessorIndex.java
+++ b/src/de/lmu/ifi/dbs/elki/index/preprocessed/AbstractPreprocessorIndex.java
@@ -41,12 +41,14 @@ import de.lmu.ifi.dbs.elki.logging.Logging;
*/
public abstract class AbstractPreprocessorIndex<O, R> extends AbstractIndex<O> {
/**
- * The data store
+ * The data store.
*/
protected WritableDataStore<R> storage = null;
/**
* Constructor.
+ *
+ * @param relation Relation to index
*/
public AbstractPreprocessorIndex(Relation<O> relation) {
super(relation);
@@ -57,5 +59,5 @@ public abstract class AbstractPreprocessorIndex<O, R> extends AbstractIndex<O> {
*
* @return Logger
*/
- abstract protected Logging getLogger();
+ protected abstract Logging getLogger();
}
diff --git a/src/de/lmu/ifi/dbs/elki/index/preprocessed/LocalProjectionIndex.java b/src/de/lmu/ifi/dbs/elki/index/preprocessed/LocalProjectionIndex.java
index 4e187a9c..7373646f 100644
--- a/src/de/lmu/ifi/dbs/elki/index/preprocessed/LocalProjectionIndex.java
+++ b/src/de/lmu/ifi/dbs/elki/index/preprocessed/LocalProjectionIndex.java
@@ -40,7 +40,7 @@ import de.lmu.ifi.dbs.elki.math.linearalgebra.ProjectionResult;
* @param <V> Vector type
* @param <P> Projection result type
*/
-public interface LocalProjectionIndex<V extends NumberVector<?, ?>, P extends ProjectionResult> extends Index {
+public interface LocalProjectionIndex<V extends NumberVector<?>, P extends ProjectionResult> extends Index {
/**
* Get the precomputed local projection for a particular object ID.
*
@@ -60,7 +60,7 @@ public interface LocalProjectionIndex<V extends NumberVector<?, ?>, P extends Pr
* @param <V> Vector type
* @param <I> Index type
*/
- public static interface Factory<V extends NumberVector<?, ?>, I extends LocalProjectionIndex<V, ?>> extends IndexFactory<V, I> {
+ public static interface Factory<V extends NumberVector<?>, I extends LocalProjectionIndex<V, ?>> extends IndexFactory<V, I> {
/**
* Instantiate the index for a given database.
*
diff --git a/src/de/lmu/ifi/dbs/elki/index/preprocessed/knn/AbstractMaterializeKNNPreprocessor.java b/src/de/lmu/ifi/dbs/elki/index/preprocessed/knn/AbstractMaterializeKNNPreprocessor.java
index 21e0abf7..7a64f294 100644
--- a/src/de/lmu/ifi/dbs/elki/index/preprocessed/knn/AbstractMaterializeKNNPreprocessor.java
+++ b/src/de/lmu/ifi/dbs/elki/index/preprocessed/knn/AbstractMaterializeKNNPreprocessor.java
@@ -31,11 +31,11 @@ import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.database.query.knn.KNNQuery;
-import de.lmu.ifi.dbs.elki.database.query.knn.KNNResult;
import de.lmu.ifi.dbs.elki.database.query.knn.PreprocessorKNNQuery;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
import de.lmu.ifi.dbs.elki.distance.distancefunction.EuclideanDistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
import de.lmu.ifi.dbs.elki.index.IndexFactory;
import de.lmu.ifi.dbs.elki.index.KNNIndex;
@@ -54,6 +54,7 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ObjectParameter;
*
* @param <O> Object type
* @param <D> Distance type
+ * @param <T> Result type
*/
public abstract class AbstractMaterializeKNNPreprocessor<O, D extends Distance<D>, T extends KNNResult<D>> extends AbstractPreprocessorIndex<O, T> implements KNNIndex<O> {
/**
@@ -155,8 +156,8 @@ public abstract class AbstractMaterializeKNNPreprocessor<O, D extends Distance<D
@SuppressWarnings("unchecked")
@Override
- public <S extends Distance<S>> KNNQuery<O, S> getKNNQuery(DistanceQuery<O, S> distanceQuery, Object... hints) {
- if(!this.distanceFunction.equals(distanceQuery.getDistanceFunction())) {
+ public <S extends Distance<S>> KNNQuery<O, S> getKNNQuery(DistanceQuery<O, S> distQ, Object... hints) {
+ if(!this.distanceFunction.equals(distQ.getDistanceFunction())) {
return null;
}
// k max supported?
@@ -185,7 +186,7 @@ public abstract class AbstractMaterializeKNNPreprocessor<O, D extends Distance<D
* @param <O> The object type
* @param <D> The distance type
*/
- public static abstract class Factory<O, D extends Distance<D>, T extends KNNResult<D>> implements IndexFactory<O, KNNIndex<O>> {
+ public abstract static class Factory<O, D extends Distance<D>, T extends KNNResult<D>> implements IndexFactory<O, KNNIndex<O>> {
/**
* Parameter to specify the number of nearest neighbors of an object to be
* materialized. must be an integer greater than 1.
@@ -193,7 +194,7 @@ public abstract class AbstractMaterializeKNNPreprocessor<O, D extends Distance<D
* Key: {@code -materialize.k}
* </p>
*/
- public static final OptionID K_ID = OptionID.getOrCreateOptionID("materialize.k", "The number of nearest neighbors of an object to be materialized.");
+ public static final OptionID K_ID = new OptionID("materialize.k", "The number of nearest neighbors of an object to be materialized.");
/**
* Parameter to indicate the distance function to be used to ascertain the
@@ -206,7 +207,7 @@ public abstract class AbstractMaterializeKNNPreprocessor<O, D extends Distance<D
* Key: {@code materialize.distance}
* </p>
*/
- public static final OptionID DISTANCE_FUNCTION_ID = OptionID.getOrCreateOptionID("materialize.distance", "the distance function to materialize the nearest neighbors");
+ public static final OptionID DISTANCE_FUNCTION_ID = new OptionID("materialize.distance", "the distance function to materialize the nearest neighbors");
/**
* Holds the value of {@link #K_ID}.
@@ -231,7 +232,7 @@ public abstract class AbstractMaterializeKNNPreprocessor<O, D extends Distance<D
}
@Override
- abstract public AbstractMaterializeKNNPreprocessor<O, D, T> instantiate(Relation<O> relation);
+ public abstract AbstractMaterializeKNNPreprocessor<O, D, T> instantiate(Relation<O> relation);
/**
* Get the distance function.
@@ -264,7 +265,7 @@ public abstract class AbstractMaterializeKNNPreprocessor<O, D extends Distance<D
*
* @apiviz.exclude
*/
- public static abstract class Parameterizer<O, D extends Distance<D>> extends AbstractParameterizer {
+ public abstract static class Parameterizer<O, D extends Distance<D>> extends AbstractParameterizer {
/**
* Holds the value of {@link #K_ID}.
*/
@@ -279,7 +280,8 @@ public abstract class AbstractMaterializeKNNPreprocessor<O, D extends Distance<D
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
// number of neighbors
- final IntParameter kP = new IntParameter(K_ID, new GreaterConstraint(1));
+ final IntParameter kP = new IntParameter(K_ID);
+ kP.addConstraint(new GreaterConstraint(1));
if(config.grab(kP)) {
k = kP.getValue();
}
@@ -292,7 +294,7 @@ public abstract class AbstractMaterializeKNNPreprocessor<O, D extends Distance<D
}
@Override
- abstract protected Factory<O, D, ?> makeInstance();
+ protected abstract Factory<O, D, ?> makeInstance();
}
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/index/preprocessed/knn/KNNJoinMaterializeKNNPreprocessor.java b/src/de/lmu/ifi/dbs/elki/index/preprocessed/knn/KNNJoinMaterializeKNNPreprocessor.java
index b7c3f0ac..bb947348 100644
--- a/src/de/lmu/ifi/dbs/elki/index/preprocessed/knn/KNNJoinMaterializeKNNPreprocessor.java
+++ b/src/de/lmu/ifi/dbs/elki/index/preprocessed/knn/KNNJoinMaterializeKNNPreprocessor.java
@@ -4,11 +4,11 @@ import de.lmu.ifi.dbs.elki.algorithm.KNNJoin;
import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
import de.lmu.ifi.dbs.elki.index.tree.spatial.SpatialEntry;
import de.lmu.ifi.dbs.elki.index.tree.spatial.rstarvariants.rstar.RStarTreeNode;
import de.lmu.ifi.dbs.elki.logging.Logging;
-import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.KNNList;
/*
This file is part of ELKI:
@@ -41,11 +41,11 @@ import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.KNNList;
* @param <V> vector type
* @param <D> distance type
*/
-public class KNNJoinMaterializeKNNPreprocessor<V extends NumberVector<V, ?>, D extends Distance<D>> extends AbstractMaterializeKNNPreprocessor<V, D, KNNList<D>> {
+public class KNNJoinMaterializeKNNPreprocessor<V extends NumberVector<?>, D extends Distance<D>> extends AbstractMaterializeKNNPreprocessor<V, D, KNNResult<D>> {
/**
* Logging class.
*/
- private static final Logging logger = Logging.getLogger(KNNJoinMaterializeKNNPreprocessor.class);
+ private static final Logging LOG = Logging.getLogger(KNNJoinMaterializeKNNPreprocessor.class);
/**
* Constructor.
@@ -67,7 +67,7 @@ public class KNNJoinMaterializeKNNPreprocessor<V extends NumberVector<V, ?>, D e
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
@Override
@@ -92,7 +92,7 @@ public class KNNJoinMaterializeKNNPreprocessor<V extends NumberVector<V, ?>, D e
* @param <O> The object type
* @param <D> The distance type
*/
- public static class Factory<O extends NumberVector<O, ?>, D extends Distance<D>> extends AbstractMaterializeKNNPreprocessor.Factory<O, D, KNNList<D>> {
+ public static class Factory<O extends NumberVector<?>, D extends Distance<D>> extends AbstractMaterializeKNNPreprocessor.Factory<O, D, KNNResult<D>> {
/**
* Constructor.
*
@@ -118,7 +118,7 @@ public class KNNJoinMaterializeKNNPreprocessor<V extends NumberVector<V, ?>, D e
* @param <O> Object type
* @param <D> Distance type
*/
- public static class Parameterizer<O extends NumberVector<O, ?>, D extends Distance<D>> extends AbstractMaterializeKNNPreprocessor.Factory.Parameterizer<O, D> {
+ public static class Parameterizer<O extends NumberVector<?>, D extends Distance<D>> extends AbstractMaterializeKNNPreprocessor.Factory.Parameterizer<O, D> {
@Override
protected KNNJoinMaterializeKNNPreprocessor.Factory<O, D> makeInstance() {
return new KNNJoinMaterializeKNNPreprocessor.Factory<O, D>(k, distanceFunction);
diff --git a/src/de/lmu/ifi/dbs/elki/index/preprocessed/knn/MaterializeKNNAndRKNNPreprocessor.java b/src/de/lmu/ifi/dbs/elki/index/preprocessed/knn/MaterializeKNNAndRKNNPreprocessor.java
index c200472b..b52b15af 100644
--- a/src/de/lmu/ifi/dbs/elki/index/preprocessed/knn/MaterializeKNNAndRKNNPreprocessor.java
+++ b/src/de/lmu/ifi/dbs/elki/index/preprocessed/knn/MaterializeKNNAndRKNNPreprocessor.java
@@ -24,10 +24,10 @@ package de.lmu.ifi.dbs.elki.index.preprocessed.knn;
*/
import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
-import java.util.Set;
-import java.util.SortedSet;
import java.util.TreeSet;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreFactory;
@@ -40,22 +40,27 @@ import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
+import de.lmu.ifi.dbs.elki.database.ids.DistanceDBIDPair;
+import de.lmu.ifi.dbs.elki.database.ids.DoubleDistanceDBIDPair;
+import de.lmu.ifi.dbs.elki.database.ids.HashSetModifiableDBIDs;
import de.lmu.ifi.dbs.elki.database.ids.SetDBIDs;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
-import de.lmu.ifi.dbs.elki.database.query.GenericDistanceResultPair;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
-import de.lmu.ifi.dbs.elki.database.query.knn.KNNResult;
import de.lmu.ifi.dbs.elki.database.query.rknn.PreprocessorRKNNQuery;
import de.lmu.ifi.dbs.elki.database.query.rknn.RKNNQuery;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.distance.DistanceUtil;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResultIter;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResultUtil;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.GenericDistanceDBIDList;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNHeap;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNUtil;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
import de.lmu.ifi.dbs.elki.index.RKNNIndex;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.logging.progress.FiniteProgress;
import de.lmu.ifi.dbs.elki.logging.progress.StepProgress;
-import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.KNNHeap;
-import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.KNNList;
import de.lmu.ifi.dbs.elki.utilities.documentation.Description;
import de.lmu.ifi.dbs.elki.utilities.documentation.Title;
@@ -64,21 +69,28 @@ import de.lmu.ifi.dbs.elki.utilities.documentation.Title;
* nearest neighbors (and their distances) to each database object.
*
* @author Elke Achtert
+ *
* @param <O> the type of database objects the preprocessor can be applied to
* @param <D> the type of distance the used distance function will return
*/
+// TODO: rewrite the double optimization. Maybe use a specialized subclass?
@Title("Materialize kNN and RkNN Neighborhood preprocessor")
@Description("Materializes the k nearest neighbors and the reverse k nearest neighbors of objects of a database.")
public class MaterializeKNNAndRKNNPreprocessor<O, D extends Distance<D>> extends MaterializeKNNPreprocessor<O, D> implements RKNNIndex<O> {
/**
* Logger to use.
*/
- private static final Logging logger = Logging.getLogger(MaterializeKNNAndRKNNPreprocessor.class);
+ private static final Logging LOG = Logging.getLogger(MaterializeKNNAndRKNNPreprocessor.class);
/**
* Additional data storage for RkNN.
*/
- private WritableDataStore<SortedSet<DistanceResultPair<D>>> materialized_RkNN;
+ private WritableDataStore<TreeSet<DistanceDBIDPair<D>>> materialized_RkNN;
+
+ /**
+ * Use optimizations for double values
+ */
+ protected boolean doubleOptimize;
/**
* Constructor.
@@ -89,12 +101,13 @@ public class MaterializeKNNAndRKNNPreprocessor<O, D extends Distance<D>> extends
*/
public MaterializeKNNAndRKNNPreprocessor(Relation<O> relation, DistanceFunction<? super O, D> distanceFunction, int k) {
super(relation, distanceFunction, k);
+ this.doubleOptimize = DistanceUtil.isDoubleDistanceFunction(distanceFunction);
}
@Override
protected void preprocess() {
createStorage();
- materialized_RkNN = DataStoreUtil.makeStorage(relation.getDBIDs(), DataStoreFactory.HINT_HOT, Set.class);
+ materialized_RkNN = DataStoreUtil.makeStorage(relation.getDBIDs(), DataStoreFactory.HINT_HOT, TreeSet.class);
FiniteProgress progress = getLogger().isVerbose() ? new FiniteProgress("Materializing k nearest neighbors and reverse k nearest neighbors (k=" + k + ")", relation.size(), getLogger()) : null;
materializeKNNAndRKNNs(DBIDUtil.ensureArray(relation.getDBIDs()), progress);
}
@@ -106,21 +119,22 @@ public class MaterializeKNNAndRKNNPreprocessor<O, D extends Distance<D>> extends
*/
private void materializeKNNAndRKNNs(ArrayDBIDs ids, FiniteProgress progress) {
// add an empty list to each rknn
- for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
+ Comparator<DistanceDBIDPair<D>> comp = DistanceDBIDResultUtil.distanceComparator();
+ for(DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
if(materialized_RkNN.get(iter) == null) {
- materialized_RkNN.put(iter, new TreeSet<DistanceResultPair<D>>());
+ materialized_RkNN.put(iter, new TreeSet<DistanceDBIDPair<D>>(comp));
}
}
// knn query
- List<KNNResult<D>> kNNList = knnQuery.getKNNForBulkDBIDs(ids, k);
- for(int i = 0; i < ids.size(); i++) {
- DBID id = ids.get(i);
+ List<? extends KNNResult<D>> kNNList = knnQuery.getKNNForBulkDBIDs(ids, k);
+ int i = 0;
+ for(DBIDIter id = ids.iter(); id.valid(); id.advance(), i++) {
KNNResult<D> kNNs = kNNList.get(i);
storage.put(id, kNNs);
- for(DistanceResultPair<D> kNN : kNNs) {
- Set<DistanceResultPair<D>> rknns = materialized_RkNN.get(kNN);
- rknns.add(new GenericDistanceResultPair<D>(kNN.getDistance(), id));
+ for(DistanceDBIDResultIter<D> iter = kNNs.iter(); iter.valid(); iter.advance()) {
+ TreeSet<DistanceDBIDPair<D>> rknns = materialized_RkNN.get(iter);
+ rknns.add(makePair(iter, id));
}
if(progress != null) {
progress.incrementProcessed(getLogger());
@@ -132,6 +146,14 @@ public class MaterializeKNNAndRKNNPreprocessor<O, D extends Distance<D>> extends
}
}
+ @SuppressWarnings("unchecked")
+ private DistanceDBIDPair<D> makePair(DistanceDBIDResultIter<D> iter, DBIDIter id) {
+ if(doubleOptimize) {
+ return (DistanceDBIDPair<D>) DBIDUtil.newDistancePair(((DoubleDistanceDBIDPair) iter.getDistancePair()).doubleDistance(), id);
+ }
+ return DBIDUtil.newDistancePair(iter.getDistance(), id);
+ }
+
@Override
protected void objectsInserted(DBIDs ids) {
StepProgress stepprog = getLogger().isVerbose() ? new StepProgress(3) : null;
@@ -171,34 +193,37 @@ public class MaterializeKNNAndRKNNPreprocessor<O, D extends Distance<D>> extends
private ArrayDBIDs updateKNNsAndRkNNs(DBIDs ids) {
ArrayModifiableDBIDs rkNN_ids = DBIDUtil.newArray();
DBIDs oldids = DBIDUtil.difference(relation.getDBIDs(), ids);
- for (DBIDIter iter = oldids.iter(); iter.valid(); iter.advance()) {
- KNNResult<D> kNNs = storage.get(iter);
- D knnDist = kNNs.getKNNDistance();
+ for(DBIDIter id = oldids.iter(); id.valid(); id.advance()) {
+ KNNResult<D> oldkNNs = storage.get(id);
+ D knnDist = oldkNNs.getKNNDistance();
// look for new kNNs
KNNHeap<D> heap = null;
- for (DBIDIter iter2 = ids.iter(); iter2.valid(); iter2.advance()) {
- D dist = distanceQuery.distance(iter, iter2);
+ for(DBIDIter newid = ids.iter(); newid.valid(); newid.advance()) {
+ D dist = distanceQuery.distance(id, newid);
if(dist.compareTo(knnDist) <= 0) {
+ // New id changes the kNNs of oldid.
if(heap == null) {
- heap = new KNNHeap<D>(k);
- heap.addAll(kNNs);
+ heap = KNNUtil.newHeap(oldkNNs);
}
- heap.add(dist, iter2);
+ heap.add(dist, newid);
}
}
+ // kNNs for oldid have changed:
if(heap != null) {
- KNNList<D> newKNNs = heap.toKNNList();
- storage.put(iter, newKNNs);
+ KNNResult<D> newkNNs = heap.toKNNList();
+ storage.put(id, newkNNs);
// get the difference
int i = 0;
int j = 0;
- List<DistanceResultPair<D>> added = new ArrayList<DistanceResultPair<D>>();
- List<DistanceResultPair<D>> removed = new ArrayList<DistanceResultPair<D>>();
- while(i < kNNs.size() && j < newKNNs.size()) {
- DistanceResultPair<D> drp1 = kNNs.get(i);
- DistanceResultPair<D> drp2 = newKNNs.get(j);
- if(!drp1.sameDBID(drp2)) {
+ GenericDistanceDBIDList<D> added = new GenericDistanceDBIDList<D>();
+ GenericDistanceDBIDList<D> removed = new GenericDistanceDBIDList<D>();
+ // TODO: use iterators.
+ while(i < oldkNNs.size() && j < newkNNs.size()) {
+ DistanceDBIDPair<D> drp1 = oldkNNs.get(i);
+ DistanceDBIDPair<D> drp2 = newkNNs.get(j);
+ // NOTE: we assume that on ties they are ordered the same way!
+ if(!DBIDUtil.equal(drp1, drp2)) {
added.add(drp2);
j++;
}
@@ -208,21 +233,25 @@ public class MaterializeKNNAndRKNNPreprocessor<O, D extends Distance<D>> extends
}
}
if(i != j) {
- for(; i < kNNs.size(); i++)
- removed.add(kNNs.get(i));
+ for(; i < oldkNNs.size(); i++) {
+ removed.add(oldkNNs.get(i));
+ }
+ for(; j < newkNNs.size(); i++) {
+ added.add(newkNNs.get(i));
+ }
}
// add new RkNN
- for(DistanceResultPair<D> drp : added) {
- Set<DistanceResultPair<D>> rknns = materialized_RkNN.get(drp);
- rknns.add(new GenericDistanceResultPair<D>(drp.getDistance(), iter.getDBID()));
+ for(DistanceDBIDResultIter<D> newnn = added.iter(); newnn.valid(); newnn.advance()) {
+ TreeSet<DistanceDBIDPair<D>> rknns = materialized_RkNN.get(newnn);
+ rknns.add(makePair(newnn, id));
}
// remove old RkNN
- for(DistanceResultPair<D> drp : removed) {
- Set<DistanceResultPair<D>> rknns = materialized_RkNN.get(drp);
- rknns.remove(new GenericDistanceResultPair<D>(drp.getDistance(), iter.getDBID()));
+ for(DistanceDBIDResultIter<D> oldnn = removed.iter(); oldnn.valid(); oldnn.advance()) {
+ TreeSet<DistanceDBIDPair<D>> rknns = materialized_RkNN.get(oldnn);
+ rknns.remove(makePair(oldnn, id));
}
- rkNN_ids.add(iter);
+ rkNN_ids.add(id);
}
}
return rkNN_ids;
@@ -237,39 +266,43 @@ public class MaterializeKNNAndRKNNPreprocessor<O, D extends Distance<D>> extends
if(stepprog != null) {
stepprog.beginStep(1, "New deletions ocurred, remove their materialized kNNs and RkNNs.", getLogger());
}
+ // Temporary storage of removed lists
List<KNNResult<D>> kNNs = new ArrayList<KNNResult<D>>(ids.size());
- List<List<DistanceResultPair<D>>> rkNNs = new ArrayList<List<DistanceResultPair<D>>>(ids.size());
- for (DBIDIter iter = aids.iter(); iter.valid(); iter.advance()) {
+ List<TreeSet<DistanceDBIDPair<D>>> rkNNs = new ArrayList<TreeSet<DistanceDBIDPair<D>>>(ids.size());
+ for(DBIDIter iter = aids.iter(); iter.valid(); iter.advance()) {
kNNs.add(storage.get(iter));
storage.delete(iter);
- rkNNs.add(new ArrayList<DistanceResultPair<D>>(materialized_RkNN.get(iter)));
+ rkNNs.add(materialized_RkNN.get(iter));
materialized_RkNN.delete(iter);
}
- ArrayDBIDs kNN_ids = extractAndRemoveIDs(kNNs, aids);
- ArrayDBIDs rkNN_ids = extractAndRemoveIDs(rkNNs, aids);
+ // Keep only those IDs not also removed
+ ArrayDBIDs kNN_ids = affectedkNN(kNNs, aids);
+ ArrayDBIDs rkNN_ids = affectedRkNN(rkNNs, aids);
// update the affected kNNs and RkNNs
if(stepprog != null) {
stepprog.beginStep(2, "New deletions ocurred, update the affected kNNs and RkNNs.", getLogger());
}
- // update the kNNs of the RkNNs
- List<KNNResult<D>> kNNList = knnQuery.getKNNForBulkDBIDs(rkNN_ids, k);
- for(int i = 0; i < rkNN_ids.size(); i++) {
- DBID id = rkNN_ids.get(i);
- storage.put(id, kNNList.get(i));
- for(DistanceResultPair<D> kNN : kNNList.get(i)) {
- materialized_RkNN.get(kNN).add(new GenericDistanceResultPair<D>(kNN.getDistance(), id));
+ // Recompute the kNN for affected objects (in rkNN lists)
+ {
+ List<? extends KNNResult<D>> kNNList = knnQuery.getKNNForBulkDBIDs(rkNN_ids, k);
+ int i = 0;
+ for(DBIDIter reknn = rkNN_ids.iter(); reknn.valid(); reknn.advance(), i++) {
+ storage.put(reknn, kNNList.get(i));
+ for(DistanceDBIDResultIter<D> it = kNNList.get(i).iter(); it.valid(); it.advance()) {
+ materialized_RkNN.get(it).add(makePair(it, reknn));
+ }
}
}
- // update the RkNNs of the kNNs
- SetDBIDs idsSet = DBIDUtil.ensureSet(ids);
- for(int i = 0; i < kNN_ids.size(); i++) {
- DBID id = kNN_ids.get(i);
- SortedSet<DistanceResultPair<D>> rkNN = materialized_RkNN.get(id);
- for(Iterator<DistanceResultPair<D>> it = rkNN.iterator(); it.hasNext();) {
- DistanceResultPair<D> drp = it.next();
- if(idsSet.contains(drp)) {
- it.remove();
+ // remove objects from RkNNs of obejcts (in kNN lists)
+ {
+ SetDBIDs idsSet = DBIDUtil.ensureSet(ids);
+ for(DBIDIter nn = kNN_ids.iter(); nn.valid(); nn.advance()) {
+ TreeSet<DistanceDBIDPair<D>> rkNN = materialized_RkNN.get(nn);
+ for(Iterator<DistanceDBIDPair<D>> it = rkNN.iterator(); it.hasNext();) {
+ if(idsSet.contains(it.next())) {
+ it.remove();
+ }
}
}
}
@@ -286,6 +319,44 @@ public class MaterializeKNNAndRKNNPreprocessor<O, D extends Distance<D>> extends
}
/**
+ * Extracts and removes the DBIDs in the given collections.
+ *
+ * @param extraxt a list of lists of DistanceResultPair to extract
+ * @param remove the ids to remove
+ * @return the DBIDs in the given collection
+ */
+ protected ArrayDBIDs affectedkNN(List<? extends KNNResult<D>> extraxt, DBIDs remove) {
+ HashSetModifiableDBIDs ids = DBIDUtil.newHashSet();
+ for(KNNResult<D> drps : extraxt) {
+ for(DBIDIter iter = drps.iter(); iter.valid(); iter.advance()) {
+ ids.add(iter);
+ }
+ }
+ ids.removeDBIDs(remove);
+ // Convert back to array
+ return DBIDUtil.newArray(ids);
+ }
+
+ /**
+ * Extracts and removes the DBIDs in the given collections.
+ *
+ * @param extraxt a list of lists of DistanceResultPair to extract
+ * @param remove the ids to remove
+ * @return the DBIDs in the given collection
+ */
+ protected ArrayDBIDs affectedRkNN(List<? extends Collection<DistanceDBIDPair<D>>> extraxt, DBIDs remove) {
+ HashSetModifiableDBIDs ids = DBIDUtil.newHashSet();
+ for(Collection<DistanceDBIDPair<D>> drps : extraxt) {
+ for(DistanceDBIDPair<D> drp : drps) {
+ ids.add(drp);
+ }
+ }
+ ids.removeDBIDs(remove);
+ // Convert back to array
+ return DBIDUtil.newArray(ids);
+ }
+
+ /**
* Returns the materialized kNNs of the specified id.
*
* @param id the query id
@@ -301,11 +372,17 @@ public class MaterializeKNNAndRKNNPreprocessor<O, D extends Distance<D>> extends
* @param id the query id
* @return the RkNNs
*/
- public List<DistanceResultPair<D>> getRKNN(DBIDRef id) {
- SortedSet<DistanceResultPair<D>> rKNN = materialized_RkNN.get(id);
- if(rKNN == null)
+ public GenericDistanceDBIDList<D> getRKNN(DBIDRef id) {
+ TreeSet<DistanceDBIDPair<D>> rKNN = materialized_RkNN.get(id);
+ if(rKNN == null) {
return null;
- return new ArrayList<DistanceResultPair<D>>(rKNN);
+ }
+ GenericDistanceDBIDList<D> ret = new GenericDistanceDBIDList<D>(rKNN.size());
+ for(DistanceDBIDPair<D> pair : rKNN) {
+ ret.add(pair);
+ }
+ ret.sort();
+ return ret;
}
@SuppressWarnings("unchecked")
@@ -338,7 +415,7 @@ public class MaterializeKNNAndRKNNPreprocessor<O, D extends Distance<D>> extends
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
diff --git a/src/de/lmu/ifi/dbs/elki/index/preprocessed/knn/MaterializeKNNPreprocessor.java b/src/de/lmu/ifi/dbs/elki/index/preprocessed/knn/MaterializeKNNPreprocessor.java
index cdc3fce4..f792833e 100644
--- a/src/de/lmu/ifi/dbs/elki/index/preprocessed/knn/MaterializeKNNPreprocessor.java
+++ b/src/de/lmu/ifi/dbs/elki/index/preprocessed/knn/MaterializeKNNPreprocessor.java
@@ -23,31 +23,28 @@ package de.lmu.ifi.dbs.elki.index.preprocessed.knn;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.util.Collection;
import java.util.List;
import javax.swing.event.EventListenerList;
import de.lmu.ifi.dbs.elki.database.ids.ArrayDBIDs;
import de.lmu.ifi.dbs.elki.database.ids.ArrayModifiableDBIDs;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
-import de.lmu.ifi.dbs.elki.database.ids.HashSetModifiableDBIDs;
import de.lmu.ifi.dbs.elki.database.ids.SetDBIDs;
import de.lmu.ifi.dbs.elki.database.query.DatabaseQuery;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
import de.lmu.ifi.dbs.elki.database.query.knn.KNNQuery;
-import de.lmu.ifi.dbs.elki.database.query.knn.KNNResult;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNHeap;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNUtil;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
-import de.lmu.ifi.dbs.elki.index.preprocessed.knn.KNNChangeEvent.Type;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.logging.progress.FiniteProgress;
import de.lmu.ifi.dbs.elki.logging.progress.StepProgress;
-import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.KNNHeap;
import de.lmu.ifi.dbs.elki.utilities.documentation.Description;
import de.lmu.ifi.dbs.elki.utilities.documentation.Title;
@@ -72,7 +69,7 @@ public class MaterializeKNNPreprocessor<O, D extends Distance<D>> extends Abstra
/**
* Logger to use.
*/
- private static final Logging logger = Logging.getLogger(MaterializeKNNPreprocessor.class);
+ private static final Logging LOG = Logging.getLogger(MaterializeKNNPreprocessor.class);
/**
* Flag to use bulk operations.
@@ -114,12 +111,12 @@ public class MaterializeKNNPreprocessor<O, D extends Distance<D>> extends Abstra
FiniteProgress progress = getLogger().isVerbose() ? new FiniteProgress("Materializing k nearest neighbors (k=" + k + ")", ids.size(), getLogger()) : null;
// Try bulk
- List<KNNResult<D>> kNNList = null;
+ List<? extends KNNResult<D>> kNNList = null;
if(usebulk) {
kNNList = knnQuery.getKNNForBulkDBIDs(ids, k);
if(kNNList != null) {
- for(int i = 0; i < ids.size(); i++) {
- DBID id = ids.get(i);
+ int i = 0;
+ for(DBIDIter id = ids.iter(); id.valid(); id.advance(), i++) {
storage.put(id, kNNList.get(i));
if(progress != null) {
progress.incrementProcessed(getLogger());
@@ -128,7 +125,7 @@ public class MaterializeKNNPreprocessor<O, D extends Distance<D>> extends Abstra
}
}
else {
- for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
+ for(DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
KNNResult<D> knn = knnQuery.getKNNForDBID(iter, k);
storage.put(iter, knn);
if(progress != null) {
@@ -143,22 +140,23 @@ public class MaterializeKNNPreprocessor<O, D extends Distance<D>> extends Abstra
}
@Override
- public final void insert(DBID id) {
- objectsInserted(id);
+ public final void insert(DBIDRef id) {
+ objectsInserted(DBIDUtil.deref(id));
}
@Override
public void insertAll(DBIDs ids) {
if(storage == null && ids.size() > 0) {
preprocess();
- } else {
+ }
+ else {
objectsInserted(ids);
}
}
@Override
- public boolean delete(DBID id) {
- objectsRemoved(id);
+ public boolean delete(DBIDRef id) {
+ objectsRemoved(DBIDUtil.deref(id));
return true;
}
@@ -181,10 +179,12 @@ public class MaterializeKNNPreprocessor<O, D extends Distance<D>> extends Abstra
if(stepprog != null) {
stepprog.beginStep(1, "New insertions ocurred, materialize their new kNNs.", getLogger());
}
- List<KNNResult<D>> kNNList = knnQuery.getKNNForBulkDBIDs(aids, k);
- for(int i = 0; i < aids.size(); i++) {
- DBID id = aids.get(i);
- storage.put(id, kNNList.get(i));
+ // Bulk-query kNNs
+ List<? extends KNNResult<D>> kNNList = knnQuery.getKNNForBulkDBIDs(aids, k);
+ // Store in storage
+ DBIDIter iter = aids.iter();
+ for(int i = 0; i < aids.size(); i++, iter.advance()) {
+ storage.put(iter, kNNList.get(i));
}
// update the affected kNNs
@@ -215,17 +215,16 @@ public class MaterializeKNNPreprocessor<O, D extends Distance<D>> extends Abstra
private ArrayDBIDs updateKNNsAfterInsertion(DBIDs ids) {
ArrayModifiableDBIDs rkNN_ids = DBIDUtil.newArray();
DBIDs oldids = DBIDUtil.difference(relation.getDBIDs(), ids);
- for (DBIDIter iter = oldids.iter(); iter.valid(); iter.advance()) {
+ for(DBIDIter iter = oldids.iter(); iter.valid(); iter.advance()) {
KNNResult<D> kNNs = storage.get(iter);
D knnDist = kNNs.get(kNNs.size() - 1).getDistance();
// look for new kNNs
KNNHeap<D> heap = null;
- for (DBIDIter iter2 = ids.iter(); iter2.valid(); iter2.advance()) {
+ for(DBIDIter iter2 = ids.iter(); iter2.valid(); iter2.advance()) {
D dist = distanceQuery.distance(iter, iter2);
if(dist.compareTo(knnDist) <= 0) {
if(heap == null) {
- heap = new KNNHeap<D>(k);
- heap.addAll(kNNs);
+ heap = KNNUtil.newHeap(kNNs);
}
heap.add(dist, iter2);
}
@@ -251,8 +250,8 @@ public class MaterializeKNNPreprocessor<O, D extends Distance<D>> extends Abstra
ArrayModifiableDBIDs rkNN_ids = DBIDUtil.newArray();
for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
KNNResult<D> kNNs = storage.get(iditer);
- for(DistanceResultPair<D> kNN : kNNs) {
- if(idsSet.contains(kNN)) {
+ for(DBIDIter it = kNNs.iter(); it.valid(); it.advance()) {
+ if(idsSet.contains(it)) {
rkNN_ids.add(iditer);
break;
}
@@ -260,10 +259,10 @@ public class MaterializeKNNPreprocessor<O, D extends Distance<D>> extends Abstra
}
// update the kNNs of the RkNNs
- List<KNNResult<D>> kNNList = knnQuery.getKNNForBulkDBIDs(rkNN_ids, k);
- for(int i = 0; i < rkNN_ids.size(); i++) {
- DBID id = rkNN_ids.get(i);
- storage.put(id, kNNList.get(i));
+ List<? extends KNNResult<D>> kNNList = knnQuery.getKNNForBulkDBIDs(rkNN_ids, k);
+ DBIDIter iter = rkNN_ids.iter();
+ for(int i = 0; i < rkNN_ids.size(); i++, iter.advance()) {
+ storage.put(iter, kNNList.get(i));
}
return rkNN_ids;
@@ -282,7 +281,7 @@ public class MaterializeKNNPreprocessor<O, D extends Distance<D>> extends Abstra
if(stepprog != null) {
stepprog.beginStep(1, "New deletions ocurred, remove their materialized kNNs.", getLogger());
}
- for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
+ for(DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
storage.delete(iter);
}
@@ -313,7 +312,7 @@ public class MaterializeKNNPreprocessor<O, D extends Distance<D>> extends Abstra
* @see KNNListener
*/
protected void fireKNNsInserted(DBIDs insertions, DBIDs updates) {
- KNNChangeEvent e = new KNNChangeEvent(this, Type.INSERT, insertions, updates);
+ KNNChangeEvent e = new KNNChangeEvent(this, KNNChangeEvent.Type.INSERT, insertions, updates);
Object[] listeners = listenerList.getListenerList();
for(int i = listeners.length - 2; i >= 0; i -= 2) {
if(listeners[i] == KNNListener.class) {
@@ -331,7 +330,7 @@ public class MaterializeKNNPreprocessor<O, D extends Distance<D>> extends Abstra
* @see KNNListener
*/
protected void fireKNNsRemoved(DBIDs removals, DBIDs updates) {
- KNNChangeEvent e = new KNNChangeEvent(this, Type.DELETE, removals, updates);
+ KNNChangeEvent e = new KNNChangeEvent(this, KNNChangeEvent.Type.DELETE, removals, updates);
Object[] listeners = listenerList.getListenerList();
for(int i = listeners.length - 2; i >= 0; i -= 2) {
if(listeners[i] == KNNListener.class) {
@@ -341,27 +340,6 @@ public class MaterializeKNNPreprocessor<O, D extends Distance<D>> extends Abstra
}
/**
- * Extracts and removes the DBIDs in the given collections.
- *
- * @param extraxt a list of lists of DistanceResultPair to extract
- * @param remove the ids to remove
- * @return the DBIDs in the given collection
- */
- protected ArrayDBIDs extractAndRemoveIDs(List<? extends Collection<DistanceResultPair<D>>> extraxt, ArrayDBIDs remove) {
- HashSetModifiableDBIDs ids = DBIDUtil.newHashSet();
- for(Collection<DistanceResultPair<D>> drps : extraxt) {
- for(DistanceResultPair<D> drp : drps) {
- ids.add(drp);
- }
- }
- for (DBIDIter iter = remove.iter(); iter.valid(); iter.advance()) {
- ids.remove(iter);
- }
- // Convert back to array
- return DBIDUtil.newArray(ids);
- }
-
- /**
* Adds a {@link KNNListener} which will be invoked when the kNNs of objects
* are changing.
*
@@ -397,7 +375,7 @@ public class MaterializeKNNPreprocessor<O, D extends Distance<D>> extends Abstra
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
diff --git a/src/de/lmu/ifi/dbs/elki/index/preprocessed/knn/MetricalIndexApproximationMaterializeKNNPreprocessor.java b/src/de/lmu/ifi/dbs/elki/index/preprocessed/knn/MetricalIndexApproximationMaterializeKNNPreprocessor.java
index d3df7855..b82e9c77 100644
--- a/src/de/lmu/ifi/dbs/elki/index/preprocessed/knn/MetricalIndexApproximationMaterializeKNNPreprocessor.java
+++ b/src/de/lmu/ifi/dbs/elki/index/preprocessed/knn/MetricalIndexApproximationMaterializeKNNPreprocessor.java
@@ -33,9 +33,11 @@ import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDPair;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
-import de.lmu.ifi.dbs.elki.database.query.knn.KNNResult;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNHeap;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNUtil;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
import de.lmu.ifi.dbs.elki.index.tree.LeafEntry;
import de.lmu.ifi.dbs.elki.index.tree.Node;
@@ -46,7 +48,6 @@ import de.lmu.ifi.dbs.elki.logging.progress.FiniteProgress;
import de.lmu.ifi.dbs.elki.math.MeanVariance;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
import de.lmu.ifi.dbs.elki.utilities.ClassGenericsUtil;
-import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.KNNHeap;
import de.lmu.ifi.dbs.elki.utilities.documentation.Description;
import de.lmu.ifi.dbs.elki.utilities.documentation.Title;
@@ -69,11 +70,11 @@ import de.lmu.ifi.dbs.elki.utilities.documentation.Title;
*/
@Title("Spatial Approximation Materialize kNN Preprocessor")
@Description("Caterializes the (approximate) k nearest neighbors of objects of a database using a spatial approximation.")
-public class MetricalIndexApproximationMaterializeKNNPreprocessor<O extends NumberVector<? super O, ?>, D extends Distance<D>, N extends Node<E>, E extends MTreeEntry<D>> extends AbstractMaterializeKNNPreprocessor<O, D, KNNResult<D>> {
+public class MetricalIndexApproximationMaterializeKNNPreprocessor<O extends NumberVector<?>, D extends Distance<D>, N extends Node<E>, E extends MTreeEntry<D>> extends AbstractMaterializeKNNPreprocessor<O, D, KNNResult<D>> {
/**
* Logger to use
*/
- private static final Logging logger = Logging.getLogger(MetricalIndexApproximationMaterializeKNNPreprocessor.class);
+ private static final Logging LOG = Logging.getLogger(MetricalIndexApproximationMaterializeKNNPreprocessor.class);
/**
* Constructor
@@ -113,9 +114,9 @@ public class MetricalIndexApproximationMaterializeKNNPreprocessor<O extends Numb
for(int i = 0; i < size; i++) {
ids.add(((LeafEntry) node.getEntry(i)).getDBID());
}
- HashMap<DBIDPair, D> cache = new HashMap<DBIDPair, D>(size * size * 3 / 8);
+ HashMap<DBIDPair, D> cache = new HashMap<DBIDPair, D>((size * size * 3) >> 2);
for(DBIDIter id = ids.iter(); id.valid(); id.advance()) {
- KNNHeap<D> kNN = new KNNHeap<D>(k, distanceQuery.infiniteDistance());
+ KNNHeap<D> kNN = KNNUtil.newHeap(distanceFunction, k);
for(DBIDIter id2 = ids.iter(); id2.valid(); id2.advance()) {
DBIDPair key = DBIDUtil.newPair(id, id2);
D d = cache.remove(key);
@@ -173,7 +174,7 @@ public class MetricalIndexApproximationMaterializeKNNPreprocessor<O extends Numb
}
throw new IllegalStateException("No metrical index found!");
}
-
+
@Override
public String getLongName() {
return "Metrical index knn approximation";
@@ -186,7 +187,7 @@ public class MetricalIndexApproximationMaterializeKNNPreprocessor<O extends Numb
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -203,7 +204,7 @@ public class MetricalIndexApproximationMaterializeKNNPreprocessor<O extends Numb
* @param <N> the type of spatial nodes in the spatial index
* @param <E> the type of spatial entries in the spatial index
*/
- public static class Factory<O extends NumberVector<? super O, ?>, D extends Distance<D>, N extends Node<E>, E extends MTreeEntry<D>> extends AbstractMaterializeKNNPreprocessor.Factory<O, D, KNNResult<D>> {
+ public static class Factory<O extends NumberVector<?>, D extends Distance<D>, N extends Node<E>, E extends MTreeEntry<D>> extends AbstractMaterializeKNNPreprocessor.Factory<O, D, KNNResult<D>> {
/**
* Constructor.
*
@@ -227,7 +228,7 @@ public class MetricalIndexApproximationMaterializeKNNPreprocessor<O extends Numb
*
* @apiviz.exclude
*/
- public static class Parameterizer<O extends NumberVector<? super O, ?>, D extends Distance<D>, N extends Node<E>, E extends MTreeEntry<D>> extends AbstractMaterializeKNNPreprocessor.Factory.Parameterizer<O, D> {
+ public static class Parameterizer<O extends NumberVector<?>, D extends Distance<D>, N extends Node<E>, E extends MTreeEntry<D>> extends AbstractMaterializeKNNPreprocessor.Factory.Parameterizer<O, D> {
@Override
protected Factory<O, D, N, E> makeInstance() {
return new Factory<O, D, N, E>(k, distanceFunction);
diff --git a/src/de/lmu/ifi/dbs/elki/index/preprocessed/knn/PartitionApproximationMaterializeKNNPreprocessor.java b/src/de/lmu/ifi/dbs/elki/index/preprocessed/knn/PartitionApproximationMaterializeKNNPreprocessor.java
index 79c70642..353e227d 100644
--- a/src/de/lmu/ifi/dbs/elki/index/preprocessed/knn/PartitionApproximationMaterializeKNNPreprocessor.java
+++ b/src/de/lmu/ifi/dbs/elki/index/preprocessed/knn/PartitionApproximationMaterializeKNNPreprocessor.java
@@ -27,26 +27,29 @@ import java.util.HashMap;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreFactory;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreUtil;
-import de.lmu.ifi.dbs.elki.database.ids.ArrayDBIDs;
import de.lmu.ifi.dbs.elki.database.ids.ArrayModifiableDBIDs;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDArrayIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDPair;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
-import de.lmu.ifi.dbs.elki.database.query.knn.KNNResult;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNHeap;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNUtil;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.logging.progress.FiniteProgress;
import de.lmu.ifi.dbs.elki.math.MeanVariance;
-import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.KNNHeap;
+import de.lmu.ifi.dbs.elki.utilities.RandomFactory;
import de.lmu.ifi.dbs.elki.utilities.documentation.Description;
import de.lmu.ifi.dbs.elki.utilities.documentation.Title;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterConstraint;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.RandomParameter;
/**
* A preprocessor for annotation of the k nearest neighbors (and their
@@ -62,12 +65,10 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
@Title("Partitioning Approximate kNN Preprocessor")
@Description("Caterializes the (approximate) k nearest neighbors of objects of a database by partitioning and only computing kNN within each partition.")
public class PartitionApproximationMaterializeKNNPreprocessor<O, D extends Distance<D>> extends AbstractMaterializeKNNPreprocessor<O, D, KNNResult<D>> {
- // TODO: randomize/shuffle?
-
/**
* Logger to use
*/
- private static final Logging logger = Logging.getLogger(PartitionApproximationMaterializeKNNPreprocessor.class);
+ private static final Logging LOG = Logging.getLogger(PartitionApproximationMaterializeKNNPreprocessor.class);
/**
* Number of partitions to use.
@@ -75,16 +76,23 @@ public class PartitionApproximationMaterializeKNNPreprocessor<O, D extends Dista
private final int partitions;
/**
+ * Random generator
+ */
+ private final RandomFactory rnd;
+
+ /**
* Constructor
*
* @param relation Relation to process
* @param distanceFunction the distance function to use
* @param k query k
* @param partitions Number of partitions
+ * @param rnd Random number generator
*/
- public PartitionApproximationMaterializeKNNPreprocessor(Relation<O> relation, DistanceFunction<? super O, D> distanceFunction, int k, int partitions) {
+ public PartitionApproximationMaterializeKNNPreprocessor(Relation<O> relation, DistanceFunction<? super O, D> distanceFunction, int k, int partitions, RandomFactory rnd) {
super(relation, distanceFunction, k);
this.partitions = partitions;
+ this.rnd = rnd;
}
@Override
@@ -92,33 +100,42 @@ public class PartitionApproximationMaterializeKNNPreprocessor<O, D extends Dista
DistanceQuery<O, D> distanceQuery = relation.getDatabase().getDistanceQuery(relation, distanceFunction);
storage = DataStoreUtil.makeStorage(relation.getDBIDs(), DataStoreFactory.HINT_STATIC, KNNResult.class);
MeanVariance ksize = new MeanVariance();
- if(logger.isVerbose()) {
- logger.verbose("Approximating nearest neighbor lists to database objects");
+ if (LOG.isVerbose()) {
+ LOG.verbose("Approximating nearest neighbor lists to database objects");
}
- ArrayDBIDs aids = DBIDUtil.ensureArray(relation.getDBIDs());
+ // Produce a random shuffling of the IDs:
+ ArrayModifiableDBIDs aids = DBIDUtil.newArray(relation.getDBIDs());
+ DBIDUtil.randomShuffle(aids, rnd);
int minsize = (int) Math.floor(aids.size() / partitions);
- FiniteProgress progress = logger.isVerbose() ? new FiniteProgress("Processing partitions.", partitions, logger) : null;
- for(int part = 0; part < partitions; part++) {
+ FiniteProgress progress = LOG.isVerbose() ? new FiniteProgress("Processing partitions.", partitions, LOG) : null;
+ for (int part = 0; part < partitions; part++) {
int size = (partitions * minsize + part >= aids.size()) ? minsize : minsize + 1;
// Collect the ids in this node.
ArrayModifiableDBIDs ids = DBIDUtil.newArray(size);
- for(int i = 0; i < size; i++) {
- assert (size * partitions + part < aids.size());
- ids.add(aids.get(i * partitions + part));
+ { // TODO: this is a bit overly complicated. The code dates back to when
+ // we were not shuffling the array beforehand. Right now, we could just
+ // compute the proper partition sizes and split it directly. But
+ // ArrayDBIDs does not have a "sublist" function yet, anyway.
+ DBIDArrayIter iter = aids.iter();
+ // Offset - really cheap on array iterators.
+ iter.seek(part);
+ // Seek in steps of "partitions". Also just a += instead of ++ op!
+ for (; iter.valid(); iter.advance(partitions)) {
+ ids.add(iter);
+ }
}
- HashMap<DBIDPair, D> cache = new HashMap<DBIDPair, D>(size * size * 3 / 8);
+ HashMap<DBIDPair, D> cache = new HashMap<DBIDPair, D>((size * size * 3) >> 3);
for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
- KNNHeap<D> kNN = new KNNHeap<D>(k, distanceQuery.infiniteDistance());
+ KNNHeap<D> kNN = KNNUtil.newHeap(distanceFunction, k);
for (DBIDIter iter2 = ids.iter(); iter2.valid(); iter2.advance()) {
DBIDPair key = DBIDUtil.newPair(iter, iter2);
D d = cache.remove(key);
- if(d != null) {
+ if (d != null) {
// consume the previous result.
kNN.add(d, iter2);
- }
- else {
+ } else {
// compute new and store the previous result.
d = distanceQuery.distance(iter, iter2);
kNN.add(d, iter2);
@@ -130,26 +147,26 @@ public class PartitionApproximationMaterializeKNNPreprocessor<O, D extends Dista
ksize.put(kNN.size());
storage.put(iter, kNN.toKNNList());
}
- if(logger.isDebugging()) {
- if(cache.size() > 0) {
- logger.warning("Cache should be empty after each run, but still has " + cache.size() + " elements.");
+ if (LOG.isDebugging()) {
+ if (cache.size() > 0) {
+ LOG.warning("Cache should be empty after each run, but still has " + cache.size() + " elements.");
}
}
- if(progress != null) {
- progress.incrementProcessed(logger);
+ if (progress != null) {
+ progress.incrementProcessed(LOG);
}
}
- if(progress != null) {
- progress.ensureCompleted(logger);
+ if (progress != null) {
+ progress.ensureCompleted(LOG);
}
- if(logger.isVerbose()) {
- logger.verbose("On average, " + ksize.getMean() + " +- " + ksize.getSampleStddev() + " neighbors returned.");
+ if (LOG.isVerbose()) {
+ LOG.verbose("On average, " + ksize.getMean() + " +- " + ksize.getSampleStddev() + " neighbors returned.");
}
}
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
@Override
@@ -176,18 +193,14 @@ public class PartitionApproximationMaterializeKNNPreprocessor<O, D extends Dista
*/
public static class Factory<O, D extends Distance<D>> extends AbstractMaterializeKNNPreprocessor.Factory<O, D, KNNResult<D>> {
/**
- * Parameter to specify the number of partitions to use for materializing
- * the kNN. Must be an integer greater than 1.
- * <p>
- * Key: {@code -partknn.p}
- * </p>
+ * The number of partitions to use
*/
- public static final OptionID PARTITIONS_ID = OptionID.getOrCreateOptionID("partknn.p", "The number of partitions to use for approximate kNN.");
+ int partitions;
/**
- * The number of partitions to use
+ * Random generator
*/
- int partitions;
+ private final RandomFactory rnd;
/**
* Constructor.
@@ -195,15 +208,17 @@ public class PartitionApproximationMaterializeKNNPreprocessor<O, D extends Dista
* @param k k
* @param distanceFunction distance function
* @param partitions number of partitions
+ * @param rnd
*/
- public Factory(int k, DistanceFunction<? super O, D> distanceFunction, int partitions) {
+ public Factory(int k, DistanceFunction<? super O, D> distanceFunction, int partitions, RandomFactory rnd) {
super(k, distanceFunction);
this.partitions = partitions;
+ this.rnd = rnd;
}
@Override
public PartitionApproximationMaterializeKNNPreprocessor<O, D> instantiate(Relation<O> relation) {
- PartitionApproximationMaterializeKNNPreprocessor<O, D> instance = new PartitionApproximationMaterializeKNNPreprocessor<O, D>(relation, distanceFunction, k, partitions);
+ PartitionApproximationMaterializeKNNPreprocessor<O, D> instance = new PartitionApproximationMaterializeKNNPreprocessor<O, D>(relation, distanceFunction, k, partitions, rnd);
return instance;
}
@@ -215,21 +230,51 @@ public class PartitionApproximationMaterializeKNNPreprocessor<O, D extends Dista
* @apiviz.exclude
*/
public static class Parameterizer<O, D extends Distance<D>> extends AbstractMaterializeKNNPreprocessor.Factory.Parameterizer<O, D> {
+ /**
+ * Parameter to specify the number of partitions to use for materializing
+ * the kNN. Must be an integer greater than 1.
+ * <p>
+ * Key: {@code -partknn.p}
+ * </p>
+ */
+ public static final OptionID PARTITIONS_ID = new OptionID("partknn.p", "The number of partitions to use for approximate kNN.");
+
+ /**
+ * Parameter to specify the random number generator.
+ * <p>
+ * Key: {@code -partknn.seed}
+ * </p>
+ */
+ public static final OptionID SEED_ID = new OptionID("partknn.seed", "The random number generator seed.");
+
+ /**
+ * Number of partitions
+ */
protected int partitions = 0;
+ /**
+ * Random generator
+ */
+ private RandomFactory rnd;
+
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- final IntParameter partitionsP = new IntParameter(PARTITIONS_ID, new GreaterConstraint(1));
- if(config.grab(partitionsP)) {
+ final IntParameter partitionsP = new IntParameter(PARTITIONS_ID);
+ partitionsP.addConstraint(new GreaterConstraint(1));
+ if (config.grab(partitionsP)) {
partitions = partitionsP.getValue();
}
+ RandomParameter rndP = new RandomParameter(SEED_ID);
+ if (config.grab(rndP)) {
+ rnd = rndP.getValue();
+ }
}
@Override
protected Factory<O, D> makeInstance() {
- return new Factory<O, D>(k, distanceFunction, partitions);
+ return new Factory<O, D>(k, distanceFunction, partitions, rnd);
}
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/index/preprocessed/knn/RandomSampleKNNPreprocessor.java b/src/de/lmu/ifi/dbs/elki/index/preprocessed/knn/RandomSampleKNNPreprocessor.java
index fa868109..592a1206 100644
--- a/src/de/lmu/ifi/dbs/elki/index/preprocessed/knn/RandomSampleKNNPreprocessor.java
+++ b/src/de/lmu/ifi/dbs/elki/index/preprocessed/knn/RandomSampleKNNPreprocessor.java
@@ -23,8 +23,6 @@ package de.lmu.ifi.dbs.elki.index.preprocessed.knn;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.util.Random;
-
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreFactory;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreUtil;
import de.lmu.ifi.dbs.elki.database.ids.ArrayDBIDs;
@@ -32,19 +30,21 @@ import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
-import de.lmu.ifi.dbs.elki.database.query.knn.KNNResult;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNHeap;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNUtil;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.logging.progress.FiniteProgress;
-import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.KNNHeap;
+import de.lmu.ifi.dbs.elki.utilities.RandomFactory;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.IntervalConstraint;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.IntervalConstraint.IntervalBoundary;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.LessConstraint;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.DoubleParameter;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.LongParameter;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.RandomParameter;
/**
* Class that computed the kNN only on a random sample.
@@ -58,7 +58,7 @@ public class RandomSampleKNNPreprocessor<O, D extends Distance<D>> extends Abstr
/**
* Logger
*/
- private static final Logging logger = Logging.getLogger(RandomSampleKNNPreprocessor.class);
+ private static final Logging LOG = Logging.getLogger(RandomSampleKNNPreprocessor.class);
/**
* Relative share of objects to get
@@ -66,9 +66,9 @@ public class RandomSampleKNNPreprocessor<O, D extends Distance<D>> extends Abstr
private final double share;
/**
- * Random seed
+ * Random generator
*/
- private final Long seed;
+ private final RandomFactory rnd;
/**
* Constructor.
@@ -77,12 +77,12 @@ public class RandomSampleKNNPreprocessor<O, D extends Distance<D>> extends Abstr
* @param distanceFunction distance function
* @param k k
* @param share Relative share
- * @param seed Random seed (may be null)
+ * @param rnd Random generator
*/
- public RandomSampleKNNPreprocessor(Relation<O> relation, DistanceFunction<? super O, D> distanceFunction, int k, double share, Long seed) {
+ public RandomSampleKNNPreprocessor(Relation<O> relation, DistanceFunction<? super O, D> distanceFunction, int k, double share, RandomFactory rnd) {
super(relation, distanceFunction, k);
this.share = share;
- this.seed = seed;
+ this.rnd = rnd;
}
@Override
@@ -93,33 +93,30 @@ public class RandomSampleKNNPreprocessor<O, D extends Distance<D>> extends Abstr
final ArrayDBIDs ids = DBIDUtil.ensureArray(relation.getDBIDs());
final int samplesize = (int) (ids.size() * share);
- final long iseed = (seed != null) ? seed : (new Random()).nextLong();
- int i = 0;
for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
- KNNHeap<D> kNN = new KNNHeap<D>(k, distanceQuery.infiniteDistance());
+ KNNHeap<D> kNN = KNNUtil.newHeap(distanceFunction, k);
- long rseed = i * 0x7FFFFFFFFFFFFFE7L + iseed;
- DBIDs rsamp = DBIDUtil.randomSample(ids, samplesize, rseed);
+ DBIDs rsamp = DBIDUtil.randomSample(ids, samplesize, rnd);
for (DBIDIter iter2 = rsamp.iter(); iter2.valid(); iter2.advance()) {
D dist = distanceQuery.distance(iter, iter2);
kNN.add(dist, iter2);
}
storage.put(iter, kNN.toKNNList());
- if(progress != null) {
+ if (progress != null) {
progress.incrementProcessed(getLogger());
}
}
- if(progress != null) {
+ if (progress != null) {
progress.ensureCompleted(getLogger());
}
}
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
@Override
@@ -151,9 +148,9 @@ public class RandomSampleKNNPreprocessor<O, D extends Distance<D>> extends Abstr
private final double share;
/**
- * Random seed
+ * Random generator
*/
- private final Long seed;
+ private final RandomFactory rnd;
/**
* Constructor.
@@ -161,17 +158,17 @@ public class RandomSampleKNNPreprocessor<O, D extends Distance<D>> extends Abstr
* @param k K
* @param distanceFunction distance function
* @param share Sample size (relative)
- * @param seed Random seed
+ * @param rnd Random generator
*/
- public Factory(int k, DistanceFunction<? super O, D> distanceFunction, double share, Long seed) {
+ public Factory(int k, DistanceFunction<? super O, D> distanceFunction, double share, RandomFactory rnd) {
super(k, distanceFunction);
this.share = share;
- this.seed = seed;
+ this.rnd = rnd;
}
@Override
public RandomSampleKNNPreprocessor<O, D> instantiate(Relation<O> relation) {
- return new RandomSampleKNNPreprocessor<O, D>(relation, distanceFunction, k, share, seed);
+ return new RandomSampleKNNPreprocessor<O, D>(relation, distanceFunction, k, share, rnd);
}
/**
@@ -193,7 +190,7 @@ public class RandomSampleKNNPreprocessor<O, D extends Distance<D>> extends Abstr
* Key: {@code -randomknn.share}
* </p>
*/
- public static final OptionID SHARE_ID = OptionID.getOrCreateOptionID("randomknn.share", "The relative amount of objects to consider for kNN computations.");
+ public static final OptionID SHARE_ID = new OptionID("randomknn.share", "The relative amount of objects to consider for kNN computations.");
/**
* Random number generator seed.
@@ -202,7 +199,7 @@ public class RandomSampleKNNPreprocessor<O, D extends Distance<D>> extends Abstr
* Key: {@code -randomknn.seed}
* </p>
*/
- public static final OptionID SEED_ID = OptionID.getOrCreateOptionID("randomknn.seed", "The random number seed.");
+ public static final OptionID SEED_ID = new OptionID("randomknn.seed", "The random number seed.");
/**
* Relative share of objects to get
@@ -210,27 +207,29 @@ public class RandomSampleKNNPreprocessor<O, D extends Distance<D>> extends Abstr
private double share = 0.0;
/**
- * Random seed
+ * Random generator
*/
- private Long seed = null;
+ private RandomFactory rnd;
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- DoubleParameter shareP = new DoubleParameter(SHARE_ID, new IntervalConstraint(0.0, IntervalBoundary.OPEN, 1.0, IntervalBoundary.OPEN));
- if(config.grab(shareP)) {
+ DoubleParameter shareP = new DoubleParameter(SHARE_ID);
+ shareP.addConstraint(new GreaterConstraint(0.0));
+ shareP.addConstraint(new LessConstraint(1.0));
+ if (config.grab(shareP)) {
share = shareP.getValue();
}
- LongParameter seedP = new LongParameter(SEED_ID, true);
- if(config.grab(seedP)) {
- seed = seedP.getValue();
+ RandomParameter rndP = new RandomParameter(SEED_ID);
+ if (config.grab(rndP)) {
+ rnd = rndP.getValue();
}
}
@Override
protected RandomSampleKNNPreprocessor.Factory<O, D> makeInstance() {
- return new RandomSampleKNNPreprocessor.Factory<O, D>(k, distanceFunction, share, seed);
+ return new RandomSampleKNNPreprocessor.Factory<O, D>(k, distanceFunction, share, rnd);
}
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/index/preprocessed/knn/SpatialApproximationMaterializeKNNPreprocessor.java b/src/de/lmu/ifi/dbs/elki/index/preprocessed/knn/SpatialApproximationMaterializeKNNPreprocessor.java
index b206194b..c7963e14 100644
--- a/src/de/lmu/ifi/dbs/elki/index/preprocessed/knn/SpatialApproximationMaterializeKNNPreprocessor.java
+++ b/src/de/lmu/ifi/dbs/elki/index/preprocessed/knn/SpatialApproximationMaterializeKNNPreprocessor.java
@@ -35,9 +35,11 @@ import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDPair;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
-import de.lmu.ifi.dbs.elki.database.query.knn.KNNResult;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNHeap;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNUtil;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
import de.lmu.ifi.dbs.elki.index.tree.LeafEntry;
import de.lmu.ifi.dbs.elki.index.tree.spatial.SpatialEntry;
@@ -47,7 +49,6 @@ import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.logging.progress.FiniteProgress;
import de.lmu.ifi.dbs.elki.math.MeanVariance;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
-import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.KNNHeap;
import de.lmu.ifi.dbs.elki.utilities.documentation.Description;
import de.lmu.ifi.dbs.elki.utilities.documentation.Title;
import de.lmu.ifi.dbs.elki.utilities.exceptions.AbortException;
@@ -70,11 +71,11 @@ import de.lmu.ifi.dbs.elki.utilities.exceptions.AbortException;
*/
@Title("Spatial Approximation Materialize kNN Preprocessor")
@Description("Caterializes the (approximate) k nearest neighbors of objects of a database using a spatial approximation.")
-public class SpatialApproximationMaterializeKNNPreprocessor<O extends NumberVector<?, ?>, D extends Distance<D>, N extends SpatialNode<N, E>, E extends SpatialEntry> extends AbstractMaterializeKNNPreprocessor<O, D, KNNResult<D>> {
+public class SpatialApproximationMaterializeKNNPreprocessor<O extends NumberVector<?>, D extends Distance<D>, N extends SpatialNode<N, E>, E extends SpatialEntry> extends AbstractMaterializeKNNPreprocessor<O, D, KNNResult<D>> {
/**
* Logger to use
*/
- private static final Logging logger = Logging.getLogger(SpatialApproximationMaterializeKNNPreprocessor.class);
+ private static final Logging LOG = Logging.getLogger(SpatialApproximationMaterializeKNNPreprocessor.class);
/**
* Constructor
@@ -118,9 +119,9 @@ public class SpatialApproximationMaterializeKNNPreprocessor<O extends NumberVect
for(int i = 0; i < size; i++) {
ids.add(((LeafEntry) node.getEntry(i)).getDBID());
}
- HashMap<DBIDPair, D> cache = new HashMap<DBIDPair, D>(size * size * 3 / 8);
+ HashMap<DBIDPair, D> cache = new HashMap<DBIDPair, D>((size * size * 3) >> 3);
for(DBIDIter id = ids.iter(); id.valid(); id.advance()) {
- KNNHeap<D> kNN = new KNNHeap<D>(k, distanceQuery.infiniteDistance());
+ KNNHeap<D> kNN = KNNUtil.newHeap(distanceFunction, k);
for(DBIDIter id2 = ids.iter(); id2.valid(); id2.advance()) {
DBIDPair key = DBIDUtil.newPair(id, id2);
D d = cache.remove(key);
@@ -160,7 +161,7 @@ public class SpatialApproximationMaterializeKNNPreprocessor<O extends NumberVect
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
@Override
@@ -186,20 +187,20 @@ public class SpatialApproximationMaterializeKNNPreprocessor<O extends NumberVect
* @param <N> the type of spatial nodes in the spatial index
* @param <E> the type of spatial entries in the spatial index
*/
- public static class Factory<D extends Distance<D>, N extends SpatialNode<N, E>, E extends SpatialEntry> extends AbstractMaterializeKNNPreprocessor.Factory<NumberVector<?, ?>, D, KNNResult<D>> {
+ public static class Factory<D extends Distance<D>, N extends SpatialNode<N, E>, E extends SpatialEntry> extends AbstractMaterializeKNNPreprocessor.Factory<NumberVector<?>, D, KNNResult<D>> {
/**
* Constructor.
*
* @param k k
* @param distanceFunction distance function
*/
- public Factory(int k, DistanceFunction<? super NumberVector<?, ?>, D> distanceFunction) {
+ public Factory(int k, DistanceFunction<? super NumberVector<?>, D> distanceFunction) {
super(k, distanceFunction);
}
@Override
- public SpatialApproximationMaterializeKNNPreprocessor<NumberVector<?, ?>, D, N, E> instantiate(Relation<NumberVector<?, ?>> relation) {
- SpatialApproximationMaterializeKNNPreprocessor<NumberVector<?, ?>, D, N, E> instance = new SpatialApproximationMaterializeKNNPreprocessor<NumberVector<?, ?>, D, N, E>(relation, distanceFunction, k);
+ public SpatialApproximationMaterializeKNNPreprocessor<NumberVector<?>, D, N, E> instantiate(Relation<NumberVector<?>> relation) {
+ SpatialApproximationMaterializeKNNPreprocessor<NumberVector<?>, D, N, E> instance = new SpatialApproximationMaterializeKNNPreprocessor<NumberVector<?>, D, N, E>(relation, distanceFunction, k);
return instance;
}
@@ -210,7 +211,7 @@ public class SpatialApproximationMaterializeKNNPreprocessor<O extends NumberVect
*
* @apiviz.exclude
*/
- public static class Parameterizer<D extends Distance<D>, N extends SpatialNode<N, E>, E extends SpatialEntry> extends AbstractMaterializeKNNPreprocessor.Factory.Parameterizer<NumberVector<?, ?>, D> {
+ public static class Parameterizer<D extends Distance<D>, N extends SpatialNode<N, E>, E extends SpatialEntry> extends AbstractMaterializeKNNPreprocessor.Factory.Parameterizer<NumberVector<?>, D> {
@Override
protected Factory<D, N, E> makeInstance() {
return new Factory<D, N, E>(k, distanceFunction);
diff --git a/src/de/lmu/ifi/dbs/elki/index/preprocessed/localpca/AbstractFilteredPCAIndex.java b/src/de/lmu/ifi/dbs/elki/index/preprocessed/localpca/AbstractFilteredPCAIndex.java
index 99e956c8..371dbca6 100644
--- a/src/de/lmu/ifi/dbs/elki/index/preprocessed/localpca/AbstractFilteredPCAIndex.java
+++ b/src/de/lmu/ifi/dbs/elki/index/preprocessed/localpca/AbstractFilteredPCAIndex.java
@@ -23,19 +23,16 @@ package de.lmu.ifi.dbs.elki.index.preprocessed.localpca;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.util.Collection;
-
import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.data.type.TypeInformation;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreFactory;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreUtil;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
import de.lmu.ifi.dbs.elki.distance.distancefunction.EuclideanDistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
import de.lmu.ifi.dbs.elki.index.preprocessed.AbstractPreprocessorIndex;
import de.lmu.ifi.dbs.elki.logging.progress.FiniteProgress;
@@ -64,11 +61,11 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ObjectParameter;
// TODO: loosen DoubleDistance restriction.
@Title("Local PCA Preprocessor")
@Description("Materializes the local PCA and the locally weighted matrix of objects of a database.")
-public abstract class AbstractFilteredPCAIndex<NV extends NumberVector<? extends NV, ?>> extends AbstractPreprocessorIndex<NV, PCAFilteredResult> implements FilteredLocalPCAIndex<NV> {
+public abstract class AbstractFilteredPCAIndex<NV extends NumberVector<?>> extends AbstractPreprocessorIndex<NV, PCAFilteredResult> implements FilteredLocalPCAIndex<NV> {
/**
* PCA utility object.
*/
- final protected PCAFilteredRunner<NV> pca;
+ protected final PCAFilteredRunner<NV> pca;
/**
* Constructor.
@@ -102,12 +99,11 @@ public abstract class AbstractFilteredPCAIndex<NV extends NumberVector<? extends
// TODO: use a bulk operation?
for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
- Collection<DistanceResultPair<DoubleDistance>> objects = objectsForPCA(id);
+ DistanceDBIDResult<DoubleDistance> objects = objectsForPCA(iditer);
PCAFilteredResult pcares = pca.processQueryResult(objects, relation);
- storage.put(id, pcares);
+ storage.put(iditer, pcares);
if(progress != null) {
progress.incrementProcessed(getLogger());
@@ -140,23 +136,23 @@ public abstract class AbstractFilteredPCAIndex<NV extends NumberVector<? extends
* @return the list of the objects (i.e. the ids and the distances to the
* query object) to be considered within the PCA
*/
- protected abstract Collection<DistanceResultPair<DoubleDistance>> objectsForPCA(DBID id);
+ protected abstract DistanceDBIDResult<DoubleDistance> objectsForPCA(DBIDRef id);
/**
- * Factory class
+ * Factory class.
*
* @author Erich Schubert
*
* @apiviz.stereotype factory
* @apiviz.uses AbstractFilteredPCAIndex oneway - - «create»
*/
- public static abstract class Factory<NV extends NumberVector<NV, ?>, I extends AbstractFilteredPCAIndex<NV>> implements FilteredLocalPCAIndex.Factory<NV, I>, Parameterizable {
+ public abstract static class Factory<NV extends NumberVector<?>, I extends AbstractFilteredPCAIndex<NV>> implements FilteredLocalPCAIndex.Factory<NV, I>, Parameterizable {
/**
* Parameter to specify the distance function used for running PCA.
*
* Key: {@code -localpca.distancefunction}
*/
- public static final OptionID PCA_DISTANCE_ID = OptionID.getOrCreateOptionID("localpca.distancefunction", "The distance function used to select objects for running PCA.");
+ public static final OptionID PCA_DISTANCE_ID = new OptionID("localpca.distancefunction", "The distance function used to select objects for running PCA.");
/**
* Holds the instance of the distance function specified by
@@ -196,7 +192,7 @@ public abstract class AbstractFilteredPCAIndex<NV extends NumberVector<? extends
*
* @apiviz.exclude
*/
- public static abstract class Parameterizer<NV extends NumberVector<NV, ?>, I extends AbstractFilteredPCAIndex<NV>> extends AbstractParameterizer {
+ public abstract static class Parameterizer<NV extends NumberVector<?>, I extends AbstractFilteredPCAIndex<NV>> extends AbstractParameterizer {
/**
* Holds the instance of the distance function specified by
* {@link #PCA_DISTANCE_ID}.
diff --git a/src/de/lmu/ifi/dbs/elki/index/preprocessed/localpca/FilteredLocalPCAIndex.java b/src/de/lmu/ifi/dbs/elki/index/preprocessed/localpca/FilteredLocalPCAIndex.java
index bc5d08c1..d0780751 100644
--- a/src/de/lmu/ifi/dbs/elki/index/preprocessed/localpca/FilteredLocalPCAIndex.java
+++ b/src/de/lmu/ifi/dbs/elki/index/preprocessed/localpca/FilteredLocalPCAIndex.java
@@ -36,7 +36,7 @@ import de.lmu.ifi.dbs.elki.math.linearalgebra.pca.PCAFilteredResult;
*
* @param <NV> Vector type
*/
-public interface FilteredLocalPCAIndex<NV extends NumberVector<?, ?>> extends LocalProjectionIndex<NV, PCAFilteredResult> {
+public interface FilteredLocalPCAIndex<NV extends NumberVector<?>> extends LocalProjectionIndex<NV, PCAFilteredResult> {
/**
* Get the precomputed local PCA for a particular object ID.
*
@@ -57,7 +57,7 @@ public interface FilteredLocalPCAIndex<NV extends NumberVector<?, ?>> extends Lo
* @param <NV> Vector type
* @param <I> Index type produced
*/
- public static interface Factory<NV extends NumberVector<?, ?>, I extends FilteredLocalPCAIndex<NV>> extends LocalProjectionIndex.Factory<NV, I> {
+ public static interface Factory<NV extends NumberVector<?>, I extends FilteredLocalPCAIndex<NV>> extends LocalProjectionIndex.Factory<NV, I> {
/**
* Instantiate the index for a given database.
*
diff --git a/src/de/lmu/ifi/dbs/elki/index/preprocessed/localpca/KNNQueryFilteredPCAIndex.java b/src/de/lmu/ifi/dbs/elki/index/preprocessed/localpca/KNNQueryFilteredPCAIndex.java
index 8d766391..00ce11b8 100644
--- a/src/de/lmu/ifi/dbs/elki/index/preprocessed/localpca/KNNQueryFilteredPCAIndex.java
+++ b/src/de/lmu/ifi/dbs/elki/index/preprocessed/localpca/KNNQueryFilteredPCAIndex.java
@@ -25,11 +25,11 @@ package de.lmu.ifi.dbs.elki.index.preprocessed.localpca;
import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.database.QueryUtil;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.query.knn.KNNQuery;
-import de.lmu.ifi.dbs.elki.database.query.knn.KNNResult;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.math.linearalgebra.pca.PCAFilteredRunner;
@@ -55,21 +55,21 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
// TODO: loosen DoubleDistance restriction.
@Title("Knn Query Based Local PCA Preprocessor")
@Description("Materializes the local PCA and the locally weighted matrix of objects of a database. The PCA is based on k nearest neighbor queries.")
-public class KNNQueryFilteredPCAIndex<NV extends NumberVector<? extends NV, ?>> extends AbstractFilteredPCAIndex<NV> {
+public class KNNQueryFilteredPCAIndex<NV extends NumberVector<?>> extends AbstractFilteredPCAIndex<NV> {
/**
* Logger.
*/
- private static final Logging logger = Logging.getLogger(KNNQueryFilteredPCAIndex.class);
+ private static final Logging LOG = Logging.getLogger(KNNQueryFilteredPCAIndex.class);
/**
- * The kNN query instance we use
+ * The kNN query instance we use.
*/
- final private KNNQuery<NV, DoubleDistance> knnQuery;
+ private final KNNQuery<NV, DoubleDistance> knnQuery;
/**
- * Query k
+ * Query k.
*/
- final private int k;
+ private final int k;
/**
* Constructor.
@@ -86,7 +86,7 @@ public class KNNQueryFilteredPCAIndex<NV extends NumberVector<? extends NV, ?>>
}
@Override
- protected KNNResult<DoubleDistance> objectsForPCA(DBID id) {
+ protected KNNResult<DoubleDistance> objectsForPCA(DBIDRef id) {
return knnQuery.getKNNForDBID(id, k);
}
@@ -102,11 +102,11 @@ public class KNNQueryFilteredPCAIndex<NV extends NumberVector<? extends NV, ?>>
@Override
public Logging getLogger() {
- return logger;
+ return LOG;
}
/**
- * Factory class
+ * Factory class.
*
* @author Erich Schubert
*
@@ -114,7 +114,7 @@ public class KNNQueryFilteredPCAIndex<NV extends NumberVector<? extends NV, ?>>
* @apiviz.landmark
* @apiviz.uses KNNQueryFilteredPCAIndex oneway - - «create»
*/
- public static class Factory<V extends NumberVector<V, ?>> extends AbstractFilteredPCAIndex.Factory<V, KNNQueryFilteredPCAIndex<V>> {
+ public static class Factory<V extends NumberVector<?>> extends AbstractFilteredPCAIndex.Factory<V, KNNQueryFilteredPCAIndex<V>> {
/**
* Optional parameter to specify the number of nearest neighbors considered
* in the PCA, must be an integer greater than 0. If this parameter is not
@@ -127,7 +127,7 @@ public class KNNQueryFilteredPCAIndex<NV extends NumberVector<? extends NV, ?>>
* Default value: three times of the dimensionality of the database objects
* </p>
*/
- public static final OptionID K_ID = OptionID.getOrCreateOptionID("localpca.k", "The number of nearest neighbors considered in the PCA. " + "If this parameter is not set, k ist set to three " + "times of the dimensionality of the database objects.");
+ public static final OptionID K_ID = new OptionID("localpca.k", "The number of nearest neighbors considered in the PCA. " + "If this parameter is not set, k ist set to three " + "times of the dimensionality of the database objects.");
/**
* Holds the value of {@link #K_ID}.
@@ -160,13 +160,15 @@ public class KNNQueryFilteredPCAIndex<NV extends NumberVector<? extends NV, ?>>
*
* @apiviz.exclude
*/
- public static class Parameterizer<NV extends NumberVector<NV, ?>> extends AbstractFilteredPCAIndex.Factory.Parameterizer<NV, KNNQueryFilteredPCAIndex<NV>> {
+ public static class Parameterizer<NV extends NumberVector<?>> extends AbstractFilteredPCAIndex.Factory.Parameterizer<NV, KNNQueryFilteredPCAIndex<NV>> {
protected int k = 0;
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- final IntParameter kP = new IntParameter(K_ID, new GreaterConstraint(0), true);
+ final IntParameter kP = new IntParameter(K_ID);
+ kP.addConstraint(new GreaterConstraint(0));
+ kP.setOptional(true);
if(config.grab(kP)) {
k = kP.getValue();
}
diff --git a/src/de/lmu/ifi/dbs/elki/index/preprocessed/localpca/RangeQueryFilteredPCAIndex.java b/src/de/lmu/ifi/dbs/elki/index/preprocessed/localpca/RangeQueryFilteredPCAIndex.java
index 99937dbf..bbc00c64 100644
--- a/src/de/lmu/ifi/dbs/elki/index/preprocessed/localpca/RangeQueryFilteredPCAIndex.java
+++ b/src/de/lmu/ifi/dbs/elki/index/preprocessed/localpca/RangeQueryFilteredPCAIndex.java
@@ -25,11 +25,11 @@ package de.lmu.ifi.dbs.elki.index.preprocessed.localpca;
import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.database.QueryUtil;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
-import de.lmu.ifi.dbs.elki.database.query.DistanceDBIDResult;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.query.range.RangeQuery;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.math.linearalgebra.pca.PCAFilteredRunner;
@@ -52,22 +52,22 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.DistanceParameter
*/
@Title("Range Query Based Local PCA Preprocessor")
@Description("Materializes the local PCA and the locally weighted matrix of objects of a database. The PCA is based on epsilon range queries.")
-public class RangeQueryFilteredPCAIndex<NV extends NumberVector<? extends NV, ?>> extends AbstractFilteredPCAIndex<NV> {
+public class RangeQueryFilteredPCAIndex<NV extends NumberVector<?>> extends AbstractFilteredPCAIndex<NV> {
// TODO: lose DoubleDistance restriction.
/**
* Logger.
*/
- private static final Logging logger = Logging.getLogger(RangeQueryFilteredPCAIndex.class);
+ private static final Logging LOG = Logging.getLogger(RangeQueryFilteredPCAIndex.class);
/**
- * The kNN query instance we use
+ * The kNN query instance we use.
*/
- final private RangeQuery<NV, DoubleDistance> rangeQuery;
+ private final RangeQuery<NV, DoubleDistance> rangeQuery;
/**
- * Query epsilon
+ * Query epsilon.
*/
- final private DoubleDistance epsilon;
+ private final DoubleDistance epsilon;
/**
* Constructor.
@@ -84,7 +84,7 @@ public class RangeQueryFilteredPCAIndex<NV extends NumberVector<? extends NV, ?>
}
@Override
- protected DistanceDBIDResult<DoubleDistance> objectsForPCA(DBID id) {
+ protected DistanceDBIDResult<DoubleDistance> objectsForPCA(DBIDRef id) {
return rangeQuery.getRangeForDBID(id, epsilon);
}
@@ -100,18 +100,18 @@ public class RangeQueryFilteredPCAIndex<NV extends NumberVector<? extends NV, ?>
@Override
public Logging getLogger() {
- return logger;
+ return LOG;
}
/**
- * Factory class
+ * Factory class.
*
* @author Erich Schubert
*
* @apiviz.stereotype factory
* @apiviz.uses RangeQueryFilteredPCAIndex oneway - - «create»
*/
- public static class Factory<V extends NumberVector<V, ?>> extends AbstractFilteredPCAIndex.Factory<V, RangeQueryFilteredPCAIndex<V>> {
+ public static class Factory<V extends NumberVector<?>> extends AbstractFilteredPCAIndex.Factory<V, RangeQueryFilteredPCAIndex<V>> {
/**
* Parameter to specify the maximum radius of the neighborhood to be
* considered in the PCA, must be suitable to the distance function
@@ -119,7 +119,7 @@ public class RangeQueryFilteredPCAIndex<NV extends NumberVector<? extends NV, ?>
*
* Key: {@code -localpca.epsilon}
*/
- public static final OptionID EPSILON_ID = OptionID.getOrCreateOptionID("localpca.epsilon", "The maximum radius of the neighborhood to be considered in the PCA.");
+ public static final OptionID EPSILON_ID = new OptionID("localpca.epsilon", "The maximum radius of the neighborhood to be considered in the PCA.");
/**
* Holds the value of {@link #EPSILON_ID}.
@@ -152,7 +152,7 @@ public class RangeQueryFilteredPCAIndex<NV extends NumberVector<? extends NV, ?>
*
* @apiviz.exclude
*/
- public static class Parameterizer<NV extends NumberVector<NV, ?>> extends AbstractFilteredPCAIndex.Factory.Parameterizer<NV, RangeQueryFilteredPCAIndex<NV>> {
+ public static class Parameterizer<NV extends NumberVector<?>> extends AbstractFilteredPCAIndex.Factory.Parameterizer<NV, RangeQueryFilteredPCAIndex<NV>> {
protected DoubleDistance epsilon = null;
@Override
diff --git a/src/de/lmu/ifi/dbs/elki/index/preprocessed/preference/AbstractPreferenceVectorIndex.java b/src/de/lmu/ifi/dbs/elki/index/preprocessed/preference/AbstractPreferenceVectorIndex.java
index 387985ab..56b09dba 100644
--- a/src/de/lmu/ifi/dbs/elki/index/preprocessed/preference/AbstractPreferenceVectorIndex.java
+++ b/src/de/lmu/ifi/dbs/elki/index/preprocessed/preference/AbstractPreferenceVectorIndex.java
@@ -40,7 +40,7 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable;
*
* @param <NV> Number vector
*/
-public abstract class AbstractPreferenceVectorIndex<NV extends NumberVector<?, ?>> extends AbstractPreprocessorIndex<NV, BitSet> implements PreferenceVectorIndex<NV> {
+public abstract class AbstractPreferenceVectorIndex<NV extends NumberVector<?>> extends AbstractPreprocessorIndex<NV, BitSet> implements PreferenceVectorIndex<NV> {
/**
* Constructor.
*
@@ -53,7 +53,7 @@ public abstract class AbstractPreferenceVectorIndex<NV extends NumberVector<?, ?
/**
* Preprocessing step.
*/
- abstract protected void preprocess();
+ protected abstract void preprocess();
@Override
public BitSet getPreferenceVector(DBIDRef objid) {
@@ -64,14 +64,14 @@ public abstract class AbstractPreferenceVectorIndex<NV extends NumberVector<?, ?
}
/**
- * Factory class
+ * Factory class.
*
* @author Erich Schubert
*
* @apiviz.stereotype factory
* @apiviz.uses AbstractPreferenceVectorIndex oneway - - «create»
*/
- public static abstract class Factory<V extends NumberVector<?, ?>, I extends PreferenceVectorIndex<V>> implements PreferenceVectorIndex.Factory<V, I>, Parameterizable {
+ public abstract static class Factory<V extends NumberVector<?>, I extends PreferenceVectorIndex<V>> implements PreferenceVectorIndex.Factory<V, I>, Parameterizable {
@Override
public abstract I instantiate(Relation<V> relation);
diff --git a/src/de/lmu/ifi/dbs/elki/index/preprocessed/preference/DiSHPreferenceVectorIndex.java b/src/de/lmu/ifi/dbs/elki/index/preprocessed/preference/DiSHPreferenceVectorIndex.java
index 416a5ffb..2b02e7d6 100644
--- a/src/de/lmu/ifi/dbs/elki/index/preprocessed/preference/DiSHPreferenceVectorIndex.java
+++ b/src/de/lmu/ifi/dbs/elki/index/preprocessed/preference/DiSHPreferenceVectorIndex.java
@@ -40,31 +40,28 @@ import de.lmu.ifi.dbs.elki.database.HashmapDatabase;
import de.lmu.ifi.dbs.elki.database.UpdatableDatabase;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreFactory;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreUtil;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.ids.ModifiableDBIDs;
-import de.lmu.ifi.dbs.elki.database.query.DistanceDBIDResult;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
import de.lmu.ifi.dbs.elki.database.query.distance.PrimitiveDistanceQuery;
import de.lmu.ifi.dbs.elki.database.query.range.RangeQuery;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.datasource.bundle.SingleObjectBundle;
import de.lmu.ifi.dbs.elki.distance.distancefunction.subspace.DimensionSelectingDistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.logging.progress.FiniteProgress;
import de.lmu.ifi.dbs.elki.result.AprioriResult;
import de.lmu.ifi.dbs.elki.utilities.ClassGenericsUtil;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.utilities.FormatUtil;
import de.lmu.ifi.dbs.elki.utilities.documentation.Description;
import de.lmu.ifi.dbs.elki.utilities.exceptions.ExceptionMessages;
import de.lmu.ifi.dbs.elki.utilities.exceptions.UnableToComplyException;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.ParameterException;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.WrongParameterValueException;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterConstraint;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
@@ -77,13 +74,15 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
* database.
*
* @author Elke Achtert
+ *
+ * @param <V> Vector type
*/
@Description("Computes the preference vector of objects of a certain database according to the DiSH algorithm.")
-public class DiSHPreferenceVectorIndex<V extends NumberVector<?, ?>> extends AbstractPreferenceVectorIndex<V> implements PreferenceVectorIndex<V> {
+public class DiSHPreferenceVectorIndex<V extends NumberVector<?>> extends AbstractPreferenceVectorIndex<V> implements PreferenceVectorIndex<V> {
/**
- * Logger to use
+ * Logger to use.
*/
- protected static final Logging logger = Logging.getLogger(DiSHPreferenceVectorIndex.class);
+ private static final Logging LOG = Logging.getLogger(DiSHPreferenceVectorIndex.class);
/**
* Available strategies for determination of the preference vector.
@@ -92,17 +91,17 @@ public class DiSHPreferenceVectorIndex<V extends NumberVector<?, ?>> extends Abs
*/
public enum Strategy {
/**
- * Apriori strategy
+ * Apriori strategy.
*/
APRIORI,
/**
- * Max intersection strategy
+ * Max intersection strategy.
*/
MAX_INTERSECTION
}
/**
- * The epsilon value for each dimension;
+ * The epsilon value for each dimension.
*/
protected DoubleDistance[] epsilon;
@@ -139,84 +138,76 @@ public class DiSHPreferenceVectorIndex<V extends NumberVector<?, ?>> extends Abs
storage = DataStoreUtil.makeStorage(relation.getDBIDs(), DataStoreFactory.HINT_HOT | DataStoreFactory.HINT_TEMP, BitSet.class);
- if(logger.isDebugging()) {
- StringBuffer msg = new StringBuffer();
+ if(LOG.isDebugging()) {
+ StringBuilder msg = new StringBuilder();
msg.append("\n eps ").append(Arrays.asList(epsilon));
msg.append("\n minpts ").append(minpts);
msg.append("\n strategy ").append(strategy);
- logger.debugFine(msg.toString());
+ LOG.debugFine(msg.toString());
}
- try {
- long start = System.currentTimeMillis();
- FiniteProgress progress = logger.isVerbose() ? new FiniteProgress("Preprocessing preference vector", relation.size(), logger) : null;
-
- // only one epsilon value specified
- int dim = DatabaseUtil.dimensionality(relation);
- if(epsilon.length == 1 && dim != 1) {
- DoubleDistance eps = epsilon[0];
- epsilon = new DoubleDistance[dim];
- Arrays.fill(epsilon, eps);
- }
-
- // epsilons as string
- RangeQuery<V, DoubleDistance>[] rangeQueries = initRangeQueries(relation, dim);
+ long start = System.currentTimeMillis();
+ FiniteProgress progress = LOG.isVerbose() ? new FiniteProgress("Preprocessing preference vector", relation.size(), LOG) : null;
- for(DBIDIter it = relation.iterDBIDs(); it.valid(); it.advance()) {
- StringBuffer msg = new StringBuffer();
- final DBID id = it.getDBID();
+ // only one epsilon value specified
+ int dim = RelationUtil.dimensionality(relation);
+ if(epsilon.length == 1 && dim != 1) {
+ DoubleDistance eps = epsilon[0];
+ epsilon = new DoubleDistance[dim];
+ Arrays.fill(epsilon, eps);
+ }
- if(logger.isDebugging()) {
- msg.append("\nid = ").append(id);
- // msg.append(" ").append(database.get(id));
- //msg.append(" ").append(database.getObjectLabelQuery().get(id));
- }
+ // epsilons as string
+ RangeQuery<V, DoubleDistance>[] rangeQueries = initRangeQueries(relation, dim);
- // determine neighbors in each dimension
- ModifiableDBIDs[] allNeighbors = ClassGenericsUtil.newArrayOfNull(dim, ModifiableDBIDs.class);
- for(int d = 0; d < dim; d++) {
- DistanceDBIDResult<DoubleDistance> qrList = rangeQueries[d].getRangeForDBID(id, epsilon[d]);
- allNeighbors[d] = DBIDUtil.newHashSet(qrList.size());
- for(DistanceResultPair<DoubleDistance> qr : qrList) {
- allNeighbors[d].add(qr.getDBID());
- }
- }
+ for(DBIDIter it = relation.iterDBIDs(); it.valid(); it.advance()) {
+ StringBuilder msg = new StringBuilder();
- if(logger.isDebugging()) {
- for(int d = 0; d < dim; d++) {
- msg.append("\n neighbors [").append(d).append("]");
- msg.append(" (").append(allNeighbors[d].size()).append(") = ");
- msg.append(allNeighbors[d]);
- }
- }
+ if(LOG.isDebugging()) {
+ msg.append("\nid = ").append(DBIDUtil.toString(it));
+ // msg.append(" ").append(database.get(id));
+ // msg.append(" ").append(database.getObjectLabelQuery().get(id));
+ }
- BitSet preferenceVector = determinePreferenceVector(relation, allNeighbors, msg);
- storage.put(id, preferenceVector);
+ // determine neighbors in each dimension
+ ModifiableDBIDs[] allNeighbors = new ModifiableDBIDs[dim];
+ for(int d = 0; d < dim; d++) {
+ DistanceDBIDResult<DoubleDistance> qrList = rangeQueries[d].getRangeForDBID(it, epsilon[d]);
+ allNeighbors[d] = DBIDUtil.newHashSet(qrList);
+ }
- if(logger.isDebugging()) {
- logger.debugFine(msg.toString());
+ if(LOG.isDebugging()) {
+ for(int d = 0; d < dim; d++) {
+ msg.append("\n neighbors [").append(d).append(']');
+ msg.append(" (").append(allNeighbors[d].size()).append(") = ");
+ msg.append(allNeighbors[d]);
}
+ }
- if(progress != null) {
- progress.incrementProcessed(logger);
- }
+ try {
+ storage.put(it, determinePreferenceVector(relation, allNeighbors, msg));
}
- if(progress != null) {
- progress.ensureCompleted(logger);
+ catch(UnableToComplyException e) {
+ throw new IllegalStateException(e);
}
- long end = System.currentTimeMillis();
- // TODO: re-add timing code!
- if(logger.isVerbose()) {
- long elapsedTime = end - start;
- logger.verbose(this.getClass().getName() + " runtime: " + elapsedTime + " milliseconds.");
+ if(LOG.isDebugging()) {
+ LOG.debugFine(msg.toString());
+ }
+
+ if(progress != null) {
+ progress.incrementProcessed(LOG);
}
}
- catch(ParameterException e) {
- throw new IllegalStateException(e);
+ if(progress != null) {
+ progress.ensureCompleted(LOG);
}
- catch(UnableToComplyException e) {
- throw new IllegalStateException(e);
+
+ long end = System.currentTimeMillis();
+ // TODO: re-add timing code!
+ if(LOG.isVerbose()) {
+ long elapsedTime = end - start;
+ LOG.verbose(this.getClass().getName() + " runtime: " + elapsedTime + " milliseconds.");
}
}
@@ -227,11 +218,10 @@ public class DiSHPreferenceVectorIndex<V extends NumberVector<?, ?>> extends Abs
* @param neighborIDs the list of ids of the neighbors in each dimension
* @param msg a string buffer for debug messages
* @return the preference vector
- * @throws de.lmu.ifi.dbs.elki.utilities.optionhandling.ParameterException
*
* @throws de.lmu.ifi.dbs.elki.utilities.exceptions.UnableToComplyException
*/
- private BitSet determinePreferenceVector(Relation<V> relation, ModifiableDBIDs[] neighborIDs, StringBuffer msg) throws ParameterException, UnableToComplyException {
+ private BitSet determinePreferenceVector(Relation<V> relation, ModifiableDBIDs[] neighborIDs, StringBuilder msg) throws UnableToComplyException {
if(strategy.equals(Strategy.APRIORI)) {
return determinePreferenceVectorByApriori(relation, neighborIDs, msg);
}
@@ -250,23 +240,21 @@ public class DiSHPreferenceVectorIndex<V extends NumberVector<?, ?>> extends Abs
* @param neighborIDs the list of ids of the neighbors in each dimension
* @param msg a string buffer for debug messages
* @return the preference vector
- * @throws de.lmu.ifi.dbs.elki.utilities.optionhandling.ParameterException
*
* @throws de.lmu.ifi.dbs.elki.utilities.exceptions.UnableToComplyException
*
*/
- private BitSet determinePreferenceVectorByApriori(Relation<V> relation, ModifiableDBIDs[] neighborIDs, StringBuffer msg) throws ParameterException, UnableToComplyException {
+ private BitSet determinePreferenceVectorByApriori(Relation<V> relation, ModifiableDBIDs[] neighborIDs, StringBuilder msg) throws UnableToComplyException {
int dimensionality = neighborIDs.length;
// database for apriori
UpdatableDatabase apriori_db = new HashmapDatabase();
- SimpleTypeInformation<?> bitmeta = VectorFieldTypeInformation.get(BitVector.class, dimensionality);
+ SimpleTypeInformation<?> bitmeta = new VectorFieldTypeInformation<BitVector>(BitVector.class, dimensionality);
for(DBIDIter it = relation.iterDBIDs(); it.valid(); it.advance()) {
- DBID id = it.getDBID();
Bit[] bits = new Bit[dimensionality];
boolean allFalse = true;
for(int d = 0; d < dimensionality; d++) {
- if(neighborIDs[d].contains(id)) {
+ if(neighborIDs[d].contains(it)) {
bits[d] = new Bit(true);
allFalse = false;
}
@@ -286,9 +274,9 @@ public class DiSHPreferenceVectorIndex<V extends NumberVector<?, ?>> extends Abs
// result of apriori
List<BitSet> frequentItemsets = aprioriResult.getSolution();
Map<BitSet, Integer> supports = aprioriResult.getSupports();
- if(logger.isDebugging()) {
- msg.append("\n Frequent itemsets: " + frequentItemsets);
- msg.append("\n All supports: " + supports);
+ if(LOG.isDebugging()) {
+ msg.append("\n Frequent itemsets: ").append(frequentItemsets);
+ msg.append("\n All supports: ").append(supports);
}
int maxSupport = 0;
int maxCardinality = 0;
@@ -302,11 +290,11 @@ public class DiSHPreferenceVectorIndex<V extends NumberVector<?, ?>> extends Abs
}
}
- if(logger.isDebugging()) {
+ if(LOG.isDebugging()) {
msg.append("\n preference ");
msg.append(FormatUtil.format(dimensionality, preferenceVector));
- msg.append("\n");
- logger.debugFine(msg.toString());
+ msg.append('\n');
+ LOG.debugFine(msg.toString());
}
return preferenceVector;
@@ -319,7 +307,7 @@ public class DiSHPreferenceVectorIndex<V extends NumberVector<?, ?>> extends Abs
* @param msg a string buffer for debug messages
* @return the preference vector
*/
- private BitSet determinePreferenceVectorByMaxIntersection(ModifiableDBIDs[] neighborIDs, StringBuffer msg) {
+ private BitSet determinePreferenceVectorByMaxIntersection(ModifiableDBIDs[] neighborIDs, StringBuilder msg) {
int dimensionality = neighborIDs.length;
BitSet preferenceVector = new BitSet(dimensionality);
@@ -330,8 +318,8 @@ public class DiSHPreferenceVectorIndex<V extends NumberVector<?, ?>> extends Abs
candidates.put(i, s_i);
}
}
- if(logger.isDebugging()) {
- msg.append("\n candidates " + candidates.keySet());
+ if(LOG.isDebugging()) {
+ msg.append("\n candidates ").append(candidates.keySet());
}
if(!candidates.isEmpty()) {
@@ -355,11 +343,11 @@ public class DiSHPreferenceVectorIndex<V extends NumberVector<?, ?>> extends Abs
}
}
- if(logger.isDebugging()) {
+ if(LOG.isDebugging()) {
msg.append("\n preference ");
msg.append(FormatUtil.format(dimensionality, preferenceVector));
- msg.append("\n");
- logger.debug(msg.toString());
+ msg.append('\n');
+ LOG.debug(msg.toString());
}
return preferenceVector;
@@ -416,20 +404,19 @@ public class DiSHPreferenceVectorIndex<V extends NumberVector<?, ?>> extends Abs
* @param dimensionality the dimensionality of the objects
* @return the dimension selecting distancefunctions to determine the
* preference vectors
- * @throws ParameterException
*/
- private RangeQuery<V, DoubleDistance>[] initRangeQueries(Relation<V> relation, int dimensionality) throws ParameterException {
+ private RangeQuery<V, DoubleDistance>[] initRangeQueries(Relation<V> relation, int dimensionality) {
Class<RangeQuery<V, DoubleDistance>> rqcls = ClassGenericsUtil.uglyCastIntoSubclass(RangeQuery.class);
RangeQuery<V, DoubleDistance>[] rangeQueries = ClassGenericsUtil.newArrayOfNull(dimensionality, rqcls);
for(int d = 0; d < dimensionality; d++) {
- rangeQueries[d] = relation.getDatabase().getRangeQuery(new PrimitiveDistanceQuery<V, DoubleDistance>(relation, new DimensionSelectingDistanceFunction(d + 1)));
+ rangeQueries[d] = relation.getDatabase().getRangeQuery(new PrimitiveDistanceQuery<V, DoubleDistance>(relation, new DimensionSelectingDistanceFunction(d)));
}
return rangeQueries;
}
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
@Override
@@ -443,7 +430,7 @@ public class DiSHPreferenceVectorIndex<V extends NumberVector<?, ?>> extends Abs
}
/**
- * Factory class
+ * Factory class.
*
* @author Erich Schubert
*
@@ -452,7 +439,7 @@ public class DiSHPreferenceVectorIndex<V extends NumberVector<?, ?>> extends Abs
*
* @param <V> Vector type
*/
- public static class Factory<V extends NumberVector<?, ?>> extends AbstractPreferenceVectorIndex.Factory<V, DiSHPreferenceVectorIndex<V>> {
+ public static class Factory<V extends NumberVector<?>> extends AbstractPreferenceVectorIndex.Factory<V, DiSHPreferenceVectorIndex<V>> {
/**
* The default value for epsilon.
*/
@@ -472,7 +459,7 @@ public class DiSHPreferenceVectorIndex<V extends NumberVector<?, ?>> extends Abs
* Default value: {@link #DEFAULT_EPSILON}
* </p>
*/
- public static final OptionID EPSILON_ID = OptionID.getOrCreateOptionID("dish.epsilon", "A comma separated list of positive doubles specifying the " + "maximum radius of the neighborhood to be " + "considered in each dimension for determination of " + "the preference vector " + "(default is " + DEFAULT_EPSILON + " in each dimension). " + "If only one value is specified, this value " + "will be used for each dimension.");
+ public static final OptionID EPSILON_ID = new OptionID("dish.epsilon", "A comma separated list of positive doubles specifying the " + "maximum radius of the neighborhood to be " + "considered in each dimension for determination of " + "the preference vector " + "(default is " + DEFAULT_EPSILON + " in each dimension). " + "If only one value is specified, this value " + "will be used for each dimension.");
/**
* Option name for {@link #MINPTS_ID}.
@@ -493,12 +480,12 @@ public class DiSHPreferenceVectorIndex<V extends NumberVector<?, ?>> extends Abs
* Key: {@code -dish.minpts}
* </p>
*/
- public static final OptionID MINPTS_ID = OptionID.getOrCreateOptionID(MINPTS_P, "Positive threshold for minumum numbers of points in the epsilon-" + "neighborhood of a point. " + CONDITION);
+ public static final OptionID MINPTS_ID = new OptionID(MINPTS_P, "Positive threshold for minumum numbers of points in the epsilon-" + "neighborhood of a point. " + CONDITION);
/**
* Default strategy.
*/
- public static Strategy DEFAULT_STRATEGY = Strategy.MAX_INTERSECTION;
+ public static final Strategy DEFAULT_STRATEGY = Strategy.MAX_INTERSECTION;
/**
* The strategy for determination of the preference vector, available
@@ -512,10 +499,10 @@ public class DiSHPreferenceVectorIndex<V extends NumberVector<?, ?>> extends Abs
* Default value: {@link #DEFAULT_STRATEGY}
* </p>
*/
- public static final OptionID STRATEGY_ID = OptionID.getOrCreateOptionID("dish.strategy", "The strategy for determination of the preference vector, " + "available strategies are: [" + Strategy.APRIORI + "| " + Strategy.MAX_INTERSECTION + "]" + "(default is " + DEFAULT_STRATEGY + ")");
+ public static final OptionID STRATEGY_ID = new OptionID("dish.strategy", "The strategy for determination of the preference vector, " + "available strategies are: [" + Strategy.APRIORI + "| " + Strategy.MAX_INTERSECTION + "]" + "(default is " + DEFAULT_STRATEGY + ")");
/**
- * The epsilon value for each dimension;
+ * The epsilon value for each dimension.
*/
protected DoubleDistance[] epsilon;
@@ -549,7 +536,7 @@ public class DiSHPreferenceVectorIndex<V extends NumberVector<?, ?>> extends Abs
}
/**
- * Return the minpts value
+ * Return the minpts value.
*
* @return minpts
*/
@@ -564,9 +551,9 @@ public class DiSHPreferenceVectorIndex<V extends NumberVector<?, ?>> extends Abs
*
* @apiviz.exclude
*/
- public static class Parameterizer<V extends NumberVector<?, ?>> extends AbstractParameterizer {
+ public static class Parameterizer<V extends NumberVector<?>> extends AbstractParameterizer {
/**
- * The epsilon value for each dimension;
+ * The epsilon value for each dimension.
*/
protected DoubleDistance[] epsilon;
@@ -583,7 +570,8 @@ public class DiSHPreferenceVectorIndex<V extends NumberVector<?, ?>> extends Abs
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- final IntParameter minptsP = new IntParameter(MINPTS_ID, new GreaterConstraint(0));
+ final IntParameter minptsP = new IntParameter(MINPTS_ID);
+ minptsP.addConstraint(new GreaterConstraint(0));
if(config.grab(minptsP)) {
minpts = minptsP.getValue();
}
diff --git a/src/de/lmu/ifi/dbs/elki/index/preprocessed/preference/HiSCPreferenceVectorIndex.java b/src/de/lmu/ifi/dbs/elki/index/preprocessed/preference/HiSCPreferenceVectorIndex.java
index 65f5f61e..fd6aa0bf 100644
--- a/src/de/lmu/ifi/dbs/elki/index/preprocessed/preference/HiSCPreferenceVectorIndex.java
+++ b/src/de/lmu/ifi/dbs/elki/index/preprocessed/preference/HiSCPreferenceVectorIndex.java
@@ -30,13 +30,15 @@ import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.database.QueryUtil;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreFactory;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreUtil;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.query.knn.KNNQuery;
-import de.lmu.ifi.dbs.elki.database.query.knn.KNNResult;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.distance.distancefunction.EuclideanDistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.logging.progress.FiniteProgress;
@@ -48,7 +50,7 @@ import de.lmu.ifi.dbs.elki.utilities.exceptions.ExceptionMessages;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterConstraint;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.IntervalConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.LessConstraint;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.DoubleParameter;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
@@ -60,14 +62,16 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
* @author Elke Achtert
*
* @see HiSC
+ *
+ * @param <V> Vector type
*/
@Title("HiSC Preprocessor")
@Description("Computes the preference vector of objects of a certain database according to the HiSC algorithm.")
-public class HiSCPreferenceVectorIndex<V extends NumberVector<?, ?>> extends AbstractPreferenceVectorIndex<V> implements PreferenceVectorIndex<V> {
+public class HiSCPreferenceVectorIndex<V extends NumberVector<?>> extends AbstractPreferenceVectorIndex<V> implements PreferenceVectorIndex<V> {
/**
- * Logger to use
+ * Logger to use.
*/
- protected static final Logging logger = Logging.getLogger(HiSCPreferenceVectorIndex.class);
+ private static final Logging LOG = Logging.getLogger(HiSCPreferenceVectorIndex.class);
/**
* Holds the value of parameter alpha.
@@ -94,49 +98,47 @@ public class HiSCPreferenceVectorIndex<V extends NumberVector<?, ?>> extends Abs
@Override
protected void preprocess() {
- if(relation == null || relation.size() <= 0) {
+ if (relation == null || relation.size() <= 0) {
throw new IllegalArgumentException(ExceptionMessages.DATABASE_EMPTY);
}
storage = DataStoreUtil.makeStorage(relation.getDBIDs(), DataStoreFactory.HINT_HOT | DataStoreFactory.HINT_TEMP, BitSet.class);
- StringBuffer msg = new StringBuffer();
+ StringBuilder msg = new StringBuilder();
long start = System.currentTimeMillis();
- FiniteProgress progress = logger.isVerbose() ? new FiniteProgress("Preprocessing preference vector", relation.size(), logger) : null;
+ FiniteProgress progress = LOG.isVerbose() ? new FiniteProgress("Preprocessing preference vector", relation.size(), LOG) : null;
KNNQuery<V, DoubleDistance> knnQuery = QueryUtil.getKNNQuery(relation, EuclideanDistanceFunction.STATIC, k);
for (DBIDIter it = relation.iterDBIDs(); it.valid(); it.advance()) {
- DBID id = it.getDBID();
-
- if(logger.isDebugging()) {
- msg.append("\n\nid = ").append(id);
- ///msg.append(" ").append(database.getObjectLabelQuery().get(id));
+ if (LOG.isDebugging()) {
+ msg.append("\n\nid = ").append(DBIDUtil.toString(it));
+ // /msg.append(" ").append(database.getObjectLabelQuery().get(id));
msg.append("\n knns: ");
}
- KNNResult<DoubleDistance> knns = knnQuery.getKNNForDBID(id, k);
- BitSet preferenceVector = determinePreferenceVector(relation, id, knns.asDBIDs(), msg);
- storage.put(id, preferenceVector);
+ KNNResult<DoubleDistance> knns = knnQuery.getKNNForDBID(it, k);
+ BitSet preferenceVector = determinePreferenceVector(relation, it, knns, msg);
+ storage.put(it, preferenceVector);
- if(progress != null) {
- progress.incrementProcessed(logger);
+ if (progress != null) {
+ progress.incrementProcessed(LOG);
}
}
- if(progress != null) {
- progress.ensureCompleted(logger);
+ if (progress != null) {
+ progress.ensureCompleted(LOG);
}
- if(logger.isDebugging()) {
- logger.debugFine(msg.toString());
+ if (LOG.isDebugging()) {
+ LOG.debugFine(msg.toString());
}
long end = System.currentTimeMillis();
// TODO: re-add timing code!
- if(logger.isVerbose()) {
+ if (LOG.isVerbose()) {
long elapsedTime = end - start;
- logger.verbose(this.getClass().getName() + " runtime: " + elapsedTime + " milliseconds.");
+ LOG.verbose(this.getClass().getName() + " runtime: " + elapsedTime + " milliseconds.");
}
}
@@ -150,20 +152,20 @@ public class HiSCPreferenceVectorIndex<V extends NumberVector<?, ?>> extends Abs
* @param msg a string buffer for debug messages
* @return the preference vector
*/
- private BitSet determinePreferenceVector(Relation<V> relation, DBID id, DBIDs neighborIDs, StringBuffer msg) {
+ private BitSet determinePreferenceVector(Relation<V> relation, DBIDRef id, DBIDs neighborIDs, StringBuilder msg) {
// variances
double[] variances = DatabaseUtil.variances(relation, relation.get(id), neighborIDs);
// preference vector
BitSet preferenceVector = new BitSet(variances.length);
- for(int d = 0; d < variances.length; d++) {
- if(variances[d] < alpha) {
+ for (int d = 0; d < variances.length; d++) {
+ if (variances[d] < alpha) {
preferenceVector.set(d);
}
}
- if(msg != null && logger.isDebugging()) {
- msg.append("\nalpha " + alpha);
+ if (msg != null && LOG.isDebugging()) {
+ msg.append("\nalpha ").append(alpha);
msg.append("\nvariances ");
msg.append(FormatUtil.format(variances, ", ", 4));
msg.append("\npreference ");
@@ -175,7 +177,7 @@ public class HiSCPreferenceVectorIndex<V extends NumberVector<?, ?>> extends Abs
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
@Override
@@ -189,7 +191,7 @@ public class HiSCPreferenceVectorIndex<V extends NumberVector<?, ?>> extends Abs
}
/**
- * Factory class
+ * Factory class.
*
* @author Erich Schubert
*
@@ -198,7 +200,7 @@ public class HiSCPreferenceVectorIndex<V extends NumberVector<?, ?>> extends Abs
*
* @param <V> Vector type
*/
- public static class Factory<V extends NumberVector<?, ?>> extends AbstractPreferenceVectorIndex.Factory<V, HiSCPreferenceVectorIndex<V>> {
+ public static class Factory<V extends NumberVector<?>> extends AbstractPreferenceVectorIndex.Factory<V, HiSCPreferenceVectorIndex<V>> {
/**
* The default value for alpha.
*/
@@ -214,7 +216,7 @@ public class HiSCPreferenceVectorIndex<V extends NumberVector<?, ?>> extends Abs
* Key: {@code -hisc.alpha}
* </p>
*/
- public static final OptionID ALPHA_ID = OptionID.getOrCreateOptionID("hisc.alpha", "The maximum absolute variance along a coordinate axis.");
+ public static final OptionID ALPHA_ID = new OptionID("hisc.alpha", "The maximum absolute variance along a coordinate axis.");
/**
* The number of nearest neighbors considered to determine the preference
@@ -227,7 +229,7 @@ public class HiSCPreferenceVectorIndex<V extends NumberVector<?, ?>> extends Abs
* Default value: three times of the dimensionality of the database objects
* </p>
*/
- public static final OptionID K_ID = OptionID.getOrCreateOptionID("hisc.k", "The number of nearest neighbors considered to determine the preference vector. If this value is not defined, k ist set to three times of the dimensionality of the database objects.");
+ public static final OptionID K_ID = new OptionID("hisc.k", "The number of nearest neighbors considered to determine the preference vector. If this value is not defined, k ist set to three times of the dimensionality of the database objects.");
/**
* Holds the value of parameter {@link #ALPHA_ID}.
@@ -254,10 +256,9 @@ public class HiSCPreferenceVectorIndex<V extends NumberVector<?, ?>> extends Abs
@Override
public HiSCPreferenceVectorIndex<V> instantiate(Relation<V> relation) {
final int usek;
- if(k == null) {
- usek = 3 * DatabaseUtil.dimensionality(relation);
- }
- else {
+ if (k == null) {
+ usek = 3 * RelationUtil.dimensionality(relation);
+ } else {
usek = k;
}
return new HiSCPreferenceVectorIndex<V>(relation, alpha, usek);
@@ -270,7 +271,7 @@ public class HiSCPreferenceVectorIndex<V extends NumberVector<?, ?>> extends Abs
*
* @apiviz.exclude
*/
- public static class Parameterizer<V extends NumberVector<?, ?>> extends AbstractParameterizer {
+ public static class Parameterizer<V extends NumberVector<?>> extends AbstractParameterizer {
/**
* Holds the value of parameter {@link #ALPHA_ID}.
*/
@@ -281,17 +282,21 @@ public class HiSCPreferenceVectorIndex<V extends NumberVector<?, ?>> extends Abs
*/
protected Integer k;
- @Override
+ @Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- final DoubleParameter ALPHA_PARAM = new DoubleParameter(ALPHA_ID, new IntervalConstraint(0.0, IntervalConstraint.IntervalBoundary.OPEN, 1.0, IntervalConstraint.IntervalBoundary.OPEN), DEFAULT_ALPHA);
- if(config.grab(ALPHA_PARAM)) {
- alpha = ALPHA_PARAM.getValue();
+ final DoubleParameter alphaP = new DoubleParameter(ALPHA_ID, DEFAULT_ALPHA);
+ alphaP.addConstraint(new GreaterConstraint(0.0));
+ alphaP.addConstraint(new LessConstraint(1.0));
+ if (config.grab(alphaP)) {
+ alpha = alphaP.doubleValue();
}
- final IntParameter K_PARAM = new IntParameter(K_ID, new GreaterConstraint(0), true);
- if(config.grab(K_PARAM)) {
- k = K_PARAM.getValue();
+ final IntParameter kP = new IntParameter(K_ID);
+ kP.addConstraint(new GreaterConstraint(0));
+ kP.setOptional(true);
+ if (config.grab(kP)) {
+ k = kP.intValue();
}
}
@@ -301,4 +306,4 @@ public class HiSCPreferenceVectorIndex<V extends NumberVector<?, ?>> extends Abs
}
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/index/preprocessed/preference/PreferenceVectorIndex.java b/src/de/lmu/ifi/dbs/elki/index/preprocessed/preference/PreferenceVectorIndex.java
index a0fba8f3..a212c2cd 100644
--- a/src/de/lmu/ifi/dbs/elki/index/preprocessed/preference/PreferenceVectorIndex.java
+++ b/src/de/lmu/ifi/dbs/elki/index/preprocessed/preference/PreferenceVectorIndex.java
@@ -38,7 +38,7 @@ import de.lmu.ifi.dbs.elki.index.IndexFactory;
*
* @param <NV> Vector type
*/
-public interface PreferenceVectorIndex<NV extends NumberVector<?, ?>> extends Index {
+public interface PreferenceVectorIndex<NV extends NumberVector<?>> extends Index {
/**
* Get the precomputed preference vector for a particular object ID.
*
@@ -58,7 +58,7 @@ public interface PreferenceVectorIndex<NV extends NumberVector<?, ?>> extends In
* @param <V> vector type
* @param <I> index type
*/
- public static interface Factory<V extends NumberVector<?, ?>, I extends PreferenceVectorIndex<V>> extends IndexFactory<V, I> {
+ public static interface Factory<V extends NumberVector<?>, I extends PreferenceVectorIndex<V>> extends IndexFactory<V, I> {
/**
* Instantiate the index for a given database.
*
diff --git a/src/de/lmu/ifi/dbs/elki/index/preprocessed/snn/SharedNearestNeighborPreprocessor.java b/src/de/lmu/ifi/dbs/elki/index/preprocessed/snn/SharedNearestNeighborPreprocessor.java
index e4d96028..d57aeb8f 100644
--- a/src/de/lmu/ifi/dbs/elki/index/preprocessed/snn/SharedNearestNeighborPreprocessor.java
+++ b/src/de/lmu/ifi/dbs/elki/index/preprocessed/snn/SharedNearestNeighborPreprocessor.java
@@ -32,12 +32,11 @@ import de.lmu.ifi.dbs.elki.database.ids.ArrayModifiableDBIDs;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
import de.lmu.ifi.dbs.elki.database.query.knn.KNNQuery;
-import de.lmu.ifi.dbs.elki.database.query.knn.KNNResult;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
import de.lmu.ifi.dbs.elki.distance.distancefunction.EuclideanDistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
import de.lmu.ifi.dbs.elki.index.preprocessed.AbstractPreprocessorIndex;
import de.lmu.ifi.dbs.elki.index.preprocessed.knn.MaterializeKNNPreprocessor;
@@ -78,7 +77,7 @@ public class SharedNearestNeighborPreprocessor<O, D extends Distance<D>> extends
/**
* Get a logger for this class.
*/
- private static final Logging logger = Logging.getLogger(SharedNearestNeighborPreprocessor.class);
+ private static final Logging LOG = Logging.getLogger(SharedNearestNeighborPreprocessor.class);
/**
* Holds the number of nearest neighbors to be used.
@@ -117,9 +116,9 @@ public class SharedNearestNeighborPreprocessor<O, D extends Distance<D>> extends
for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
ArrayModifiableDBIDs neighbors = DBIDUtil.newArray(numberOfNeighbors);
KNNResult<D> kNN = knnquery.getKNNForDBID(iditer, numberOfNeighbors);
- for(DistanceResultPair<D> pair : kNN) {
+ for (DBIDIter iter = kNN.iter(); iter.valid(); iter.advance()) {
// if(!id.equals(nid)) {
- neighbors.add(pair);
+ neighbors.add(iter);
// }
// Size limitation to exactly numberOfNeighbors
if(neighbors.size() >= numberOfNeighbors) {
@@ -147,7 +146,7 @@ public class SharedNearestNeighborPreprocessor<O, D extends Distance<D>> extends
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
@Override
@@ -190,7 +189,7 @@ public class SharedNearestNeighborPreprocessor<O, D extends Distance<D>> extends
* Key: {@code sharedNearestNeighbors}
* </p>
*/
- public static final OptionID NUMBER_OF_NEIGHBORS_ID = OptionID.getOrCreateOptionID("sharedNearestNeighbors", "number of nearest neighbors to consider (at least 1)");
+ public static final OptionID NUMBER_OF_NEIGHBORS_ID = new OptionID("sharedNearestNeighbors", "number of nearest neighbors to consider (at least 1)");
/**
* Parameter to indicate the distance function to be used to ascertain the
@@ -203,7 +202,7 @@ public class SharedNearestNeighborPreprocessor<O, D extends Distance<D>> extends
* Key: {@code SNNDistanceFunction}
* </p>
*/
- public static final OptionID DISTANCE_FUNCTION_ID = OptionID.getOrCreateOptionID("SNNDistanceFunction", "the distance function to asses the nearest neighbors");
+ public static final OptionID DISTANCE_FUNCTION_ID = new OptionID("SNNDistanceFunction", "the distance function to asses the nearest neighbors");
/**
* Holds the number of nearest neighbors to be used.
@@ -268,7 +267,8 @@ public class SharedNearestNeighborPreprocessor<O, D extends Distance<D>> extends
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- final IntParameter numberOfNeighborsP = new IntParameter(NUMBER_OF_NEIGHBORS_ID, new GreaterEqualConstraint(1));
+ final IntParameter numberOfNeighborsP = new IntParameter(NUMBER_OF_NEIGHBORS_ID);
+ numberOfNeighborsP.addConstraint(new GreaterEqualConstraint(1));
if(config.grab(numberOfNeighborsP)) {
numberOfNeighbors = numberOfNeighborsP.getValue();
}
diff --git a/src/de/lmu/ifi/dbs/elki/index/preprocessed/subspaceproj/AbstractSubspaceProjectionIndex.java b/src/de/lmu/ifi/dbs/elki/index/preprocessed/subspaceproj/AbstractSubspaceProjectionIndex.java
index da16dd08..1d23681d 100644
--- a/src/de/lmu/ifi/dbs/elki/index/preprocessed/subspaceproj/AbstractSubspaceProjectionIndex.java
+++ b/src/de/lmu/ifi/dbs/elki/index/preprocessed/subspaceproj/AbstractSubspaceProjectionIndex.java
@@ -32,13 +32,13 @@ import de.lmu.ifi.dbs.elki.database.datastore.DataStoreFactory;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
-import de.lmu.ifi.dbs.elki.database.query.DistanceDBIDResult;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
-import de.lmu.ifi.dbs.elki.database.query.GenericDistanceDBIDList;
+import de.lmu.ifi.dbs.elki.database.ids.DistanceDBIDPair;
import de.lmu.ifi.dbs.elki.database.query.range.RangeQuery;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
import de.lmu.ifi.dbs.elki.distance.distancefunction.EuclideanDistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.GenericDistanceDBIDList;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
import de.lmu.ifi.dbs.elki.index.preprocessed.AbstractPreprocessorIndex;
import de.lmu.ifi.dbs.elki.logging.progress.FiniteProgress;
@@ -66,7 +66,7 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ObjectParameter;
*/
@Title("Local PCA Preprocessor")
@Description("Materializes the local PCA and the locally weighted matrix of objects of a database.")
-public abstract class AbstractSubspaceProjectionIndex<NV extends NumberVector<?, ?>, D extends Distance<D>, P extends ProjectionResult> extends AbstractPreprocessorIndex<NV, P> implements SubspaceProjectionIndex<NV, P> {
+public abstract class AbstractSubspaceProjectionIndex<NV extends NumberVector<?>, D extends Distance<D>, P extends ProjectionResult> extends AbstractPreprocessorIndex<NV, P> implements SubspaceProjectionIndex<NV, P> {
/**
* Contains the value of parameter epsilon;
*/
@@ -122,10 +122,10 @@ public abstract class AbstractSubspaceProjectionIndex<NV extends NumberVector<?,
pres = computeProjection(iditer, neighbors, relation);
}
else {
- DistanceResultPair<D> firstQR = neighbors.iterator().next();
- neighbors = new GenericDistanceDBIDList<D>();
- neighbors.add(firstQR);
- pres = computeProjection(iditer, neighbors, relation);
+ DistanceDBIDPair<D> firstQR = neighbors.iter().getDistancePair();
+ GenericDistanceDBIDList<D> newne = new GenericDistanceDBIDList<D>();
+ newne.add(firstQR);
+ pres = computeProjection(iditer, newne, relation);
}
storage.put(iditer, pres);
@@ -177,7 +177,7 @@ public abstract class AbstractSubspaceProjectionIndex<NV extends NumberVector<?,
* @apiviz.stereotype factory
* @apiviz.uses AbstractSubspaceProjectionIndex oneway - - «create»
*/
- public static abstract class Factory<NV extends NumberVector<?, ?>, D extends Distance<D>, I extends AbstractSubspaceProjectionIndex<NV, D, ?>> implements SubspaceProjectionIndex.Factory<NV, I>, Parameterizable {
+ public abstract static class Factory<NV extends NumberVector<?>, D extends Distance<D>, I extends AbstractSubspaceProjectionIndex<NV, D, ?>> implements SubspaceProjectionIndex.Factory<NV, I>, Parameterizable {
/**
* Contains the value of parameter epsilon;
*/
@@ -222,7 +222,7 @@ public abstract class AbstractSubspaceProjectionIndex<NV extends NumberVector<?,
*
* @apiviz.exclude
*/
- public static abstract class Parameterizer<NV extends NumberVector<?, ?>, D extends Distance<D>, C> extends AbstractParameterizer {
+ public abstract static class Parameterizer<NV extends NumberVector<?>, D extends Distance<D>, C> extends AbstractParameterizer {
/**
* Contains the value of parameter epsilon;
*/
@@ -263,9 +263,10 @@ public abstract class AbstractSubspaceProjectionIndex<NV extends NumberVector<?,
}
protected void configMinPts(Parameterization config) {
- IntParameter minptsP = new IntParameter(AbstractProjectedDBSCAN.MINPTS_ID, new GreaterConstraint(0));
+ IntParameter minptsP = new IntParameter(AbstractProjectedDBSCAN.MINPTS_ID);
+ minptsP.addConstraint(new GreaterConstraint(0));
if(config.grab(minptsP)) {
- minpts = minptsP.getValue();
+ minpts = minptsP.intValue();
}
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/index/preprocessed/subspaceproj/FourCSubspaceIndex.java b/src/de/lmu/ifi/dbs/elki/index/preprocessed/subspaceproj/FourCSubspaceIndex.java
index e61b9144..80212981 100644
--- a/src/de/lmu/ifi/dbs/elki/index/preprocessed/subspaceproj/FourCSubspaceIndex.java
+++ b/src/de/lmu/ifi/dbs/elki/index/preprocessed/subspaceproj/FourCSubspaceIndex.java
@@ -26,13 +26,13 @@ package de.lmu.ifi.dbs.elki.index.preprocessed.subspaceproj;
import java.util.ArrayList;
import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.ModifiableDBIDs;
-import de.lmu.ifi.dbs.elki.database.query.DistanceDBIDResult;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.logging.LoggingUtil;
@@ -67,11 +67,11 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.Flag;
*/
@Title("4C Preprocessor")
@Description("Computes the local dimensionality and locally weighted matrix of objects of a certain database according to the 4C algorithm.\n" + "The PCA is based on epsilon range queries.")
-public class FourCSubspaceIndex<V extends NumberVector<V, ?>, D extends Distance<D>> extends AbstractSubspaceProjectionIndex<V, D, PCAFilteredResult> {
+public class FourCSubspaceIndex<V extends NumberVector<?>, D extends Distance<D>> extends AbstractSubspaceProjectionIndex<V, D, PCAFilteredResult> {
/**
* Our logger
*/
- private final static Logging logger = Logging.getLogger(FourCSubspaceIndex.class);
+ private static final Logging LOG = Logging.getLogger(FourCSubspaceIndex.class);
/**
* The Filtered PCA Runner
@@ -95,23 +95,23 @@ public class FourCSubspaceIndex<V extends NumberVector<V, ?>, D extends Distance
@Override
protected PCAFilteredResult computeProjection(DBIDRef id, DistanceDBIDResult<D> neighbors, Relation<V> database) {
ModifiableDBIDs ids = DBIDUtil.newArray(neighbors.size());
- for(DistanceResultPair<D> neighbor : neighbors) {
- ids.add(neighbor.getDBID());
+ for (DBIDIter neighbor = neighbors.iter(); neighbor.valid(); neighbor.advance()) {
+ ids.add(neighbor);
}
PCAFilteredResult pcares = pca.processIds(ids, database);
- if(logger.isDebugging()) {
- StringBuffer msg = new StringBuffer();
- msg.append(id).append(" "); //.append(database.getObjectLabelQuery().get(id));
+ if (LOG.isDebugging()) {
+ StringBuilder msg = new StringBuilder();
+ msg.append(id).append(' '); // .append(database.getObjectLabelQuery().get(id));
msg.append("\ncorrDim ").append(pcares.getCorrelationDimension());
- logger.debugFine(msg.toString());
+ LOG.debugFine(msg.toString());
}
return pcares;
}
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
@Override
@@ -135,7 +135,7 @@ public class FourCSubspaceIndex<V extends NumberVector<V, ?>, D extends Distance
* @param <V> Vector type
* @param <D> Distance type
*/
- public static class Factory<V extends NumberVector<V, ?>, D extends Distance<D>> extends AbstractSubspaceProjectionIndex.Factory<V, D, FourCSubspaceIndex<V, D>> {
+ public static class Factory<V extends NumberVector<?>, D extends Distance<D>> extends AbstractSubspaceProjectionIndex.Factory<V, D, FourCSubspaceIndex<V, D>> {
/**
* The default value for delta.
*/
@@ -171,7 +171,7 @@ public class FourCSubspaceIndex<V extends NumberVector<V, ?>, D extends Distance
*
* @apiviz.exclude
*/
- public static class Parameterizer<V extends NumberVector<V, ?>, D extends Distance<D>> extends AbstractSubspaceProjectionIndex.Factory.Parameterizer<V, D, Factory<V, D>> {
+ public static class Parameterizer<V extends NumberVector<?>, D extends Distance<D>> extends AbstractSubspaceProjectionIndex.Factory.Parameterizer<V, D, Factory<V, D>> {
/**
* The Filtered PCA Runner
*/
@@ -183,18 +183,19 @@ public class FourCSubspaceIndex<V extends NumberVector<V, ?>, D extends Distance
// flag absolute
boolean absolute = false;
Flag absoluteF = new Flag(LimitEigenPairFilter.EIGENPAIR_FILTER_ABSOLUTE);
- if(config.grab(absoluteF)) {
- absolute = absoluteF.getValue();
+ if (config.grab(absoluteF)) {
+ absolute = absoluteF.isTrue();
}
// Parameter delta
double delta = 0.0;
- DoubleParameter deltaP = new DoubleParameter(LimitEigenPairFilter.EIGENPAIR_FILTER_DELTA, new GreaterEqualConstraint(0), DEFAULT_DELTA);
- if(config.grab(deltaP)) {
- delta = deltaP.getValue();
+ DoubleParameter deltaP = new DoubleParameter(LimitEigenPairFilter.EIGENPAIR_FILTER_DELTA, DEFAULT_DELTA);
+ deltaP.addConstraint(new GreaterEqualConstraint(0));
+ if (config.grab(deltaP)) {
+ delta = deltaP.doubleValue();
}
// Absolute flag doesn't have a sensible default value for delta.
- if(absolute && deltaP.tookDefaultValue()) {
+ if (absolute && deltaP.tookDefaultValue()) {
config.reportError(new WrongParameterValueException("Illegal parameter setting: " + "Flag " + absoluteF.getName() + " is set, " + "but no value for " + deltaP.getName() + " is specified."));
}
@@ -220,7 +221,7 @@ public class FourCSubspaceIndex<V extends NumberVector<V, ?>, D extends Distance
// eigen pair filter
pcaParameters.addParameter(PCAFilteredRunner.PCA_EIGENPAIR_FILTER, LimitEigenPairFilter.class.getName());
// abs
- if(absolute) {
+ if (absolute) {
pcaParameters.addFlag(LimitEigenPairFilter.EIGENPAIR_FILTER_ABSOLUTE);
}
// delta
@@ -231,18 +232,18 @@ public class FourCSubspaceIndex<V extends NumberVector<V, ?>, D extends Distance
pcaParameters.addParameter(PCAFilteredRunner.SMALL_ID, 1);
Class<PCAFilteredRunner<V>> cls = ClassGenericsUtil.uglyCastIntoSubclass(PCAFilteredRunner.class);
pca = pcaParameters.tryInstantiate(cls);
- for(ParameterException e : pcaParameters.getErrors()) {
+ for (ParameterException e : pcaParameters.getErrors()) {
LoggingUtil.warning("Error in internal parameterization: " + e.getMessage());
}
- final ArrayList<ParameterConstraint<Number>> deltaCons = new ArrayList<ParameterConstraint<Number>>();
+ final ArrayList<ParameterConstraint<? super Double>> deltaCons = new ArrayList<ParameterConstraint<? super Double>>();
// TODO: this constraint is already set in the parameter itself, since
// it
// also applies to the relative case, right? -- erich
// deltaCons.add(new GreaterEqualConstraint(0));
deltaCons.add(new LessEqualConstraint(1));
- GlobalParameterConstraint gpc = new ParameterFlagGlobalConstraint<Number, Double>(deltaP, deltaCons, absoluteF, false);
+ GlobalParameterConstraint gpc = new ParameterFlagGlobalConstraint<Double>(deltaP, deltaCons, absoluteF, false);
config.checkConstraint(gpc);
}
@@ -252,4 +253,4 @@ public class FourCSubspaceIndex<V extends NumberVector<V, ?>, D extends Distance
}
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/index/preprocessed/subspaceproj/PreDeConSubspaceIndex.java b/src/de/lmu/ifi/dbs/elki/index/preprocessed/subspaceproj/PreDeConSubspaceIndex.java
index 34bfb5e9..17590804 100644
--- a/src/de/lmu/ifi/dbs/elki/index/preprocessed/subspaceproj/PreDeConSubspaceIndex.java
+++ b/src/de/lmu/ifi/dbs/elki/index/preprocessed/subspaceproj/PreDeConSubspaceIndex.java
@@ -24,11 +24,11 @@ package de.lmu.ifi.dbs.elki.index.preprocessed.subspaceproj;
*/
import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
-import de.lmu.ifi.dbs.elki.database.query.DistanceDBIDResult;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Matrix;
@@ -37,8 +37,8 @@ import de.lmu.ifi.dbs.elki.utilities.FormatUtil;
import de.lmu.ifi.dbs.elki.utilities.documentation.Description;
import de.lmu.ifi.dbs.elki.utilities.documentation.Title;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.IntervalConstraint;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.IntervalConstraint.IntervalBoundary;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.LessConstraint;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.DoubleParameter;
@@ -55,11 +55,11 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.DoubleParameter;
*/
@Title("PreDeCon Preprocessor")
@Description("Computes the projected dimension of objects of a certain database according to the PreDeCon algorithm.\n" + "The variance analysis is based on epsilon range queries.")
-public class PreDeConSubspaceIndex<V extends NumberVector<? extends V, ?>, D extends Distance<D>> extends AbstractSubspaceProjectionIndex<V, D, SubspaceProjectionResult> {
+public class PreDeConSubspaceIndex<V extends NumberVector<?>, D extends Distance<D>> extends AbstractSubspaceProjectionIndex<V, D, SubspaceProjectionResult> {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(PreDeConSubspaceIndex.class);
+ private static final Logging LOG = Logging.getLogger(PreDeConSubspaceIndex.class);
/**
* The threshold for small eigenvalues.
@@ -87,25 +87,25 @@ public class PreDeConSubspaceIndex<V extends NumberVector<? extends V, ?>, D ext
@Override
protected SubspaceProjectionResult computeProjection(DBIDRef id, DistanceDBIDResult<D> neighbors, Relation<V> database) {
- StringBuffer msg = null;
+ StringBuilder msg = null;
int referenceSetSize = neighbors.size();
V obj = database.get(id);
- if(getLogger().isDebugging()) {
- msg = new StringBuffer();
- msg.append("referenceSetSize = " + referenceSetSize);
- msg.append("\ndelta = " + delta);
+ if (getLogger().isDebugging()) {
+ msg = new StringBuilder();
+ msg.append("referenceSetSize = ").append(referenceSetSize);
+ msg.append("\ndelta = ").append(delta);
}
- if(referenceSetSize == 0) {
+ if (referenceSetSize == 0) {
throw new RuntimeException("Reference Set Size = 0. This should never happen!");
}
// prepare similarity matrix
int dim = obj.getDimensionality();
Matrix simMatrix = new Matrix(dim, dim, 0);
- for(int i = 0; i < dim; i++) {
+ for (int i = 0; i < dim; i++) {
simMatrix.set(i, i, 1);
}
@@ -114,38 +114,41 @@ public class PreDeConSubspaceIndex<V extends NumberVector<? extends V, ?>, D ext
// start variance analysis
double[] sum = new double[dim];
- for(DistanceResultPair<D> neighbor : neighbors) {
- V o = database.get(neighbor.getDBID());
- for(int d = 0; d < dim; d++) {
- sum[d] += Math.pow(obj.doubleValue(d + 1) - o.doubleValue(d + 1), 2.0);
+ for (DBIDIter neighbor = neighbors.iter(); neighbor.valid(); neighbor.advance()) {
+ V o = database.get(neighbor);
+ for (int d = 0; d < dim; d++) {
+ sum[d] += Math.pow(obj.doubleValue(d) - o.doubleValue(d), 2.0);
}
}
- for(int d = 0; d < dim; d++) {
- if(Math.sqrt(sum[d]) / referenceSetSize <= delta) {
- if(msg != null) {
- msg.append("\nsum[" + d + "]= " + sum[d]);
- msg.append("\n Math.sqrt(sum[d]) / referenceSetSize)= " + Math.sqrt(sum[d]) / referenceSetSize);
+ for (int d = 0; d < dim; d++) {
+ if (Math.sqrt(sum[d]) / referenceSetSize <= delta) {
+ if (msg != null) {
+ msg.append("\nsum[").append(d).append("]= ").append(sum[d]);
+ msg.append("\n Math.sqrt(sum[d]) / referenceSetSize)= ").append(Math.sqrt(sum[d]) / referenceSetSize);
}
// projDim++;
simMatrix.set(d, d, kappa);
- }
- else {
+ } else {
// bug in paper?
projDim++;
}
}
- if(projDim == 0) {
- if(msg != null) {
+ if (projDim == 0) {
+ if (msg != null) {
// msg.append("\nprojDim == 0!");
}
projDim = dim;
}
- if(msg != null) {
- msg.append("\nprojDim " /*+ database.getObjectLabelQuery().get(id)*/ + ": " + projDim);
- msg.append("\nsimMatrix " /*+ database.getObjectLabelQuery().get(id)*/ + ": " + FormatUtil.format(simMatrix, FormatUtil.NF4));
+ if (msg != null) {
+ msg.append("\nprojDim ");
+ // .append(database.getObjectLabelQuery().get(id));
+ msg.append(": ").append(projDim);
+ msg.append("\nsimMatrix ");
+ // .append(database.getObjectLabelQuery().get(id));
+ msg.append(": ").append(FormatUtil.format(simMatrix, FormatUtil.NF4));
getLogger().debugFine(msg.toString());
}
@@ -164,11 +167,11 @@ public class PreDeConSubspaceIndex<V extends NumberVector<? extends V, ?>, D ext
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
- * Factory
+ * Factory.
*
* @author Erich Schubert
*
@@ -178,22 +181,22 @@ public class PreDeConSubspaceIndex<V extends NumberVector<? extends V, ?>, D ext
* @param <V> Vector type
* @param <D> Distance type
*/
- public static class Factory<V extends NumberVector<? extends V, ?>, D extends Distance<D>> extends AbstractSubspaceProjectionIndex.Factory<V, D, PreDeConSubspaceIndex<V, D>> {
+ public static class Factory<V extends NumberVector<?>, D extends Distance<D>> extends AbstractSubspaceProjectionIndex.Factory<V, D, PreDeConSubspaceIndex<V, D>> {
/**
* The default value for delta.
*/
public static final double DEFAULT_DELTA = 0.01;
/**
- * Parameter for Delta
+ * Parameter for Delta.
*/
- public static final OptionID DELTA_ID = OptionID.getOrCreateOptionID("predecon.delta", "a double between 0 and 1 specifying the threshold for small Eigenvalues (default is delta = " + DEFAULT_DELTA + ").");
+ public static final OptionID DELTA_ID = new OptionID("predecon.delta", "a double between 0 and 1 specifying the threshold for small Eigenvalues (default is delta = " + DEFAULT_DELTA + ").");
/**
* The threshold for small eigenvalues.
*/
protected double delta;
-
+
/**
* Constructor.
*
@@ -219,7 +222,7 @@ public class PreDeConSubspaceIndex<V extends NumberVector<? extends V, ?>, D ext
*
* @apiviz.exclude
*/
- public static class Parameterizer<V extends NumberVector<? extends V, ?>, D extends Distance<D>> extends AbstractSubspaceProjectionIndex.Factory.Parameterizer<V, D, Factory<V, D>> {
+ public static class Parameterizer<V extends NumberVector<?>, D extends Distance<D>> extends AbstractSubspaceProjectionIndex.Factory.Parameterizer<V, D, Factory<V, D>> {
/**
* The threshold for small eigenvalues.
*/
@@ -228,9 +231,11 @@ public class PreDeConSubspaceIndex<V extends NumberVector<? extends V, ?>, D ext
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- DoubleParameter deltaP = new DoubleParameter(DELTA_ID, new IntervalConstraint(0.0, IntervalBoundary.OPEN, 1.0, IntervalBoundary.OPEN), DEFAULT_DELTA);
- if(config.grab(deltaP)) {
- delta = deltaP.getValue();
+ DoubleParameter deltaP = new DoubleParameter(DELTA_ID, DEFAULT_DELTA);
+ deltaP.addConstraint(new GreaterConstraint(0.0));
+ deltaP.addConstraint(new LessConstraint(1.0));
+ if (config.grab(deltaP)) {
+ delta = deltaP.doubleValue();
}
}
@@ -240,4 +245,4 @@ public class PreDeConSubspaceIndex<V extends NumberVector<? extends V, ?>, D ext
}
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/index/preprocessed/subspaceproj/SubspaceProjectionIndex.java b/src/de/lmu/ifi/dbs/elki/index/preprocessed/subspaceproj/SubspaceProjectionIndex.java
index 210db8f6..67cc1701 100644
--- a/src/de/lmu/ifi/dbs/elki/index/preprocessed/subspaceproj/SubspaceProjectionIndex.java
+++ b/src/de/lmu/ifi/dbs/elki/index/preprocessed/subspaceproj/SubspaceProjectionIndex.java
@@ -38,7 +38,7 @@ import de.lmu.ifi.dbs.elki.math.linearalgebra.ProjectionResult;
*
* @param <NV> Vector type
*/
-public interface SubspaceProjectionIndex<NV extends NumberVector<?, ?>, P extends ProjectionResult> extends LocalProjectionIndex<NV, P> {
+public interface SubspaceProjectionIndex<NV extends NumberVector<?>, P extends ProjectionResult> extends LocalProjectionIndex<NV, P> {
/**
* Get the precomputed local subspace for a particular object ID.
*
@@ -60,7 +60,7 @@ public interface SubspaceProjectionIndex<NV extends NumberVector<?, ?>, P extend
* @param <NV> Vector type
* @param <I> Index type produced
*/
- public static interface Factory<NV extends NumberVector<?, ?>, I extends SubspaceProjectionIndex<NV, ?>> extends LocalProjectionIndex.Factory<NV, I> {
+ public static interface Factory<NV extends NumberVector<?>, I extends SubspaceProjectionIndex<NV, ?>> extends LocalProjectionIndex.Factory<NV, I> {
/**
* Instantiate the index for a given database.
*
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/AbstractLeafEntry.java b/src/de/lmu/ifi/dbs/elki/index/tree/AbstractLeafEntry.java
index d2d38976..ea5fd6de 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/AbstractLeafEntry.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/AbstractLeafEntry.java
@@ -74,7 +74,7 @@ public abstract class AbstractLeafEntry implements LeafEntry {
*/
@Override
public void writeExternal(ObjectOutput out) throws IOException {
- out.writeInt(id.getIntegerID());
+ out.writeInt(DBIDUtil.asInteger(id));
}
/**
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/AbstractNode.java b/src/de/lmu/ifi/dbs/elki/index/tree/AbstractNode.java
index 42f4fa11..4f2c6d04 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/AbstractNode.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/AbstractNode.java
@@ -341,7 +341,7 @@ public abstract class AbstractNode<E extends Entry> extends AbstractExternalizab
public final void splitTo(AbstractNode<E> newNode, List<E> sorting, int splitPoint) {
assert (isLeaf() == newNode.isLeaf());
deleteAllEntries();
- StringBuffer msg = LoggingConfiguration.DEBUG ? new StringBuffer("\n") : null;
+ StringBuilder msg = LoggingConfiguration.DEBUG ? new StringBuilder("\n") : null;
for(int i = 0; i < splitPoint; i++) {
addEntry(sorting.get(i));
@@ -373,7 +373,7 @@ public abstract class AbstractNode<E extends Entry> extends AbstractExternalizab
public final void splitTo(AbstractNode<E> newNode, List<E> assignmentsToFirst, List<E> assignmentsToSecond) {
assert (isLeaf() == newNode.isLeaf());
deleteAllEntries();
- StringBuffer msg = LoggingConfiguration.DEBUG ? new StringBuffer() : null;
+ StringBuilder msg = LoggingConfiguration.DEBUG ? new StringBuilder() : null;
// assignments to this node
for(E entry : assignmentsToFirst) {
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/IndexTree.java b/src/de/lmu/ifi/dbs/elki/index/tree/IndexTree.java
index e4cdc71f..13e91151 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/IndexTree.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/IndexTree.java
@@ -44,7 +44,7 @@ public abstract class IndexTree<N extends Node<E>, E extends Entry> {
/**
* The file storing the entries of this index.
*/
- final private PageFile<N> file;
+ private final PageFile<N> file;
/**
* True if this index is already initialized.
@@ -105,7 +105,7 @@ public abstract class IndexTree<N extends Node<E>, E extends Entry> {
*
* @return the static logger
*/
- abstract protected Logging getLogger();
+ protected abstract Logging getLogger();
/**
* Returns the entry representing the root if this index.
@@ -213,6 +213,9 @@ public abstract class IndexTree<N extends Node<E>, E extends Entry> {
/**
* Initializes this index from an existing persistent file.
+ *
+ * @param header File header
+ * @param file Page file
*/
public void initializeFromFile(TreeIndexHeader header, PageFile<N> file) {
this.dirCapacity = header.getDirCapacity();
@@ -221,7 +224,7 @@ public abstract class IndexTree<N extends Node<E>, E extends Entry> {
this.leafMinimum = header.getLeafMinimum();
if(getLogger().isDebugging()) {
- StringBuffer msg = new StringBuffer();
+ StringBuilder msg = new StringBuilder();
msg.append(getClass());
msg.append("\n file = ").append(file.getClass());
getLogger().debugFine(msg.toString());
@@ -242,7 +245,7 @@ public abstract class IndexTree<N extends Node<E>, E extends Entry> {
createEmptyRoot(exampleLeaf);
if(getLogger().isDebugging()) {
- StringBuffer msg = new StringBuffer();
+ StringBuilder msg = new StringBuilder();
msg.append(getClass()).append("\n");
msg.append(" file = ").append(file.getClass()).append("\n");
msg.append(" maximum number of dir entries = ").append((dirCapacity - 1)).append("\n");
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/IndexTreePath.java b/src/de/lmu/ifi/dbs/elki/index/tree/IndexTreePath.java
index 560bd250..2634ae07 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/IndexTreePath.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/IndexTreePath.java
@@ -284,7 +284,7 @@ public class IndexTreePath<E extends Entry> {
*/
@Override
public String toString() {
- StringBuffer buffer = new StringBuffer("[");
+ StringBuilder buffer = new StringBuilder("[");
for(int counter = 0, maxCounter = getPathCount(); counter < maxCounter; counter++) {
if(counter > 0) {
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/TreeIndexFactory.java b/src/de/lmu/ifi/dbs/elki/index/tree/TreeIndexFactory.java
index a170ca5b..c40effe5 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/TreeIndexFactory.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/TreeIndexFactory.java
@@ -60,7 +60,7 @@ public abstract class TreeIndexFactory<O, I extends Index> implements IndexFacto
* Key: {@code -treeindex.file}
* </p>
*/
- public static final OptionID FILE_ID = OptionID.getOrCreateOptionID("treeindex.file", "The name of the file storing the index. " + "If this parameter is not set the index is hold in the main memory.");
+ public static final OptionID FILE_ID = new OptionID("treeindex.file", "The name of the file storing the index. " + "If this parameter is not set the index is hold in the main memory.");
/**
* Parameter to specify the size of a page in bytes, must be an integer
@@ -72,7 +72,7 @@ public abstract class TreeIndexFactory<O, I extends Index> implements IndexFacto
* Key: {@code -treeindex.pagesize}
* </p>
*/
- public static final OptionID PAGE_SIZE_ID = OptionID.getOrCreateOptionID("treeindex.pagesize", "The size of a page in bytes.");
+ public static final OptionID PAGE_SIZE_ID = new OptionID("treeindex.pagesize", "The size of a page in bytes.");
/**
* Parameter to specify the size of the cache in bytes, must be an integer
@@ -84,7 +84,7 @@ public abstract class TreeIndexFactory<O, I extends Index> implements IndexFacto
* Key: {@code -treeindex.cachesize}
* </p>
*/
- public static final OptionID CACHE_SIZE_ID = OptionID.getOrCreateOptionID("treeindex.cachesize", "The size of the cache in bytes.");
+ public static final OptionID CACHE_SIZE_ID = new OptionID("treeindex.cachesize", "The size of the cache in bytes.");
/**
* Holds the name of the file storing the index specified by {@link #FILE_ID},
@@ -126,13 +126,12 @@ public abstract class TreeIndexFactory<O, I extends Index> implements IndexFacto
// FIXME: make this single-shot when filename is set!
protected <N extends ExternalizablePage> PageFile<N> makePageFile(Class<N> cls) {
final PageFile<N> inner;
- if(fileName == null) {
+ if (fileName == null) {
inner = new MemoryPageFile<N>(pageSize);
- }
- else {
+ } else {
inner = new PersistentPageFile<N>(pageSize, fileName, cls);
}
- if(cacheSize >= Integer.MAX_VALUE) {
+ if (cacheSize >= Integer.MAX_VALUE) {
return inner;
}
return new LRUCache<N>(cacheSize, inner);
@@ -148,7 +147,7 @@ public abstract class TreeIndexFactory<O, I extends Index> implements IndexFacto
*
* @apiviz.exclude
*/
- public static abstract class Parameterizer<O> extends AbstractParameterizer {
+ public abstract static class Parameterizer<O> extends AbstractParameterizer {
protected String fileName = null;
protected int pageSize;
@@ -158,26 +157,28 @@ public abstract class TreeIndexFactory<O, I extends Index> implements IndexFacto
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- FileParameter FILE_PARAM = new FileParameter(FILE_ID, FileParameter.FileType.OUTPUT_FILE, true);
- if(config.grab(FILE_PARAM)) {
- fileName = FILE_PARAM.getValue().getPath();
- }
- else {
+ FileParameter fileNameP = new FileParameter(FILE_ID, FileParameter.FileType.OUTPUT_FILE, true);
+ if (config.grab(fileNameP)) {
+ fileName = fileNameP.getValue().getPath();
+ } else {
fileName = null;
}
- final IntParameter PAGE_SIZE_PARAM = new IntParameter(PAGE_SIZE_ID, new GreaterConstraint(0), 4000);
- if(config.grab(PAGE_SIZE_PARAM)) {
- pageSize = PAGE_SIZE_PARAM.getValue();
+ final IntParameter pageSizeP = new IntParameter(PAGE_SIZE_ID, 4000);
+ pageSizeP.addConstraint(new GreaterConstraint(0));
+ if (config.grab(pageSizeP)) {
+ pageSize = pageSizeP.getValue();
}
- LongParameter CACHE_SIZE_PARAM = new LongParameter(CACHE_SIZE_ID, new GreaterEqualConstraint(0), Integer.MAX_VALUE);
- if(config.grab(CACHE_SIZE_PARAM)) {
- cacheSize = CACHE_SIZE_PARAM.getValue();
+ // FIXME: long, but limited to int values?!?
+ LongParameter cacheSizeP = new LongParameter(CACHE_SIZE_ID, Integer.MAX_VALUE);
+ cacheSizeP.addConstraint(new GreaterEqualConstraint(0));
+ if (config.grab(cacheSizeP)) {
+ cacheSize = cacheSizeP.getValue();
}
}
@Override
protected abstract TreeIndexFactory<O, ?> makeInstance();
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/AbstractMTree.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/AbstractMTree.java
index a35a4057..a7a063bd 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/AbstractMTree.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/AbstractMTree.java
@@ -26,7 +26,6 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
-import java.util.Map;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
@@ -43,12 +42,8 @@ import de.lmu.ifi.dbs.elki.index.tree.metrical.MetricalIndexTree;
import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.split.Assignments;
import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.split.MLBDistSplit;
import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.split.MTreeSplit;
-import de.lmu.ifi.dbs.elki.index.tree.query.GenericMTreeDistanceSearchCandidate;
import de.lmu.ifi.dbs.elki.persistent.PageFile;
import de.lmu.ifi.dbs.elki.persistent.PageFileUtil;
-import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.Heap;
-import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.KNNHeap;
-import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.UpdatableHeap;
/**
* Abstract super class for all M-Tree variants.
@@ -65,17 +60,17 @@ import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.UpdatableHeap;
*/
public abstract class AbstractMTree<O, D extends Distance<D>, N extends AbstractMTreeNode<O, D, N, E>, E extends MTreeEntry<D>> extends MetricalIndexTree<O, D, N, E> {
/**
- * Debugging flag: do extra integrity checks
+ * Debugging flag: do extra integrity checks.
*/
- protected final static boolean extraIntegrityChecks = true;
+ protected static final boolean EXTRA_INTEGRITY_CHECKS = false;
/**
- * Holds the instance of the trees distance function
+ * Holds the instance of the trees distance function.
*/
protected DistanceFunction<O, D> distanceFunction;
/**
- * The distance query
+ * The distance query.
*/
protected DistanceQuery<O, D> distanceQuery;
@@ -103,11 +98,11 @@ public abstract class AbstractMTree<O, D extends Distance<D>, N extends Abstract
}
/**
- * Get the distance factory
+ * Get the distance factory.
*
* @return the distance factory used
*/
- protected final D getDistanceFactory() {
+ public final D getDistanceFactory() {
return distanceFunction.getDistanceFactory();
}
@@ -120,7 +115,7 @@ public abstract class AbstractMTree<O, D extends Distance<D>, N extends Abstract
*/
@Override
public String toString() {
- StringBuffer result = new StringBuffer();
+ StringBuilder result = new StringBuilder();
int dirNodes = 0;
int leafNodes = 0;
int objects = 0;
@@ -128,8 +123,8 @@ public abstract class AbstractMTree<O, D extends Distance<D>, N extends Abstract
N node = getRoot();
- while(!node.isLeaf()) {
- if(node.getNumEntries() > 0) {
+ while (!node.isLeaf()) {
+ if (node.getNumEntries() > 0) {
E entry = node.getEntry(0);
node = getNode(entry);
levels++;
@@ -137,22 +132,20 @@ public abstract class AbstractMTree<O, D extends Distance<D>, N extends Abstract
}
BreadthFirstEnumeration<N, E> enumeration = new BreadthFirstEnumeration<N, E>(this, getRootPath());
- while(enumeration.hasMoreElements()) {
+ while (enumeration.hasMoreElements()) {
IndexTreePath<E> path = enumeration.nextElement();
E entry = path.getLastPathComponent().getEntry();
- if(entry.isLeafEntry()) {
+ if (entry.isLeafEntry()) {
objects++;
result.append("\n ").append(entry.toString());
- }
- else {
+ } else {
node = getNode(entry);
result.append("\n\n").append(node).append(", numEntries = ").append(node.getNumEntries());
result.append("\n").append(entry.toString());
- if(node.isLeaf()) {
+ if (node.isLeaf()) {
leafNodes++;
- }
- else {
+ } else {
dirNodes++;
}
}
@@ -178,17 +171,17 @@ public abstract class AbstractMTree<O, D extends Distance<D>, N extends Abstract
*/
// todo: implement a bulk load for M-Tree and remove this method
public void insert(E entry, boolean withPreInsert) {
- if(getLogger().isDebugging()) {
+ if (getLogger().isDebugging()) {
getLogger().debugFine("insert " + entry.getRoutingObjectID() + "\n");
}
- if(!initialized) {
+ if (!initialized) {
initialize(entry);
}
// choose subtree for insertion
IndexTreePath<E> subtree = choosePath(entry, getRootPath());
- if(getLogger().isDebugging()) {
+ if (getLogger().isDebugging()) {
getLogger().debugFine("insertion-subtree " + subtree + "\n");
}
@@ -198,7 +191,7 @@ public abstract class AbstractMTree<O, D extends Distance<D>, N extends Abstract
entry.setParentDistance(parentDistance);
// create leaf entry and do pre insert
- if(withPreInsert) {
+ if (withPreInsert) {
preInsert(entry);
}
@@ -211,8 +204,8 @@ public abstract class AbstractMTree<O, D extends Distance<D>, N extends Abstract
adjustTree(subtree);
// test
- if(extraIntegrityChecks) {
- if(withPreInsert) {
+ if (EXTRA_INTEGRITY_CHECKS) {
+ if (withPreInsert) {
getRoot().integrityCheck(this, getRootEntry());
}
}
@@ -221,13 +214,13 @@ public abstract class AbstractMTree<O, D extends Distance<D>, N extends Abstract
/**
* Bulk insert.
*
- * @param entries
+ * @param entries Entries to insert
*/
public void insertAll(List<E> entries) {
- if(!initialized && entries.size() > 0) {
+ if (!initialized && entries.size() > 0) {
initialize(entries.get(0));
}
- for(E entry : entries) {
+ for (E entry : entries) {
insert(entry, false);
}
}
@@ -239,84 +232,6 @@ public abstract class AbstractMTree<O, D extends Distance<D>, N extends Abstract
}
/**
- * Performs a k-nearest neighbor query for the given FeatureVector with the
- * given parameter k and the according distance function. The query result is
- * in ascending order to the distance to the query object.
- *
- * @param q the id of the query object
- * @param knnList the query result list
- */
- protected final void doKNNQuery(DBID q, KNNHeap<D> knnList) {
- final Heap<GenericMTreeDistanceSearchCandidate<D>> pq = new UpdatableHeap<GenericMTreeDistanceSearchCandidate<D>>();
-
- // push root
- pq.add(new GenericMTreeDistanceSearchCandidate<D>(getDistanceFactory().nullDistance(), getRootID(), null));
- D d_k = knnList.getKNNDistance();
-
- if (d_k == null) {
- // Empty tree?
- return;
- }
-
- // search in tree
- while(!pq.isEmpty()) {
- GenericMTreeDistanceSearchCandidate<D> pqNode = pq.poll();
-
- if(pqNode.mindist.compareTo(d_k) > 0) {
- return;
- }
-
- N node = getNode(pqNode.nodeID);
- DBID o_p = pqNode.routingObjectID;
-
- // directory node
- if(!node.isLeaf()) {
- for(int i = 0; i < node.getNumEntries(); i++) {
- E entry = node.getEntry(i);
- DBID o_r = entry.getRoutingObjectID();
- D r_or = entry.getCoveringRadius();
- D d1 = o_p != null ? distanceQuery.distance(o_p, q) : getDistanceFactory().nullDistance();
- D d2 = o_p != null ? distanceQuery.distance(o_r, o_p) : getDistanceFactory().nullDistance();
-
- D diff = d1.compareTo(d2) > 0 ? d1.minus(d2) : d2.minus(d1);
-
- D sum = d_k.plus(r_or);
-
- if(diff.compareTo(sum) <= 0) {
- D d3 = distance(o_r, q);
- D d_min = DistanceUtil.max(d3.minus(r_or), getDistanceFactory().nullDistance());
- if(d_min.compareTo(d_k) <= 0) {
- pq.add(new GenericMTreeDistanceSearchCandidate<D>(d_min, getPageID(entry), o_r));
- }
- }
- }
-
- }
-
- // data node
- else {
- for(int i = 0; i < node.getNumEntries(); i++) {
- E entry = node.getEntry(i);
- DBID o_j = entry.getRoutingObjectID();
-
- D d1 = o_p != null ? distanceQuery.distance(o_p, q) : getDistanceFactory().nullDistance();
- D d2 = o_p != null ? distanceQuery.distance(o_j, o_p) : getDistanceFactory().nullDistance();
-
- D diff = d1.compareTo(d2) > 0 ? d1.minus(d2) : d2.minus(d1);
-
- if(diff.compareTo(d_k) <= 0) {
- D d3 = distanceQuery.distance(o_j, q);
- if(d3.compareTo(d_k) <= 0) {
- knnList.add(d3, o_j);
- d_k = knnList.getKNNDistance();
- }
- }
- }
- }
- }
- }
-
- /**
* Chooses the best path of the specified subtree for insertion of the given
* object.
*
@@ -328,90 +243,52 @@ public abstract class AbstractMTree<O, D extends Distance<D>, N extends Abstract
N node = getNode(subtree.getLastPathComponent().getEntry());
// leaf
- if(node.isLeaf()) {
+ if (node.isLeaf()) {
return subtree;
}
- D nullDistance = getDistanceFactory().nullDistance();
- List<DistanceEntry<D, E>> candidatesWithoutExtension = new ArrayList<DistanceEntry<D, E>>();
- List<DistanceEntry<D, E>> candidatesWithExtension = new ArrayList<DistanceEntry<D, E>>();
+ DistanceEntry<D, E> bestCandidate;
+ D enlarge; // Track best enlargement - null for no enlargement needed.
+ // Initialize from first:
+ {
+ E entry = node.getEntry(0);
+ D distance = distance(object.getRoutingObjectID(), entry.getRoutingObjectID());
+ bestCandidate = new DistanceEntry<D, E>(entry, distance, 0);
+ if (distance.compareTo(entry.getCoveringRadius()) <= 0) {
+ enlarge = null;
+ } else {
+ enlarge = distance.minus(entry.getCoveringRadius());
+ }
+ }
- for(int i = 0; i < node.getNumEntries(); i++) {
+ // Iterate over remaining
+ for (int i = 1; i < node.getNumEntries(); i++) {
E entry = node.getEntry(i);
D distance = distance(object.getRoutingObjectID(), entry.getRoutingObjectID());
- D enlrg = distance.minus(entry.getCoveringRadius());
- if(enlrg.compareTo(nullDistance) <= 0) {
- candidatesWithoutExtension.add(new DistanceEntry<D, E>(entry, distance, i));
- }
- else {
- candidatesWithExtension.add(new DistanceEntry<D, E>(entry, enlrg, i));
+ if (distance.compareTo(entry.getCoveringRadius()) <= 0) {
+ if (enlarge != null || distance.compareTo(bestCandidate.getDistance()) < 0) {
+ bestCandidate = new DistanceEntry<D, E>(entry, distance, i);
+ enlarge = null;
+ }
+ } else if (enlarge != null) {
+ D enlrg = distance.minus(entry.getCoveringRadius());
+ if (enlrg.compareTo(enlarge) < 0) {
+ bestCandidate = new DistanceEntry<D, E>(entry, distance, i);
+ enlarge = enlrg;
+ }
}
}
- DistanceEntry<D, E> bestCandidate;
- if(!candidatesWithoutExtension.isEmpty()) {
- bestCandidate = Collections.min(candidatesWithoutExtension);
- }
- else {
- Collections.sort(candidatesWithExtension);
- bestCandidate = Collections.min(candidatesWithExtension);
- E entry = bestCandidate.getEntry();
- D cr = entry.getCoveringRadius();
- entry.setCoveringRadius(cr.plus(bestCandidate.getDistance()));
+ // Apply enlargement
+ if (enlarge != null) {
+ bestCandidate.getEntry().setCoveringRadius(enlarge);
}
return choosePath(object, subtree.pathByAddingChild(new TreeIndexPathComponent<E>(bestCandidate.getEntry(), bestCandidate.getIndex())));
}
/**
- * Performs a batch k-nearest neigbor query for a list of query objects.
- *
- * @param node the node reprsenting the subtree on which the query should be
- * performed
- * @param ids the ids of th query objects
- * @param knnLists the knn lists of the query objcets
- *
- * @deprecated Change to use by-object NN lookups instead.
- */
- @Deprecated
- protected final void batchNN(N node, DBIDs ids, Map<DBID, KNNHeap<D>> knnLists) {
- if(node.isLeaf()) {
- for(int i = 0; i < node.getNumEntries(); i++) {
- E p = node.getEntry(i);
- for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
- DBID q = iter.getDBID();
- KNNHeap<D> knns_q = knnLists.get(q);
- D knn_q_maxDist = knns_q.getKNNDistance();
-
- D dist_pq = distanceQuery.distance(p.getRoutingObjectID(), q);
- if(dist_pq.compareTo(knn_q_maxDist) <= 0) {
- knns_q.add(dist_pq, p.getRoutingObjectID());
- }
- }
- }
- }
- else {
- List<DistanceEntry<D, E>> entries = getSortedEntries(node, ids);
- for(DistanceEntry<D, E> distEntry : entries) {
- D minDist = distEntry.getDistance();
- for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
- DBID q = iter.getDBID();
- KNNHeap<D> knns_q = knnLists.get(q);
- D knn_q_maxDist = knns_q.getKNNDistance();
-
- if(minDist.compareTo(knn_q_maxDist) <= 0) {
- E entry = distEntry.getEntry();
- N child = getNode(entry);
- batchNN(child, ids, knnLists);
- break;
- }
- }
- }
- }
- }
-
- /**
* Sorts the entries of the specified node according to their minimum distance
* to the specified object.
*
@@ -422,10 +299,11 @@ public abstract class AbstractMTree<O, D extends Distance<D>, N extends Abstract
protected final List<DistanceEntry<D, E>> getSortedEntries(N node, DBID q) {
List<DistanceEntry<D, E>> result = new ArrayList<DistanceEntry<D, E>>();
- for(int i = 0; i < node.getNumEntries(); i++) {
+ for (int i = 0; i < node.getNumEntries(); i++) {
E entry = node.getEntry(i);
D distance = distance(entry.getRoutingObjectID(), q);
- D minDist = entry.getCoveringRadius().compareTo(distance) > 0 ? getDistanceFactory().nullDistance() : distance.minus(entry.getCoveringRadius());
+ D radius = entry.getCoveringRadius();
+ D minDist = radius.compareTo(distance) > 0 ? getDistanceFactory().nullDistance() : distance.minus(radius);
result.add(new DistanceEntry<D, E>(entry, minDist, i));
}
@@ -445,14 +323,15 @@ public abstract class AbstractMTree<O, D extends Distance<D>, N extends Abstract
protected final List<DistanceEntry<D, E>> getSortedEntries(N node, DBIDs ids) {
List<DistanceEntry<D, E>> result = new ArrayList<DistanceEntry<D, E>>();
- for(int i = 0; i < node.getNumEntries(); i++) {
+ for (int i = 0; i < node.getNumEntries(); i++) {
E entry = node.getEntry(i);
+ D radius = entry.getCoveringRadius();
D minMinDist = getDistanceFactory().infiniteDistance();
for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
- D distance = distanceQuery.distance(entry.getRoutingObjectID(), iter.getDBID());
- D minDist = entry.getCoveringRadius().compareTo(distance) > 0 ? getDistanceFactory().nullDistance() : distance.minus(entry.getCoveringRadius());
- minMinDist = DistanceUtil.max(minMinDist, minDist);
+ D distance = distanceQuery.distance(entry.getRoutingObjectID(), iter);
+ D minDist = radius.compareTo(distance) > 0 ? getDistanceFactory().nullDistance() : distance.minus(radius);
+ minMinDist = DistanceUtil.min(minMinDist, minDist);
}
result.add(new DistanceEntry<D, E>(entry, minMinDist, i));
}
@@ -469,27 +348,13 @@ public abstract class AbstractMTree<O, D extends Distance<D>, N extends Abstract
* @return the distance between the two specified ids
*/
protected final D distance(DBID id1, DBID id2) {
- if(id1 == null || id2 == null) {
+ if (id1 == null || id2 == null) {
return getDistanceFactory().undefinedDistance();
}
return distanceQuery.distance(id1, id2);
}
/**
- * Returns the distance between the given object and the id.
- *
- * @param id1 the first id
- * @param o2 the second object
- * @return the distance between the two specified objects
- */
- protected final D distance(DBID id1, O o2) {
- if(id1 == null) {
- return getDistanceFactory().undefinedDistance();
- }
- return distanceQuery.distance(id1, o2);
- }
-
- /**
* Creates a new directory entry representing the specified node.
*
* @param node the node to be represented by the new entry
@@ -498,7 +363,7 @@ public abstract class AbstractMTree<O, D extends Distance<D>, N extends Abstract
* the routing object of the parent node
* @return the newly created directory entry
*/
- abstract protected E createNewDirectoryEntry(N node, DBID routingObjectID, D parentDistance);
+ protected abstract E createNewDirectoryEntry(N node, DBID routingObjectID, D parentDistance);
/**
* Splits the specified node and returns the split result.
@@ -512,10 +377,9 @@ public abstract class AbstractMTree<O, D extends Distance<D>, N extends Abstract
MTreeSplit<O, D, N, E> split = new MLBDistSplit<O, D, N, E>(node, distanceQuery);
Assignments<D, E> assignments = split.getAssignments();
final N newNode;
- if(node.isLeaf()) {
+ if (node.isLeaf()) {
newNode = createNewLeafNode();
- }
- else {
+ } else {
newNode = createNewDirectoryNode();
}
node.splitTo(newNode, assignments.getFirstAssignments(), assignments.getSecondAssignments());
@@ -524,7 +388,7 @@ public abstract class AbstractMTree<O, D extends Distance<D>, N extends Abstract
writeNode(node);
writeNode(newNode);
- if(getLogger().isDebugging()) {
+ if (getLogger().isDebugging()) {
String msg = "Split Node " + node.getPageID() + " (" + this.getClass() + ")\n" + " newNode " + newNode.getPageID() + "\n" + " firstPromoted " + assignments.getFirstRoutingObject() + "\n" + " firstAssignments(" + node.getPageID() + ") " + assignments.getFirstAssignments() + "\n" + " firstCR " + assignments.getFirstCoveringRadius() + "\n" + " secondPromoted " + assignments.getSecondRoutingObject() + "\n" + " secondAssignments(" + newNode.getPageID() + ") " + assignments.getSecondAssignments() + "\n" + " secondCR " + assignments.getSecondCoveringRadius() + "\n";
getLogger().debugFine(msg);
}
@@ -533,38 +397,12 @@ public abstract class AbstractMTree<O, D extends Distance<D>, N extends Abstract
}
/**
- * Sorts the entries of the specified node according to their minimum distance
- * to the specified objects.
- *
- * @param node the node
- * @param ids the ids of the objects
- * @return a list of the sorted entries
- */
- // FIXME: Duplicate from above?
- /*
- * private List<DistanceEntry<D, E>> getSortedEntries(N node, DBIDs ids) {
- * List<DistanceEntry<D, E>> result = new ArrayList<DistanceEntry<D, E>>();
- *
- * for(int i = 0; i < node.getNumEntries(); i++) { E entry = node.getEntry(i);
- *
- * D minMinDist = distanceFunction.infiniteDistance(); for(DBID q : ids) { D
- * distance = distance(entry.getRoutingObjectID(), q); D minDist =
- * entry.getCoveringRadius().compareTo(distance) > 0 ?
- * distanceFunction.nullDistance() :
- * distance.minus(entry.getCoveringRadius()); if(minDist.compareTo(minMinDist)
- * < 0) { minMinDist = minDist; } } result.add(new DistanceEntry<D, E>(entry,
- * minMinDist, i)); }
- *
- * Collections.sort(result); return result; }
- */
-
- /**
* Adjusts the tree after insertion of some nodes.
*
* @param subtree the subtree to be adjusted
*/
private void adjustTree(IndexTreePath<E> subtree) {
- if(getLogger().isDebugging()) {
+ if (getLogger().isDebugging()) {
getLogger().debugFine("Adjust tree " + subtree + "\n");
}
@@ -573,14 +411,14 @@ public abstract class AbstractMTree<O, D extends Distance<D>, N extends Abstract
N node = getNode(subtree.getLastPathComponent().getEntry());
// overflow in node; split the node
- if(hasOverflow(node)) {
+ if (hasOverflow(node)) {
SplitResult splitResult = split(node);
N splitNode = splitResult.newNode;
Assignments<D, E> assignments = splitResult.split.getAssignments();
// if root was split: create a new root that points the two split
// nodes
- if(isRoot(node)) {
+ if (isRoot(node)) {
// FIXME: stimmen die parentDistance der Kinder in node & splitNode?
IndexTreePath<E> newRootPath = createNewRoot(node, splitNode, assignments.getFirstRoutingObject(), assignments.getSecondRoutingObject());
adjustTree(newRootPath);
@@ -590,7 +428,7 @@ public abstract class AbstractMTree<O, D extends Distance<D>, N extends Abstract
// get the parent and add the new split node
E parentEntry = subtree.getParentPath().getLastPathComponent().getEntry();
N parent = getNode(parentEntry);
- if(getLogger().isDebugging()) {
+ if (getLogger().isDebugging()) {
getLogger().debugFine("parent " + parent);
}
D parentDistance2 = distance(parentEntry.getRoutingObjectID(), assignments.getSecondRoutingObject());
@@ -612,7 +450,7 @@ public abstract class AbstractMTree<O, D extends Distance<D>, N extends Abstract
// no overflow, only adjust parameters of the entry representing the
// node
else {
- if(!isRoot(node)) {
+ if (!isRoot(node)) {
E parentEntry = subtree.getParentPath().getLastPathComponent().getEntry();
N parent = getNode(parentEntry);
int index = subtree.getLastPathComponent().getIndex();
@@ -639,7 +477,7 @@ public abstract class AbstractMTree<O, D extends Distance<D>, N extends Abstract
* otherwise
*/
private boolean hasOverflow(N node) {
- if(node.isLeaf()) {
+ if (node.isLeaf()) {
return node.getNumEntries() == leafCapacity;
}
@@ -665,9 +503,9 @@ public abstract class AbstractMTree<O, D extends Distance<D>, N extends Abstract
// switch the ids
oldRoot.setPageID(root.getPageID());
- if(!oldRoot.isLeaf()) {
+ if (!oldRoot.isLeaf()) {
// FIXME: what is happening here?
- for(int i = 0; i < oldRoot.getNumEntries(); i++) {
+ for (int i = 0; i < oldRoot.getNumEntries(); i++) {
N node = getNode(oldRoot.getEntry(i));
writeNode(node);
}
@@ -691,7 +529,7 @@ public abstract class AbstractMTree<O, D extends Distance<D>, N extends Abstract
writeNode(root);
writeNode(oldRoot);
writeNode(newNode);
- if(getLogger().isDebugging()) {
+ if (getLogger().isDebugging()) {
String msg = "Create new Root: ID=" + root.getPageID();
msg += "\nchild1 " + oldRoot;
msg += "\nchild2 " + newNode;
@@ -707,10 +545,22 @@ public abstract class AbstractMTree<O, D extends Distance<D>, N extends Abstract
* @apiviz.composedOf MTreeSplit
*/
private class SplitResult {
+ /**
+ * Split used
+ */
protected MTreeSplit<O, D, N, E> split;
+ /**
+ * New sibling
+ */
protected N newNode;
+ /**
+ * Constructor.
+ *
+ * @param split Split that was used
+ * @param newNode New sibling
+ */
public SplitResult(MTreeSplit<O, D, N, E> split, N newNode) {
this.split = split;
this.newNode = newNode;
@@ -721,16 +571,13 @@ public abstract class AbstractMTree<O, D extends Distance<D>, N extends Abstract
public List<E> getLeaves() {
List<E> result = new ArrayList<E>();
BreadthFirstEnumeration<N, E> enumeration = new BreadthFirstEnumeration<N, E>(this, getRootPath());
- while(enumeration.hasMoreElements()) {
+ while (enumeration.hasMoreElements()) {
IndexTreePath<E> path = enumeration.nextElement();
E entry = path.getLastPathComponent().getEntry();
- if(entry.isLeafEntry()) {
- // ignore, we are within a leaf!
- }
- else {
+ if (!entry.isLeafEntry()) {
// TODO: any way to skip unnecessary reads?
N node = getNode(entry);
- if(node.isLeaf()) {
+ if (node.isLeaf()) {
result.add(entry);
}
}
@@ -747,8 +594,8 @@ public abstract class AbstractMTree<O, D extends Distance<D>, N extends Abstract
int levels = 0;
N node = getRoot();
- while(!node.isLeaf()) {
- if(node.getNumEntries() > 0) {
+ while (!node.isLeaf()) {
+ if (node.getNumEntries() > 0) {
E entry = node.getEntry(0);
node = getNode(entry);
levels++;
@@ -756,4 +603,4 @@ public abstract class AbstractMTree<O, D extends Distance<D>, N extends Abstract
}
return levels;
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/AbstractMTreeFactory.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/AbstractMTreeFactory.java
index 9ae50bbe..3769a562 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/AbstractMTreeFactory.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/AbstractMTreeFactory.java
@@ -60,7 +60,7 @@ public abstract class AbstractMTreeFactory<O, D extends Distance<D>, N extends A
* {@link de.lmu.ifi.dbs.elki.distance.distancefunction.EuclideanDistanceFunction}
* </p>
*/
- public static final OptionID DISTANCE_FUNCTION_ID = OptionID.getOrCreateOptionID("mtree.distancefunction", "Distance function to determine the distance between database objects.");
+ public static final OptionID DISTANCE_FUNCTION_ID = new OptionID("mtree.distancefunction", "Distance function to determine the distance between database objects.");
/**
* Holds the instance of the distance function specified by
@@ -93,7 +93,7 @@ public abstract class AbstractMTreeFactory<O, D extends Distance<D>, N extends A
*
* @apiviz.exclude
*/
- public static abstract class Parameterizer<O, D extends Distance<D>> extends TreeIndexFactory.Parameterizer<O> {
+ public abstract static class Parameterizer<O, D extends Distance<D>> extends TreeIndexFactory.Parameterizer<O> {
protected DistanceFunction<O, D> distanceFunction = null;
@Override
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/MTreeDirectoryEntry.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/MTreeDirectoryEntry.java
index 0b07c446..3902973f 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/MTreeDirectoryEntry.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/MTreeDirectoryEntry.java
@@ -152,7 +152,7 @@ public class MTreeDirectoryEntry<D extends Distance<D>> extends AbstractDirector
@Override
public void writeExternal(ObjectOutput out) throws IOException {
super.writeExternal(out);
- out.writeInt(routingObjectID.getIntegerID());
+ out.writeInt(DBIDUtil.asInteger(routingObjectID));
out.writeObject(parentDistance);
out.writeObject(coveringRadius);
}
@@ -209,6 +209,6 @@ public class MTreeDirectoryEntry<D extends Distance<D>> extends AbstractDirector
if(parentDistance != null ? !parentDistance.equals(that.parentDistance) : that.parentDistance != null) {
return false;
}
- return !(routingObjectID != null ? !routingObjectID.equals(that.routingObjectID) : that.routingObjectID != null);
+ return !(routingObjectID != null ? !DBIDUtil.equal(routingObjectID, that.routingObjectID) : that.routingObjectID != null);
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/AbstractMkTree.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/AbstractMkTree.java
index 9391a2fa..f3c440b5 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/AbstractMkTree.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/AbstractMkTree.java
@@ -23,16 +23,24 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.util.List;
+import java.util.HashMap;
+import java.util.Map;
+import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
+import de.lmu.ifi.dbs.elki.database.query.knn.KNNQuery;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTree;
import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTreeNode;
import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeEntry;
+import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.query.MTreeQueryUtil;
import de.lmu.ifi.dbs.elki.persistent.PageFile;
/**
@@ -48,6 +56,11 @@ import de.lmu.ifi.dbs.elki.persistent.PageFile;
*/
public abstract class AbstractMkTree<O, D extends Distance<D>, N extends AbstractMTreeNode<O, D, N, E>, E extends MTreeEntry<D>> extends AbstractMTree<O, D, N, E> {
/**
+ * Internal class for performing knn queries
+ */
+ protected KNNQuery<O, D> knnq;
+
+ /**
* Constructor.
*
* @param pagefile Page file
@@ -56,8 +69,9 @@ public abstract class AbstractMkTree<O, D extends Distance<D>, N extends Abstrac
*/
public AbstractMkTree(PageFile<N> pagefile, DistanceQuery<O, D> distanceQuery, DistanceFunction<O, D> distanceFunction) {
super(pagefile, distanceQuery, distanceFunction);
+ this.knnq = MTreeQueryUtil.getKNNQuery(this, distanceQuery);
}
-
+
/**
* Performs a reverse k-nearest neighbor query for the given object ID. The
* query result is in ascending order to the distance to the query object.
@@ -66,5 +80,25 @@ public abstract class AbstractMkTree<O, D extends Distance<D>, N extends Abstrac
* @param k the number of nearest neighbors to be returned
* @return a List of the query results
*/
- public abstract List<DistanceResultPair<D>> reverseKNNQuery(final DBIDRef id, int k);
-} \ No newline at end of file
+ public abstract DistanceDBIDResult<D> reverseKNNQuery(final DBIDRef id, int k);
+
+ /**
+ * Performs a batch k-nearest neighbor query for a list of query objects.
+ *
+ * @param node the node representing the subtree on which the query should be
+ * performed
+ * @param ids the ids of the query objects
+ * @param kmax Maximum k value
+ *
+ * @deprecated Change to use by-object NN lookups instead.
+ */
+ @Deprecated
+ protected final Map<DBID, KNNResult<D>> batchNN(N node, DBIDs ids, int kmax) {
+ Map<DBID, KNNResult<D>> res = new HashMap<DBID, KNNResult<D>>(ids.size());
+ for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
+ DBID id = DBIDUtil.deref(iter);
+ res.put(id, knnq.getKNNForDBID(id, kmax));
+ }
+ return res;
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/AbstractMkTreeUnified.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/AbstractMkTreeUnified.java
index 88013000..34507479 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/AbstractMkTreeUnified.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/AbstractMkTreeUnified.java
@@ -23,7 +23,6 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -32,12 +31,12 @@ import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.ModifiableDBIDs;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
import de.lmu.ifi.dbs.elki.index.tree.TreeIndexHeader;
import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTreeNode;
import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeEntry;
import de.lmu.ifi.dbs.elki.persistent.PageFile;
-import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.KNNHeap;
/**
* Abstract class for all M-Tree variants supporting processing of reverse
@@ -82,35 +81,29 @@ public abstract class AbstractMkTreeUnified<O, D extends Distance<D>, N extends
@Override
public void insertAll(List<E> entries) {
- if(entries.size() <= 0) {
+ if (entries.size() <= 0) {
return;
}
- if(!initialized) {
+ if (!initialized) {
initialize(entries.get(0));
}
- Map<DBID, KNNHeap<D>> knnLists = new HashMap<DBID, KNNHeap<D>>();
ModifiableDBIDs ids = DBIDUtil.newArray(entries.size());
// insert sequentially
- for(E entry : entries) {
- // create knnList for the object
- final DBID id = entry.getRoutingObjectID();
-
- ids.add(id);
- knnLists.put(id, new KNNHeap<D>(k_max, getDistanceFactory().infiniteDistance()));
-
+ for (E entry : entries) {
+ ids.add(entry.getRoutingObjectID());
// insert the object
super.insert(entry, false);
}
// do batch nn
- batchNN(getRoot(), ids, knnLists);
+ Map<DBID, KNNResult<D>> knnLists = batchNN(getRoot(), ids, k_max);
// adjust the knn distances
kNNdistanceAdjustment(getRootEntry(), knnLists);
- if(extraIntegrityChecks) {
+ if (EXTRA_INTEGRITY_CHECKS) {
getRoot().integrityCheck(this, getRootEntry());
}
}
@@ -121,7 +114,7 @@ public abstract class AbstractMkTreeUnified<O, D extends Distance<D>, N extends
* @param entry the root entry of the current subtree
* @param knnLists a map of knn lists for each leaf entry
*/
- protected abstract void kNNdistanceAdjustment(E entry, Map<DBID, KNNHeap<D>> knnLists);
+ protected abstract void kNNdistanceAdjustment(E entry, Map<DBID, KNNResult<D>> knnLists);
/**
* Get the value of k_max.
@@ -131,4 +124,4 @@ public abstract class AbstractMkTreeUnified<O, D extends Distance<D>, N extends
public int getKmax() {
return k_max;
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/AbstractMkTreeUnifiedFactory.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/AbstractMkTreeUnifiedFactory.java
index 51df5077..9570995d 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/AbstractMkTreeUnifiedFactory.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/AbstractMkTreeUnifiedFactory.java
@@ -55,7 +55,7 @@ public abstract class AbstractMkTreeUnifiedFactory<O, D extends Distance<D>, N e
* Key: {@code -mktree.kmax}
* </p>
*/
- public static final OptionID K_MAX_ID = OptionID.getOrCreateOptionID("mktree.kmax", "Specifies the maximal number k of reverse k nearest neighbors to be supported.");
+ public static final OptionID K_MAX_ID = new OptionID("mktree.kmax", "Specifies the maximal number k of reverse k nearest neighbors to be supported.");
/**
* Holds the value of parameter {@link #K_MAX_ID}.
@@ -83,13 +83,14 @@ public abstract class AbstractMkTreeUnifiedFactory<O, D extends Distance<D>, N e
*
* @apiviz.exclude
*/
- public static abstract class Parameterizer<O, D extends Distance<D>> extends AbstractMTreeFactory.Parameterizer<O, D> {
+ public abstract static class Parameterizer<O, D extends Distance<D>> extends AbstractMTreeFactory.Parameterizer<O, D> {
protected int k_max;
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- IntParameter k_maxP = new IntParameter(K_MAX_ID, new GreaterConstraint(0));
+ IntParameter k_maxP = new IntParameter(K_MAX_ID);
+ k_maxP.addConstraint(new GreaterConstraint(0));
if (config.grab(k_maxP)) {
k_max = k_maxP.getValue();
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/MkAppTree.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/MkAppTree.java
index 0f15a4a9..51d59e73 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/MkAppTree.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/MkAppTree.java
@@ -24,11 +24,8 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.mkapp;
*/
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.Map.Entry;
import de.lmu.ifi.dbs.elki.database.ids.ArrayModifiableDBIDs;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
@@ -37,10 +34,12 @@ import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.ids.ModifiableDBIDs;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
-import de.lmu.ifi.dbs.elki.database.query.GenericDistanceResultPair;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResultIter;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.GenericDistanceDBIDList;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance;
import de.lmu.ifi.dbs.elki.index.tree.LeafEntry;
import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.AbstractMkTree;
@@ -49,8 +48,6 @@ import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.math.statistics.PolynomialRegression;
import de.lmu.ifi.dbs.elki.persistent.PageFile;
import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.Heap;
-import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.KNNHeap;
-import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.KNNList;
import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.UpdatableHeap;
/**
@@ -69,7 +66,7 @@ public class MkAppTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(MkAppTree.class);
+ private static final Logging LOG = Logging.getLogger(MkAppTree.class);
/**
* Parameter k.
@@ -130,41 +127,30 @@ public class MkAppTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree
return;
}
- if(logger.isDebugging()) {
- logger.debugFine("insert " + entries + "\n");
+ if(LOG.isDebugging()) {
+ LOG.debugFine("insert " + entries + "\n");
}
if(!initialized) {
initialize(entries.get(0));
}
- Map<DBID, KNNHeap<D>> knnHeaps = new HashMap<DBID, KNNHeap<D>>(entries.size());
ModifiableDBIDs ids = DBIDUtil.newArray(entries.size());
// insert
for(MkAppEntry<D> entry : entries) {
- DBID id = entry.getRoutingObjectID();
- // create knnList for the object
- knnHeaps.put(id, new KNNHeap<D>(k_max + 1, getDistanceQuery().infiniteDistance()));
-
- ids.add(id);
+ ids.add(entry.getRoutingObjectID());
// insert the object
super.insert(entry, false);
}
// do batch nn
- batchNN(getRoot(), ids, knnHeaps);
-
- // finish KNN lists (sort them completely)
- Map<DBID, KNNList<D>> knnLists = new HashMap<DBID, KNNList<D>>();
- for(Entry<DBID, KNNHeap<D>> ent : knnHeaps.entrySet()) {
- knnLists.put(ent.getKey(), ent.getValue().toKNNList());
- }
-
+ Map<DBID, KNNResult<D>> knnLists = batchNN(getRoot(), ids, k_max + 1);
+
// adjust the knn distances
adjustApproximatedKNNDistances(getRootEntry(), knnLists);
- if(extraIntegrityChecks) {
+ if(EXTRA_INTEGRITY_CHECKS) {
getRoot().integrityCheck(this, getRootEntry());
}
}
@@ -178,9 +164,55 @@ public class MkAppTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree
* @return a List of the query results
*/
@Override
- public List<DistanceResultPair<D>> reverseKNNQuery(DBIDRef id, int k) {
- List<DistanceResultPair<D>> result = doReverseKNNQuery(k, id);
- Collections.sort(result);
+ public DistanceDBIDResult<D> reverseKNNQuery(DBIDRef id, int k) {
+ GenericDistanceDBIDList<D> result = new GenericDistanceDBIDList<D>();
+ final Heap<GenericMTreeDistanceSearchCandidate<D>> pq = new UpdatableHeap<GenericMTreeDistanceSearchCandidate<D>>();
+
+ // push root
+ pq.add(new GenericMTreeDistanceSearchCandidate<D>(getDistanceQuery().nullDistance(), getRootID(), null, null));
+
+ // search in tree
+ while(!pq.isEmpty()) {
+ GenericMTreeDistanceSearchCandidate<D> pqNode = pq.poll();
+ // FIXME: cache the distance to the routing object in the queue node!
+
+ MkAppTreeNode<O, D> node = getNode(pqNode.nodeID);
+
+ // directory node
+ if(!node.isLeaf()) {
+ for(int i = 0; i < node.getNumEntries(); i++) {
+ MkAppEntry<D> entry = node.getEntry(i);
+ D distance = getDistanceQuery().distance(entry.getRoutingObjectID(), id);
+ D minDist = entry.getCoveringRadius().compareTo(distance) > 0 ? getDistanceQuery().nullDistance() : distance.minus(entry.getCoveringRadius());
+
+ double approxValue = log ? Math.exp(entry.approximatedValueAt(k)) : entry.approximatedValueAt(k);
+ if(approxValue < 0) {
+ approxValue = 0;
+ }
+ D approximatedKnnDist = getDistanceQuery().getDistanceFactory().fromDouble(approxValue);
+
+ if(minDist.compareTo(approximatedKnnDist) <= 0) {
+ pq.add(new GenericMTreeDistanceSearchCandidate<D>(minDist, getPageID(entry), entry.getRoutingObjectID(), null));
+ }
+ }
+ }
+ // data node
+ else {
+ for(int i = 0; i < node.getNumEntries(); i++) {
+ MkAppLeafEntry<D> entry = (MkAppLeafEntry<D>) node.getEntry(i);
+ D distance = getDistanceQuery().distance(entry.getRoutingObjectID(), id);
+ double approxValue = log ? StrictMath.exp(entry.approximatedValueAt(k)) : entry.approximatedValueAt(k);
+ if(approxValue < 0) {
+ approxValue = 0;
+ }
+ D approximatedKnnDist = getDistanceQuery().getDistanceFactory().fromDouble(approxValue);
+
+ if(distance.compareTo(approximatedKnnDist) <= 0) {
+ result.add(distance, entry.getRoutingObjectID());
+ }
+ }
+ }
+ }
return result;
}
@@ -215,7 +247,7 @@ public class MkAppTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree
}
if(dirCapacity < 10) {
- logger.warning("Page size is choosen too small! Maximum number of entries " + "in a directory node = " + (dirCapacity - 1));
+ LOG.warning("Page size is choosen too small! Maximum number of entries " + "in a directory node = " + (dirCapacity - 1));
}
// leafCapacity = (file.getPageSize() - overhead) / (objectID +
@@ -228,90 +260,31 @@ public class MkAppTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree
}
if(leafCapacity < 10) {
- logger.warning("Page size is choosen too small! Maximum number of entries " + "in a leaf node = " + (leafCapacity - 1));
+ LOG.warning("Page size is choosen too small! Maximum number of entries " + "in a leaf node = " + (leafCapacity - 1));
}
initialized = true;
- if(logger.isVerbose()) {
- logger.verbose("Directory Capacity: " + (dirCapacity - 1) + "\nLeaf Capacity: " + (leafCapacity - 1));
- }
- }
-
- /**
- * Performs a reverse knn query.
- *
- * @param k the parameter k of the rknn query
- * @param q the id of the query object
- * @return the result of the reverse knn query
- */
- private List<DistanceResultPair<D>> doReverseKNNQuery(int k, DBIDRef q) {
- List<DistanceResultPair<D>> result = new ArrayList<DistanceResultPair<D>>();
- final Heap<GenericMTreeDistanceSearchCandidate<D>> pq = new UpdatableHeap<GenericMTreeDistanceSearchCandidate<D>>();
-
- // push root
- pq.add(new GenericMTreeDistanceSearchCandidate<D>(getDistanceQuery().nullDistance(), getRootID(), null));
-
- // search in tree
- while(!pq.isEmpty()) {
- GenericMTreeDistanceSearchCandidate<D> pqNode = pq.poll();
-
- MkAppTreeNode<O, D> node = getNode(pqNode.nodeID);
-
- // directory node
- if(!node.isLeaf()) {
- for(int i = 0; i < node.getNumEntries(); i++) {
- MkAppEntry<D> entry = node.getEntry(i);
- D distance = getDistanceQuery().distance(entry.getRoutingObjectID(), q);
- D minDist = entry.getCoveringRadius().compareTo(distance) > 0 ? getDistanceQuery().nullDistance() : distance.minus(entry.getCoveringRadius());
-
- double approxValue = log ? Math.exp(entry.approximatedValueAt(k)) : entry.approximatedValueAt(k);
- if(approxValue < 0) {
- approxValue = 0;
- }
- D approximatedKnnDist = getDistanceQuery().getDistanceFactory().parseString(Double.toString(approxValue));
-
- if(minDist.compareTo(approximatedKnnDist) <= 0) {
- pq.add(new GenericMTreeDistanceSearchCandidate<D>(minDist, getPageID(entry), entry.getRoutingObjectID()));
- }
- }
- }
- // data node
- else {
- for(int i = 0; i < node.getNumEntries(); i++) {
- MkAppLeafEntry<D> entry = (MkAppLeafEntry<D>) node.getEntry(i);
- D distance = getDistanceQuery().distance(entry.getRoutingObjectID(), q);
- double approxValue = log ? StrictMath.exp(entry.approximatedValueAt(k)) : entry.approximatedValueAt(k);
- if(approxValue < 0) {
- approxValue = 0;
- }
- D approximatedKnnDist = getDistanceQuery().getDistanceFactory().parseString(Double.toString(approxValue));
-
- if(distance.compareTo(approximatedKnnDist) <= 0) {
- result.add(new GenericDistanceResultPair<D>(distance, entry.getRoutingObjectID()));
- }
- }
- }
+ if(LOG.isVerbose()) {
+ LOG.verbose("Directory Capacity: " + (dirCapacity - 1) + "\nLeaf Capacity: " + (leafCapacity - 1));
}
- return result;
}
- private List<D> getMeanKNNList(DBIDs ids, Map<DBID, KNNList<D>> knnLists) {
+ private List<D> getMeanKNNList(DBIDs ids, Map<DBID, KNNResult<D>> knnLists) {
double[] means = new double[k_max];
- for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
- DBID id = iter.getDBID();
- KNNList<D> knns = knnLists.get(id);
- List<D> knnDists = knns.asDistanceList();
- for(int k = 0; k < k_max; k++) {
- D knnDist = knnDists.get(k);
- means[k] += knnDist.doubleValue();
+ for(DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
+ DBID id = DBIDUtil.deref(iter);
+ KNNResult<D> knns = knnLists.get(id);
+ int k = 0;
+ for(DistanceDBIDResultIter<D> it = knns.iter(); k < k_max && it.valid(); it.advance(), k++) {
+ means[k] += it.getDistance().doubleValue();
}
}
List<D> result = new ArrayList<D>();
for(int k = 0; k < k_max; k++) {
means[k] /= ids.size();
- result.add(getDistanceQuery().getDistanceFactory().parseString(Double.toString(means[k])));
+ result.add(getDistanceQuery().getDistanceFactory().fromDouble(means[k]));
}
return result;
@@ -323,7 +296,7 @@ public class MkAppTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree
* @param entry the root entry of the current subtree
* @param knnLists a map of knn lists for each leaf entry
*/
- private void adjustApproximatedKNNDistances(MkAppEntry<D> entry, Map<DBID, KNNList<D>> knnLists) {
+ private void adjustApproximatedKNNDistances(MkAppEntry<D> entry, Map<DBID, KNNResult<D>> knnLists) {
MkAppTreeNode<O, D> node = getNode(entry);
if(node.isLeaf()) {
@@ -378,7 +351,7 @@ public class MkAppTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree
* @return the polynomial approximation of the specified knn-distances.
*/
private PolynomialApproximation approximateKnnDistances(List<D> knnDistances) {
- StringBuffer msg = new StringBuffer();
+ StringBuilder msg = new StringBuilder();
// count the zero distances (necessary of log-log space is used)
int k_0 = 0;
@@ -411,9 +384,9 @@ public class MkAppTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree
PolynomialRegression regression = new PolynomialRegression(y, x, p);
PolynomialApproximation approximation = new PolynomialApproximation(regression.getEstimatedCoefficients().getArrayCopy());
- if(logger.isDebugging()) {
+ if(LOG.isDebugging()) {
msg.append("approximation ").append(approximation);
- logger.debugFine(msg.toString());
+ LOG.debugFine(msg.toString());
}
return approximation;
@@ -464,6 +437,6 @@ public class MkAppTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/MkAppTreeFactory.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/MkAppTreeFactory.java
index 233129b4..c0f12895 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/MkAppTreeFactory.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/MkAppTreeFactory.java
@@ -50,17 +50,17 @@ public class MkAppTreeFactory<O, D extends NumberDistance<D, ?>> extends Abstrac
/**
* Parameter for nolog
*/
- public static final OptionID NOLOG_ID = OptionID.getOrCreateOptionID("mkapp.nolog", "Flag to indicate that the approximation is done in the ''normal'' space instead of the log-log space (which is default).");
+ public static final OptionID NOLOG_ID = new OptionID("mkapp.nolog", "Flag to indicate that the approximation is done in the ''normal'' space instead of the log-log space (which is default).");
/**
* Parameter for k
*/
- public static final OptionID K_ID = OptionID.getOrCreateOptionID("mkapp.k", "positive integer specifying the maximum number k of reverse k nearest neighbors to be supported.");
+ public static final OptionID K_ID = new OptionID("mkapp.k", "positive integer specifying the maximum number k of reverse k nearest neighbors to be supported.");
/**
* Parameter for p
*/
- public static final OptionID P_ID = OptionID.getOrCreateOptionID("mkapp.p", "positive integer specifying the order of the polynomial approximation.");
+ public static final OptionID P_ID = new OptionID("mkapp.p", "positive integer specifying the order of the polynomial approximation.");
/**
* Parameter k.
@@ -131,19 +131,21 @@ public class MkAppTreeFactory<O, D extends NumberDistance<D, ?>> extends Abstrac
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- IntParameter K_PARAM = new IntParameter(K_ID, new GreaterConstraint(0));
- if(config.grab(K_PARAM)) {
- k_max = K_PARAM.getValue();
+ IntParameter kP = new IntParameter(K_ID);
+ kP.addConstraint(new GreaterConstraint(0));
+ if(config.grab(kP)) {
+ k_max = kP.getValue();
}
- IntParameter P_PARAM = new IntParameter(P_ID, new GreaterConstraint(0));
- if(config.grab(P_PARAM)) {
- p = P_PARAM.getValue();
+ IntParameter pP = new IntParameter(P_ID);
+ pP.addConstraint(new GreaterConstraint(0));
+ if(config.grab(pP)) {
+ p = pP.getValue();
}
- Flag NOLOG_FLAG = new Flag(NOLOG_ID);
- if(config.grab(NOLOG_FLAG)) {
- log = !NOLOG_FLAG.getValue();
+ Flag nologF = new Flag(NOLOG_ID);
+ if(config.grab(nologF)) {
+ log = !nologF.getValue();
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/MkAppTreeIndex.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/MkAppTreeIndex.java
index 90c31676..9776de63 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/MkAppTreeIndex.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/MkAppTreeIndex.java
@@ -28,6 +28,8 @@ import java.util.List;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.query.DatabaseQuery;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
@@ -43,8 +45,7 @@ import de.lmu.ifi.dbs.elki.index.RKNNIndex;
import de.lmu.ifi.dbs.elki.index.RangeIndex;
import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTree;
import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.AbstractMkTree;
-import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.query.MetricalIndexKNNQuery;
-import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.query.MetricalIndexRangeQuery;
+import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.query.MTreeQueryUtil;
import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.query.MkTreeRKNNQuery;
import de.lmu.ifi.dbs.elki.persistent.PageFile;
import de.lmu.ifi.dbs.elki.utilities.exceptions.ExceptionMessages;
@@ -93,7 +94,7 @@ public class MkAppTreeIndex<O, D extends NumberDistance<D, ?>> extends MkAppTree
}
@Override
- public void insert(DBID id) {
+ public void insert(DBIDRef id) {
throw new UnsupportedOperationException("Insertion of single objects is not supported!");
}
@@ -101,7 +102,7 @@ public class MkAppTreeIndex<O, D extends NumberDistance<D, ?>> extends MkAppTree
public void insertAll(DBIDs ids) {
List<MkAppEntry<D>> objs = new ArrayList<MkAppEntry<D>>(ids.size());
for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
- DBID id = iter.getDBID();
+ DBID id = DBIDUtil.deref(iter);
final O object = relation.get(id);
objs.add(createNewLeafEntry(id, object, getDistanceFactory().undefinedDistance()));
}
@@ -116,7 +117,7 @@ public class MkAppTreeIndex<O, D extends NumberDistance<D, ?>> extends MkAppTree
* implemented yet.
*/
@Override
- public final boolean delete(DBID id) {
+ public final boolean delete(DBIDRef id) {
throw new UnsupportedOperationException(ExceptionMessages.UNSUPPORTED_NOT_YET);
}
@@ -154,7 +155,7 @@ public class MkAppTreeIndex<O, D extends NumberDistance<D, ?>> extends MkAppTree
}
AbstractMTree<O, S, ?, ?> idx = (AbstractMTree<O, S, ?, ?>) this;
DistanceQuery<O, S> dq = distanceFunction.instantiate(relation);
- return new MetricalIndexKNNQuery<O, S>(idx, dq);
+ return MTreeQueryUtil.getKNNQuery(idx, dq, hints);
}
@SuppressWarnings("unchecked")
@@ -179,7 +180,7 @@ public class MkAppTreeIndex<O, D extends NumberDistance<D, ?>> extends MkAppTree
}
AbstractMTree<O, S, ?, ?> idx = (AbstractMTree<O, S, ?, ?>) this;
DistanceQuery<O, S> dq = distanceFunction.instantiate(relation);
- return new MetricalIndexRangeQuery<O, S>(idx, dq);
+ return MTreeQueryUtil.getRangeQuery(idx, dq, hints);
}
@SuppressWarnings("unchecked")
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/MkAppTreeNode.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/MkAppTreeNode.java
index bc6a83a9..c730eb4f 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/MkAppTreeNode.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/MkAppTreeNode.java
@@ -91,7 +91,7 @@ class MkAppTreeNode<O, D extends NumberDistance<D, ?>> extends AbstractMTreeNode
}
if(LoggingConfiguration.DEBUG) {
- StringBuffer msg = new StringBuffer();
+ StringBuilder msg = new StringBuilder();
msg.append("b " + FormatUtil.format(b, 4));
Logger.getLogger(this.getClass().getName()).fine(msg.toString());
}
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/ConvexHull.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/ConvexHull.java
index 7cb5a16b..6787aa9c 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/ConvexHull.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/ConvexHull.java
@@ -112,7 +112,7 @@ public class ConvexHull {
* should be computed
*/
private void determineLowerAndUpperHull(double[] x, double[] y) {
- StringBuffer msg = new StringBuffer();
+ StringBuilder msg = new StringBuilder();
// first point is always in lowerHull and upperHull
lowerHull[0] = 0;
l = 1;
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/MkCoPTree.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/MkCoPTree.java
index 26ae17db..562f7f4a 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/MkCoPTree.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/MkCoPTree.java
@@ -24,21 +24,20 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.mkcop;
*/
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.Map.Entry;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.ModifiableDBIDs;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
-import de.lmu.ifi.dbs.elki.database.query.GenericDistanceResultPair;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResultIter;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.GenericDistanceDBIDList;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance;
import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.AbstractMkTree;
import de.lmu.ifi.dbs.elki.index.tree.query.GenericMTreeDistanceSearchCandidate;
@@ -47,9 +46,6 @@ import de.lmu.ifi.dbs.elki.persistent.PageFile;
import de.lmu.ifi.dbs.elki.utilities.FormatUtil;
import de.lmu.ifi.dbs.elki.utilities.QueryStatistic;
import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.Heap;
-import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.KNNHeap;
-import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.KNNList;
-import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.UpdatableHeap;
/**
* MkCopTree is a metrical index structure based on the concepts of the M-Tree
@@ -68,7 +64,7 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(MkCoPTree.class);
+ private static final Logging LOG = Logging.getLogger(MkCoPTree.class);
/**
* Parameter k.
@@ -125,41 +121,30 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree
return;
}
- if(logger.isDebugging()) {
- logger.debugFine("insert " + entries + "\n");
+ if(LOG.isDebugging()) {
+ LOG.debugFine("insert " + entries + "\n");
}
if(!initialized) {
initialize(entries.get(0));
}
- Map<DBID, KNNHeap<D>> knnHeaps = new HashMap<DBID, KNNHeap<D>>(entries.size());
ModifiableDBIDs ids = DBIDUtil.newArray(entries.size());
// insert
for(MkCoPEntry<D> entry : entries) {
- DBID id = entry.getRoutingObjectID();
- // create knnList for the object
- knnHeaps.put(id, new KNNHeap<D>(k_max + 1, getDistanceQuery().infiniteDistance()));
-
- ids.add(id);
+ ids.add(entry.getRoutingObjectID());
// insert the object
super.insert(entry, false);
}
- // do batch nn
- batchNN(getRoot(), ids, knnHeaps);
-
- // finish KNN lists (sort them completely)
- Map<DBID, KNNList<D>> knnLists = new HashMap<DBID, KNNList<D>>();
- for(Entry<DBID, KNNHeap<D>> ent : knnHeaps.entrySet()) {
- knnLists.put(ent.getKey(), ent.getValue().toKNNList());
- }
+ // perform nearest neighbor queries
+ Map<DBID, KNNResult<D>> knnLists = batchNN(getRoot(), ids, k_max);
// adjust the knn distances
adjustApproximatedKNNDistances(getRootEntry(), knnLists);
- if(extraIntegrityChecks) {
+ if(EXTRA_INTEGRITY_CHECKS) {
getRoot().integrityCheck(this, getRootEntry());
}
}
@@ -173,38 +158,35 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree
* @return a List of the query results
*/
@Override
- public List<DistanceResultPair<D>> reverseKNNQuery(DBIDRef id, int k) {
+ public DistanceDBIDResult<D> reverseKNNQuery(DBIDRef id, int k) {
if(k > this.k_max) {
throw new IllegalArgumentException("Parameter k has to be less or equal than " + "parameter kmax of the MCop-Tree!");
}
- List<DistanceResultPair<D>> result = new ArrayList<DistanceResultPair<D>>();
+ GenericDistanceDBIDList<D> result = new GenericDistanceDBIDList<D>();
ModifiableDBIDs candidates = DBIDUtil.newArray();
doReverseKNNQuery(k, id, result, candidates);
// refinement of candidates
- Map<DBID, KNNHeap<D>> knnLists = new HashMap<DBID, KNNHeap<D>>();
- for (DBIDIter iter = candidates.iter(); iter.valid(); iter.advance()) {
- knnLists.put(iter.getDBID(), new KNNHeap<D>(k, getDistanceQuery().infiniteDistance()));
- }
- batchNN(getRoot(), candidates, knnLists);
+ Map<DBID, KNNResult<D>> knnLists = batchNN(getRoot(), candidates, k);
- Collections.sort(result);
+ result.sort();
// Collections.sort(candidates);
rkNNStatistics.addCandidates(candidates.size());
rkNNStatistics.addTrueHits(result.size());
- for (DBIDIter iter = candidates.iter(); iter.valid(); iter.advance()) {
- DBID cid = iter.getDBID();
- for(DistanceResultPair<D> qr : knnLists.get(id)) {
- if(qr.getDBID().equals(id)) {
- result.add(new GenericDistanceResultPair<D>(qr.getDistance(), cid));
+ for(DBIDIter iter = candidates.iter(); iter.valid(); iter.advance()) {
+ DBID cid = DBIDUtil.deref(iter);
+ KNNResult<D> cands = knnLists.get(cid);
+ for (DistanceDBIDResultIter<D> iter2 = cands.iter(); iter2.valid(); iter2.advance()) {
+ if(DBIDUtil.equal(id, iter2)) {
+ result.add(iter2.getDistance(), cid);
break;
}
}
}
- Collections.sort(result);
+ result.sort();
rkNNStatistics.addResults(result.size());
return result;
@@ -257,10 +239,11 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree
}
if(dirCapacity < 10) {
- logger.warning("Page size is choosen too small! Maximum number of entries " + "in a directory node = " + (dirCapacity - 1));
+ LOG.warning("Page size is choosen too small! Maximum number of entries " + "in a directory node = " + (dirCapacity - 1));
}
- // leafCapacity = (file.getPageSize() - overhead) / (objectID + parentDistance +
+ // leafCapacity = (file.getPageSize() - overhead) / (objectID +
+ // parentDistance +
// consApprox + progrApprox) + 1
leafCapacity = (int) (getPageSize() - overhead) / (4 + distanceSize + 2 * 10) + 1;
@@ -269,13 +252,13 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree
}
if(leafCapacity < 10) {
- logger.warning("Page size is choosen too small! Maximum number of entries " + "in a leaf node = " + (leafCapacity - 1));
+ LOG.warning("Page size is choosen too small! Maximum number of entries " + "in a leaf node = " + (leafCapacity - 1));
}
initialized = true;
- if(logger.isVerbose()) {
- logger.verbose("Directory Capacity: " + (dirCapacity - 1) + "\nLeaf Capacity: " + (leafCapacity - 1));
+ if(LOG.isVerbose()) {
+ LOG.verbose("Directory Capacity: " + (dirCapacity - 1) + "\nLeaf Capacity: " + (leafCapacity - 1));
}
}
@@ -288,15 +271,16 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree
* @param candidates holds possible candidates for the result (they need a
* refinement)
*/
- private void doReverseKNNQuery(int k, DBIDRef q, List<DistanceResultPair<D>> result, ModifiableDBIDs candidates) {
- final Heap<GenericMTreeDistanceSearchCandidate<D>> pq = new UpdatableHeap<GenericMTreeDistanceSearchCandidate<D>>();
+ private void doReverseKNNQuery(int k, DBIDRef q, GenericDistanceDBIDList<D> result, ModifiableDBIDs candidates) {
+ final Heap<GenericMTreeDistanceSearchCandidate<D>> pq = new Heap<GenericMTreeDistanceSearchCandidate<D>>();
// push root
- pq.add(new GenericMTreeDistanceSearchCandidate<D>(getDistanceQuery().nullDistance(), getRootID(), null));
+ pq.add(new GenericMTreeDistanceSearchCandidate<D>(getDistanceQuery().nullDistance(), getRootID(), null, null));
// search in tree
while(!pq.isEmpty()) {
GenericMTreeDistanceSearchCandidate<D> pqNode = pq.poll();
+ // FIXME: cache the distance to the routing object in the queue node!
MkCoPTreeNode<O, D> node = getNode(pqNode.nodeID);
@@ -309,7 +293,7 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree
D approximatedKnnDist_cons = entry.approximateConservativeKnnDistance(k, getDistanceQuery());
if(minDist.compareTo(approximatedKnnDist_cons) <= 0) {
- pq.add(new GenericMTreeDistanceSearchCandidate<D>(minDist, getPageID(entry), entry.getRoutingObjectID()));
+ pq.add(new GenericMTreeDistanceSearchCandidate<D>(minDist, getPageID(entry), entry.getRoutingObjectID(), null));
}
}
}
@@ -321,7 +305,7 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree
D approximatedKnnDist_prog = entry.approximateProgressiveKnnDistance(k, getDistanceQuery());
if(distance.compareTo(approximatedKnnDist_prog) <= 0) {
- result.add(new GenericDistanceResultPair<D>(distance, entry.getRoutingObjectID()));
+ result.add(distance, entry.getRoutingObjectID());
}
else {
D approximatedKnnDist_cons = entry.approximateConservativeKnnDistance(k, getDistanceQuery());
@@ -341,7 +325,7 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree
* @param entry the root entry of the current subtree
* @param knnLists a map of knn lists for each leaf entry
*/
- private void adjustApproximatedKNNDistances(MkCoPEntry<D> entry, Map<DBID, KNNList<D>> knnLists) {
+ private void adjustApproximatedKNNDistances(MkCoPEntry<D> entry, Map<DBID, KNNResult<D>> knnLists) {
MkCoPTreeNode<O, D> node = getNode(entry);
if(node.isLeaf()) {
@@ -393,10 +377,10 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree
* @param knnDistances TODO: Spezialbehandlung fuer identische Punkte in DB
* (insbes. Distanz 0)
*/
- private void approximateKnnDistances(MkCoPLeafEntry<D> entry, KNNList<D> knnDistances) {
- StringBuffer msg = new StringBuffer();
- if(logger.isDebugging()) {
- msg.append("\nknnDistances " + knnDistances);
+ private void approximateKnnDistances(MkCoPLeafEntry<D> entry, KNNResult<D> knnDistances) {
+ StringBuilder msg = LOG.isDebugging() ? new StringBuilder() : null;
+ if(msg != null) {
+ msg.append("\nknnDistances ").append(knnDistances);
}
// count the zero distances
@@ -434,16 +418,16 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree
sum_log_k2 += (log_k[i] * log_k[i]);
}
- if(logger.isDebugging()) {
- msg.append("\nk_0 " + k_0);
- msg.append("\nk_max " + k_max);
- msg.append("\nlog_k(" + log_k.length + ") " + FormatUtil.format(log_k));
- msg.append("\nsum_log_k " + sum_log_k);
- msg.append("\nsum_log_k^2 " + sum_log_k2);
- msg.append("\nkDists " + knnDistances);
- msg.append("\nlog_kDist(" + log_kDist.length + ") " + FormatUtil.format(log_kDist));
- msg.append("\nsum_log_kDist " + sum_log_kDist);
- msg.append("\nsum_log_k_kDist " + sum_log_k_kDist);
+ if(msg != null) {
+ msg.append("\nk_0 ").append(k_0);
+ msg.append("\nk_max ").append(k_max);
+ msg.append("\nlog_k(").append(log_k.length).append(") ").append(FormatUtil.format(log_k));
+ msg.append("\nsum_log_k ").append(sum_log_k);
+ msg.append("\nsum_log_k^2 ").append(sum_log_k2);
+ msg.append("\nkDists ").append(knnDistances);
+ msg.append("\nlog_kDist(").append(log_kDist.length).append(") ").append(FormatUtil.format(log_kDist));
+ msg.append("\nsum_log_kDist ").append(sum_log_kDist);
+ msg.append("\nsum_log_k_kDist ").append(sum_log_k_kDist);
}
// lower and upper hull
@@ -457,28 +441,28 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree
double err1 = ssqerr(k_0, k_max, log_k, log_kDist, conservative.getM(), conservative.getT());
double err2 = ssqerr(k_0, k_max, log_k, log_kDist, c2.getM(), c2.getT());
- if(logger.isDebugging()) {
- msg.append("err1 " + err1);
- msg.append("err2 " + err2);
+ if(msg != null) {
+ msg.append("err1 ").append(err1);
+ msg.append("err2 ").append(err2);
}
if(err1 > err2 && err1 - err2 > 0.000000001) {
// if (err1 > err2) {
- StringBuffer warning = new StringBuffer();
+ StringBuilder warning = new StringBuilder();
int u = convexHull.getNumberOfPointsInUpperHull();
int[] upperHull = convexHull.getUpperHull();
- warning.append("\nentry " + entry.getRoutingObjectID());
- warning.append("\nlower Hull " + convexHull.getNumberOfPointsInLowerHull() + " " + FormatUtil.format(convexHull.getLowerHull()));
- warning.append("\nupper Hull " + convexHull.getNumberOfPointsInUpperHull() + " " + FormatUtil.format(convexHull.getUpperHull()));
- warning.append("\nerr1 " + err1);
- warning.append("\nerr2 " + err2);
- warning.append("\nconservative1 " + conservative);
- warning.append("\nconservative2 " + c2);
+ warning.append("\nentry ").append(entry.getRoutingObjectID());
+ warning.append("\nlower Hull ").append(convexHull.getNumberOfPointsInLowerHull()).append(" ").append(FormatUtil.format(convexHull.getLowerHull()));
+ warning.append("\nupper Hull ").append(convexHull.getNumberOfPointsInUpperHull()).append(" ").append(FormatUtil.format(convexHull.getUpperHull()));
+ warning.append("\nerr1 ").append(err1);
+ warning.append("\nerr2 ").append(err2);
+ warning.append("\nconservative1 ").append(conservative);
+ warning.append("\nconservative2 ").append(c2);
for(int i = 0; i < u; i++) {
- warning.append("\nlog_k[" + upperHull[i] + "] = " + log_k[upperHull[i]]);
- warning.append("\nlog_kDist[" + upperHull[i] + "] = " + log_kDist[upperHull[i]]);
+ warning.append("\nlog_k[").append(upperHull[i]).append("] = ").append(log_k[upperHull[i]]);
+ warning.append("\nlog_kDist[").append(upperHull[i]).append("] = ").append(log_kDist[upperHull[i]]);
}
// warning(warning.toString());
}
@@ -489,10 +473,9 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree
entry.setConservativeKnnDistanceApproximation(conservative);
entry.setProgressiveKnnDistanceApproximation(progressive);
- if(logger.isDebugging()) {
- logger.debugFine(msg.toString());
+ if(msg != null) {
+ LOG.debugFine(msg.toString());
}
-
}
/**
@@ -505,13 +488,13 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree
*/
private ApproximationLine approximateLowerHull(ConvexHull convexHull, double[] log_k, double sum_log_k, double sum_log_k2, double[] log_kDist, double sum_log_kDist, double sum_log_k_kDist) {
- StringBuffer msg = new StringBuffer();
+ StringBuilder msg = new StringBuilder();
int[] lowerHull = convexHull.getLowerHull();
int l = convexHull.getNumberOfPointsInLowerHull();
int k_0 = k_max - lowerHull.length + 1;
// linear search on all line segments on the lower convex hull
- msg.append("lower hull l = " + l + "\n");
+ msg.append("lower hull l = ").append(l).append("\n");
double low_error = Double.MAX_VALUE;
double low_m = 0.0;
double low_t = 0.0;
@@ -520,7 +503,7 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree
double cur_m = (log_kDist[lowerHull[i]] - log_kDist[lowerHull[i - 1]]) / (log_k[lowerHull[i]] - log_k[lowerHull[i - 1]]);
double cur_t = log_kDist[lowerHull[i]] - cur_m * log_k[lowerHull[i]];
double cur_error = ssqerr(k_0, k_max, log_k, log_kDist, cur_m, cur_t);
- msg.append(" Segment = " + i + " m = " + cur_m + " t = " + cur_t + " lowerror = " + cur_error + "\n");
+ msg.append(" Segment = ").append(i).append(" m = ").append(cur_m).append(" t = ").append(cur_t).append(" lowerror = ").append(cur_error).append("\n");
if(cur_error < low_error) {
low_error = cur_error;
low_m = cur_m;
@@ -557,7 +540,7 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree
}
private ApproximationLine approximateUpperHull(ConvexHull convexHull, double[] log_k, double[] log_kDist) {
- StringBuffer msg = new StringBuffer();
+ StringBuilder msg = new StringBuilder();
int[] upperHull = convexHull.getUpperHull();
int u = convexHull.getNumberOfPointsInUpperHull();
@@ -572,13 +555,13 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree
double current_t = log_kDist[ii] - current_m * log_k[ii];
ApproximationLine current_approx = new ApproximationLine(k_0, current_m, current_t);
- if(logger.isDebugging()) {
- msg.append("\nlog_kDist[" + jj + "] " + log_kDist[jj]);
- msg.append("\nlog_kDist[" + ii + "] " + log_kDist[ii]);
- msg.append("\nlog_k[" + jj + "] " + log_k[jj]);
- msg.append("\nlog_k[" + ii + "] " + log_k[ii]);
- msg.append("\n" + (log_kDist[jj] - log_kDist[ii]));
- msg.append("\ncurrent_approx_" + i + " " + current_approx);
+ if(LOG.isDebugging()) {
+ msg.append("\nlog_kDist[").append(jj).append("] ").append(log_kDist[jj]);
+ msg.append("\nlog_kDist[").append(ii).append("] ").append(log_kDist[ii]);
+ msg.append("\nlog_k[").append(jj).append("] ").append(log_k[jj]);
+ msg.append("\nlog_k[").append(ii).append("] ").append(log_k[ii]);
+ msg.append("\n").append((log_kDist[jj] - log_kDist[ii]));
+ msg.append("\ncurrent_approx_").append(i).append(" ").append(current_approx);
}
boolean ok = true;
@@ -598,15 +581,15 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree
}
}
- if(logger.isDebugging()) {
- msg.append("\nupper Approx " + approx);
- logger.debugFine(msg.toString());
+ if(LOG.isDebugging()) {
+ msg.append("\nupper Approx ").append(approx);
+ LOG.debugFine(msg.toString());
}
return approx;
}
private ApproximationLine approximateUpperHull_PAPER(ConvexHull convexHull, double[] log_k, double sum_log_k, double sum_log_k2, double[] log_kDist, double sum_log_kDist, double sum_log_k_kDist) {
- StringBuffer msg = new StringBuffer();
+ StringBuilder msg = LOG.isDebugging() ? new StringBuilder() : null;
int[] upperHull = convexHull.getUpperHull();
int u = convexHull.getNumberOfPointsInUpperHull();
@@ -624,9 +607,9 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree
double m_a = optimize(k_0, k_max, sum_log_k, sum_log_k2, x_a, y_a, sum_log_k_kDist, sum_log_kDist);
double t_a = y_a - m_a * x_a;
- if(logger.isDebugging()) {
- msg.append("\na=" + a + " m_a=" + m_a + ", t_a=" + t_a);
- msg.append("\n err " + ssqerr(k_0, k_max, log_k, log_kDist, m_a, m_a));
+ if(msg != null) {
+ msg.append("\na=").append(a).append(" m_a=").append(m_a).append(", t_a=").append(t_a);
+ msg.append("\n err ").append(ssqerr(k_0, k_max, log_k, log_kDist, m_a, m_a));
}
double x_p = a == 0 ? Double.NaN : log_k[upperHull[a - 1]];
@@ -639,13 +622,12 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree
if(lessThanPre && lessThanSuc) {
ApproximationLine appr = new ApproximationLine(k_0, m_a, t_a);
- if(logger.isDebugging()) {
- msg.append("\n1 anchor = " + a);
- logger.debugFine(msg.toString());
+ if(msg != null) {
+ msg.append("\n1 anchor = ").append(a);
+ LOG.debugFine(msg.toString());
}
return appr;
}
-
else if(!lessThanPre) {
if(marked.contains(a - 1)) {
m_a = (y_a - y_p) / (x_a - x_p);
@@ -655,14 +637,14 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree
t_a = y_a - m_a * x_a;
ApproximationLine appr = new ApproximationLine(k_0, m_a, t_a);
- if(logger.isDebugging()) {
- msg.append("2 anchor = " + a);
- msg.append(" appr1 " + appr);
- msg.append(" x_a " + x_a + ", y_a " + y_a);
- msg.append(" x_p " + x_p + ", y_p " + y_p);
- msg.append(" a " + a);
- msg.append(" upperHull " + FormatUtil.format(upperHull));
- logger.debugFine(msg.toString());
+ if(msg != null) {
+ msg.append("2 anchor = ").append(a);
+ msg.append(" appr1 ").append(appr);
+ msg.append(" x_a ").append(x_a).append(", y_a ").append(y_a);
+ msg.append(" x_p ").append(x_p).append(", y_p ").append(y_p);
+ msg.append(" a ").append(a);
+ msg.append(" upperHull ").append(FormatUtil.format(upperHull));
+ LOG.debugFine(msg.toString());
}
return appr;
}
@@ -679,10 +661,10 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree
t_a = y_a - m_a * x_a;
ApproximationLine appr = new ApproximationLine(k_0, m_a, t_a);
- if(logger.isDebugging()) {
- msg.append("3 anchor = " + a + " -- " + (a + 1));
- msg.append(" appr2 " + appr);
- logger.debugFine(msg.toString());
+ if(msg != null) {
+ msg.append("3 anchor = ").append(a).append(" -- ").append((a + 1));
+ msg.append(" appr2 ").append(appr);
+ LOG.debugFine(msg.toString());
}
return appr;
}
@@ -698,7 +680,7 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree
@SuppressWarnings("unused")
private ApproximationLine approximateUpperHull_OLD(ConvexHull convexHull, double[] log_k, double sum_log_k, double sum_log_k2, double[] log_kDist, double sum_log_kDist, double sum_log_k_kDist) {
- StringBuffer msg = new StringBuffer();
+ StringBuilder msg = new StringBuilder();
int[] upperHull = convexHull.getUpperHull();
int u = convexHull.getNumberOfPointsInUpperHull();
int k_0 = k_max - upperHull.length + 1;
@@ -794,6 +776,6 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/MkCoPTreeIndex.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/MkCoPTreeIndex.java
index 460e15b7..d3e45f8c 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/MkCoPTreeIndex.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/MkCoPTreeIndex.java
@@ -28,6 +28,8 @@ import java.util.List;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.query.DatabaseQuery;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
@@ -43,8 +45,7 @@ import de.lmu.ifi.dbs.elki.index.RKNNIndex;
import de.lmu.ifi.dbs.elki.index.RangeIndex;
import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTree;
import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.AbstractMkTree;
-import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.query.MetricalIndexKNNQuery;
-import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.query.MetricalIndexRangeQuery;
+import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.query.MTreeQueryUtil;
import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.query.MkTreeRKNNQuery;
import de.lmu.ifi.dbs.elki.persistent.PageFile;
import de.lmu.ifi.dbs.elki.utilities.exceptions.ExceptionMessages;
@@ -92,7 +93,7 @@ public class MkCoPTreeIndex<O, D extends NumberDistance<D, ?>> extends MkCoPTree
}
@Override
- public void insert(DBID id) {
+ public void insert(DBIDRef id) {
throw new UnsupportedOperationException("Insertion of single objects is not supported!");
}
@@ -100,7 +101,7 @@ public class MkCoPTreeIndex<O, D extends NumberDistance<D, ?>> extends MkCoPTree
public void insertAll(DBIDs ids) {
List<MkCoPEntry<D>> objs = new ArrayList<MkCoPEntry<D>>(ids.size());
for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
- DBID id = iter.getDBID();
+ DBID id = DBIDUtil.deref(iter);
final O object = relation.get(id);
objs.add(createNewLeafEntry(id, object, getDistanceFactory().undefinedDistance()));
}
@@ -115,7 +116,7 @@ public class MkCoPTreeIndex<O, D extends NumberDistance<D, ?>> extends MkCoPTree
* implemented yet.
*/
@Override
- public final boolean delete(DBID id) {
+ public final boolean delete(DBIDRef id) {
throw new UnsupportedOperationException(ExceptionMessages.UNSUPPORTED_NOT_YET);
}
@@ -153,7 +154,7 @@ public class MkCoPTreeIndex<O, D extends NumberDistance<D, ?>> extends MkCoPTree
}
AbstractMTree<O, S, ?, ?> idx = (AbstractMTree<O, S, ?, ?>) this;
DistanceQuery<O, S> dq = distanceFunction.instantiate(relation);
- return new MetricalIndexKNNQuery<O, S>(idx, dq);
+ return MTreeQueryUtil.getKNNQuery(idx, dq, hints);
}
@SuppressWarnings("unchecked")
@@ -178,7 +179,7 @@ public class MkCoPTreeIndex<O, D extends NumberDistance<D, ?>> extends MkCoPTree
}
AbstractMTree<O, S, ?, ?> idx = (AbstractMTree<O, S, ?, ?>) this;
DistanceQuery<O, S> dq = distanceFunction.instantiate(relation);
- return new MetricalIndexRangeQuery<O, S>(idx, dq);
+ return MTreeQueryUtil.getRangeQuery(idx, dq, hints);
}
@SuppressWarnings("unchecked")
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/MkCoPTreeNode.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/MkCoPTreeNode.java
index 01fca2f2..f2e4a114 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/MkCoPTreeNode.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/MkCoPTreeNode.java
@@ -23,13 +23,10 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.mkcop;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.util.logging.Logger;
-
import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance;
import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTree;
import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTreeNode;
-import de.lmu.ifi.dbs.elki.logging.LoggingConfiguration;
/**
* Represents a node in an MkCop-Tree.
@@ -99,15 +96,6 @@ class MkCoPTreeNode<O, D extends NumberDistance<D, ?>> extends AbstractMTreeNode
}
}
- if(LoggingConfiguration.DEBUG) {
- StringBuffer msg = new StringBuffer();
- msg.append("k_0 " + k_0);
- msg.append("k_max " + k_max);
- msg.append("y_1 " + y_1);
- msg.append("y_kmax " + y_kmax);
- Logger.getLogger(this.getClass().getName()).fine(msg.toString());
- }
-
// determine m and t
double m = (y_kmax - y_1) / (Math.log(k_max) - Math.log(k_0));
double t = y_1 - m * Math.log(k_0);
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/MkCopTreeFactory.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/MkCopTreeFactory.java
index 8d88ee79..4ac96df3 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/MkCopTreeFactory.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/MkCopTreeFactory.java
@@ -49,7 +49,7 @@ public class MkCopTreeFactory<O, D extends NumberDistance<D, ?>> extends Abstrac
/**
* Parameter for k
*/
- public static final OptionID K_ID = OptionID.getOrCreateOptionID("mkcop.k", "positive integer specifying the maximum number k of reverse k nearest neighbors to be supported.");
+ public static final OptionID K_ID = new OptionID("mkcop.k", "positive integer specifying the maximum number k of reverse k nearest neighbors to be supported.");
/**
* Parameter k.
@@ -93,9 +93,10 @@ public class MkCopTreeFactory<O, D extends NumberDistance<D, ?>> extends Abstrac
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- IntParameter k_maxP = new IntParameter(K_ID, new GreaterConstraint(0));
+ IntParameter k_maxP = new IntParameter(K_ID);
+ k_maxP.addConstraint(new GreaterConstraint(0));
if (config.grab(k_maxP)) {
- k_max = k_maxP.getValue();
+ k_max = k_maxP.intValue();
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkmax/MkMaxTree.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkmax/MkMaxTree.java
index f9f77723..36f9bbf1 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkmax/MkMaxTree.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkmax/MkMaxTree.java
@@ -23,9 +23,6 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.mkmax;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -34,18 +31,22 @@ import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.ModifiableDBIDs;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
-import de.lmu.ifi.dbs.elki.database.query.GenericDistanceResultPair;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.distance.DistanceUtil;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResultIter;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.GenericDistanceDBIDList;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNHeap;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNUtil;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.ModifiableDistanceDBIDResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
import de.lmu.ifi.dbs.elki.index.tree.DistanceEntry;
import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.AbstractMkTreeUnified;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.persistent.PageFile;
import de.lmu.ifi.dbs.elki.utilities.QueryStatistic;
-import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.KNNHeap;
/**
* MkMaxTree is a metrical index structure based on the concepts of the M-Tree
@@ -64,7 +65,7 @@ public class MkMaxTree<O, D extends Distance<D>> extends AbstractMkTreeUnified<O
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(MkMaxTree.class);
+ private static final Logging LOG = Logging.getLogger(MkMaxTree.class);
/**
* Provides some statistics about performed reverse knn-queries.
@@ -90,38 +91,36 @@ public class MkMaxTree<O, D extends Distance<D>> extends AbstractMkTreeUnified<O
* in a second step.
*/
@Override
- public List<DistanceResultPair<D>> reverseKNNQuery(DBIDRef id, int k) {
+ public DistanceDBIDResult<D> reverseKNNQuery(DBIDRef id, int k) {
if(k > this.getKmax()) {
throw new IllegalArgumentException("Parameter k has to be equal or less than " + "parameter k of the MkMax-Tree!");
}
// get the candidates
- List<DistanceResultPair<D>> candidates = new ArrayList<DistanceResultPair<D>>();
+ GenericDistanceDBIDList<D> candidates = new GenericDistanceDBIDList<D>();
doReverseKNNQuery(id, getRoot(), null, candidates);
if(k == this.getKmax()) {
- Collections.sort(candidates);
+ candidates.sort();
rkNNStatistics.addTrueHits(candidates.size());
rkNNStatistics.addResults(candidates.size());
return candidates;
}
// refinement of candidates
- Map<DBID, KNNHeap<D>> knnLists = new HashMap<DBID, KNNHeap<D>>();
- ModifiableDBIDs candidateIDs = DBIDUtil.newArray();
- for(DistanceResultPair<D> candidate : candidates) {
- KNNHeap<D> knns = new KNNHeap<D>(k, getDistanceQuery().infiniteDistance());
- knnLists.put(candidate.getDBID(), knns);
- candidateIDs.add(candidate.getDBID());
+ ModifiableDBIDs candidateIDs = DBIDUtil.newArray(candidates.size());
+ for (DBIDIter candidate = candidates.iter(); candidate.valid(); candidate.advance()) {
+ candidateIDs.add(candidate);
}
- batchNN(getRoot(), candidateIDs, knnLists);
+ Map<DBID, KNNResult<D>> knnLists = batchNN(getRoot(), candidateIDs, k);
- List<DistanceResultPair<D>> result = new ArrayList<DistanceResultPair<D>>();
+ GenericDistanceDBIDList<D> result = new GenericDistanceDBIDList<D>();
for (DBIDIter iter = candidateIDs.iter(); iter.valid(); iter.advance()) {
- DBID cid = iter.getDBID();
- for(DistanceResultPair<D> qr : knnLists.get(cid)) {
- if(id.equals(qr.getDBID())) {
- result.add(new GenericDistanceResultPair<D>(qr.getDistance(), cid));
+ DBID cid = DBIDUtil.deref(iter);
+ KNNResult<D> cands = knnLists.get(cid);
+ for (DistanceDBIDResultIter<D> iter2 = cands.iter(); iter2.valid(); iter2.advance()) {
+ if(DBIDUtil.equal(id, iter2)) {
+ result.add(iter2.getDistance(), cid);
break;
}
}
@@ -129,7 +128,7 @@ public class MkMaxTree<O, D extends Distance<D>> extends AbstractMkTreeUnified<O
rkNNStatistics.addResults(result.size());
rkNNStatistics.addCandidates(candidates.size());
- Collections.sort(result);
+ result.sort();
return result;
}
@@ -155,7 +154,7 @@ public class MkMaxTree<O, D extends Distance<D>> extends AbstractMkTreeUnified<O
*/
@Override
protected void preInsert(MkMaxEntry<D> entry) {
- KNNHeap<D> knns_o = new KNNHeap<D>(getKmax(), getDistanceQuery().infiniteDistance());
+ KNNHeap<D> knns_o = KNNUtil.newHeap(distanceFunction, getKmax());
preInsert(entry, getRootEntry(), knns_o);
}
@@ -163,7 +162,7 @@ public class MkMaxTree<O, D extends Distance<D>> extends AbstractMkTreeUnified<O
* Adjusts the knn distance in the subtree of the specified root entry.
*/
@Override
- protected void kNNdistanceAdjustment(MkMaxEntry<D> entry, Map<DBID, KNNHeap<D>> knnLists) {
+ protected void kNNdistanceAdjustment(MkMaxEntry<D> entry, Map<DBID, KNNResult<D>> knnLists) {
MkMaxTreeNode<O, D> node = getNode(entry);
D knnDist_node = getDistanceQuery().nullDistance();
if(node.isLeaf()) {
@@ -194,14 +193,14 @@ public class MkMaxTree<O, D extends Distance<D>> extends AbstractMkTreeUnified<O
* @param node_entry the entry representing the node
* @param result the list for the query result
*/
- private void doReverseKNNQuery(DBIDRef q, MkMaxTreeNode<O, D> node, MkMaxEntry<D> node_entry, List<DistanceResultPair<D>> result) {
+ private void doReverseKNNQuery(DBIDRef q, MkMaxTreeNode<O, D> node, MkMaxEntry<D> node_entry, ModifiableDistanceDBIDResult<D> result) {
// data node
if(node.isLeaf()) {
for(int i = 0; i < node.getNumEntries(); i++) {
MkMaxEntry<D> entry = node.getEntry(i);
D distance = getDistanceQuery().distance(entry.getRoutingObjectID(), q);
if(distance.compareTo(entry.getKnnDistance()) <= 0) {
- result.add(new GenericDistanceResultPair<D>(distance, entry.getRoutingObjectID()));
+ result.add(distance, entry.getRoutingObjectID());
}
}
}
@@ -231,8 +230,8 @@ public class MkMaxTree<O, D extends Distance<D>> extends AbstractMkTreeUnified<O
* @param knns_q the knns of q
*/
private void preInsert(MkMaxEntry<D> q, MkMaxEntry<D> nodeEntry, KNNHeap<D> knns_q) {
- if(logger.isDebugging()) {
- logger.debugFine("preInsert " + q + " - " + nodeEntry + "\n");
+ if(LOG.isDebugging()) {
+ LOG.debugFine("preInsert " + q + " - " + nodeEntry + "\n");
}
D knnDist_q = knns_q.getKNNDistance();
@@ -248,10 +247,9 @@ public class MkMaxTree<O, D extends Distance<D>> extends AbstractMkTreeUnified<O
// p is nearer to q than the farthest kNN-candidate of q
// ==> p becomes a knn-candidate
if(dist_pq.compareTo(knnDist_q) <= 0) {
- DistanceResultPair<D> knn = new GenericDistanceResultPair<D>(dist_pq, p.getRoutingObjectID());
- knns_q.add(knn);
+ knns_q.add(dist_pq, p.getRoutingObjectID());
if(knns_q.size() >= getKmax()) {
- knnDist_q = knns_q.getMaximumDistance();
+ knnDist_q = knns_q.getKNNDistance();
q.setKnnDistance(knnDist_q);
}
@@ -259,15 +257,13 @@ public class MkMaxTree<O, D extends Distance<D>> extends AbstractMkTreeUnified<O
// p is nearer to q than to its farthest knn-candidate
// q becomes knn of p
if(dist_pq.compareTo(p.getKnnDistance()) <= 0) {
- KNNHeap<D> knns_p = new KNNHeap<D>(getKmax(), getDistanceQuery().infiniteDistance());
- knns_p.add(new GenericDistanceResultPair<D>(dist_pq, q.getRoutingObjectID()));
- doKNNQuery(p.getRoutingObjectID(), knns_p);
+ KNNResult<D> knns_p = knnq.getKNNForDBID(p.getRoutingObjectID(), getKmax() - 1);
- if(knns_p.size() < getKmax()) {
+ if(knns_p.size() + 1 < getKmax()) {
p.setKnnDistance(getDistanceQuery().undefinedDistance());
}
else {
- D knnDist_p = knns_p.getMaximumDistance();
+ D knnDist_p = DistanceUtil.max(dist_pq, knns_p.getKNNDistance());
p.setKnnDistance(knnDist_p);
}
}
@@ -288,8 +284,8 @@ public class MkMaxTree<O, D extends Distance<D>> extends AbstractMkTreeUnified<O
knnDist_node = DistanceUtil.max(knnDist_node, dirEntry.getKnnDistance());
}
}
- if(logger.isDebugging()) {
- logger.debugFine(nodeEntry + "set knn dist " + knnDist_node);
+ if(LOG.isDebugging()) {
+ LOG.debugFine(nodeEntry + "set knn dist " + knnDist_node);
}
nodeEntry.setKnnDistance(knnDist_node);
}
@@ -313,7 +309,7 @@ public class MkMaxTree<O, D extends Distance<D>> extends AbstractMkTreeUnified<O
}
if(dirCapacity < 10) {
- logger.warning("Page size is choosen too small! Maximum number of entries " + "in a directory node = " + (dirCapacity - 1));
+ LOG.warning("Page size is choosen too small! Maximum number of entries " + "in a directory node = " + (dirCapacity - 1));
}
// leafCapacity = (file.getPageSize() - overhead) / (objectID +
@@ -326,7 +322,7 @@ public class MkMaxTree<O, D extends Distance<D>> extends AbstractMkTreeUnified<O
}
if(leafCapacity < 10) {
- logger.warning("Page size is choosen too small! Maximum number of entries " + "in a leaf node = " + (leafCapacity - 1));
+ LOG.warning("Page size is choosen too small! Maximum number of entries " + "in a leaf node = " + (leafCapacity - 1));
}
}
@@ -365,6 +361,6 @@ public class MkMaxTree<O, D extends Distance<D>> extends AbstractMkTreeUnified<O
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkmax/MkMaxTreeIndex.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkmax/MkMaxTreeIndex.java
index d1fd2b0f..0bd31dca 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkmax/MkMaxTreeIndex.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkmax/MkMaxTreeIndex.java
@@ -28,6 +28,8 @@ import java.util.List;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.query.DatabaseQuery;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
@@ -36,22 +38,32 @@ import de.lmu.ifi.dbs.elki.database.query.range.RangeQuery;
import de.lmu.ifi.dbs.elki.database.query.rknn.RKNNQuery;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
import de.lmu.ifi.dbs.elki.index.KNNIndex;
import de.lmu.ifi.dbs.elki.index.RKNNIndex;
import de.lmu.ifi.dbs.elki.index.RangeIndex;
import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTree;
import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.AbstractMkTreeUnified;
-import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.query.MetricalIndexKNNQuery;
-import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.query.MetricalIndexRangeQuery;
+import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.query.MTreeQueryUtil;
import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.query.MkTreeRKNNQuery;
import de.lmu.ifi.dbs.elki.persistent.PageFile;
-import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.KNNHeap;
import de.lmu.ifi.dbs.elki.utilities.exceptions.ExceptionMessages;
+/**
+ * MkMax tree
+ *
+ * @author Elke Achtert
+ *
+ * @param <O> Object type
+ * @param <D> Distance type
+ */
public class MkMaxTreeIndex<O, D extends Distance<D>> extends MkMaxTree<O, D> implements RangeIndex<O>, KNNIndex<O>, RKNNIndex<O> {
+ /**
+ * Relation indexed.
+ */
private Relation<O> relation;
-
+
/**
* Constructor.
*
@@ -71,22 +83,21 @@ public class MkMaxTreeIndex<O, D extends Distance<D>> extends MkMaxTree<O, D> im
* @return a new MkMaxLeafEntry representing the specified data object
*/
protected MkMaxLeafEntry<D> createNewLeafEntry(DBID id, O object, D parentDistance) {
- KNNHeap<D> knnList = new KNNHeap<D>(getKmax() - 1);
- doKNNQuery(id, knnList);
- D knnDistance = knnList.getMaximumDistance();
+ KNNResult<D> knns = knnq.getKNNForObject(object, getKmax() - 1);
+ D knnDistance = knns.getKNNDistance();
return new MkMaxLeafEntry<D>(id, parentDistance, knnDistance);
}
@Override
- public void insert(DBID id) {
- insert(createNewLeafEntry(id, relation.get(id), getDistanceFactory().undefinedDistance()), false);
+ public void insert(DBIDRef id) {
+ insert(createNewLeafEntry(DBIDUtil.deref(id), relation.get(id), getDistanceFactory().undefinedDistance()), false);
}
@Override
public void insertAll(DBIDs ids) {
List<MkMaxEntry<D>> objs = new ArrayList<MkMaxEntry<D>>(ids.size());
for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
- DBID id = iter.getDBID();
+ DBID id = DBIDUtil.deref(iter);
final O object = relation.get(id);
objs.add(createNewLeafEntry(id, object, getDistanceFactory().undefinedDistance()));
}
@@ -101,7 +112,7 @@ public class MkMaxTreeIndex<O, D extends Distance<D>> extends MkMaxTree<O, D> im
* implemented yet.
*/
@Override
- public final boolean delete(DBID id) {
+ public final boolean delete(DBIDRef id) {
throw new UnsupportedOperationException(ExceptionMessages.UNSUPPORTED_NOT_YET);
}
@@ -139,7 +150,7 @@ public class MkMaxTreeIndex<O, D extends Distance<D>> extends MkMaxTree<O, D> im
}
AbstractMTree<O, S, ?, ?> idx = (AbstractMTree<O, S, ?, ?>) this;
DistanceQuery<O, S> dq = distanceFunction.instantiate(relation);
- return new MetricalIndexKNNQuery<O, S>(idx, dq);
+ return MTreeQueryUtil.getKNNQuery(idx, dq, hints);
}
@SuppressWarnings("unchecked")
@@ -164,7 +175,7 @@ public class MkMaxTreeIndex<O, D extends Distance<D>> extends MkMaxTree<O, D> im
}
AbstractMTree<O, S, ?, ?> idx = (AbstractMTree<O, S, ?, ?>) this;
DistanceQuery<O, S> dq = distanceFunction.instantiate(relation);
- return new MetricalIndexRangeQuery<O, S>(idx, dq);
+ return MTreeQueryUtil.getRangeQuery(idx, dq);
}
@SuppressWarnings("unchecked")
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mktab/MkTabTree.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mktab/MkTabTree.java
index 433a01fa..f5410839 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mktab/MkTabTree.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mktab/MkTabTree.java
@@ -24,22 +24,22 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.mktab;
*/
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import java.util.Map;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
-import de.lmu.ifi.dbs.elki.database.query.GenericDistanceResultPair;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.distance.DistanceUtil;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.GenericDistanceDBIDList;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNUtil;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.AbstractMkTreeUnified;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.persistent.PageFile;
-import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.KNNHeap;
/**
* MkTabTree is a metrical index structure based on the concepts of the M-Tree
@@ -58,7 +58,7 @@ public class MkTabTree<O, D extends Distance<D>> extends AbstractMkTreeUnified<O
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(MkTabTree.class);
+ private static final Logging LOG = Logging.getLogger(MkTabTree.class);
/**
* Constructor.
@@ -91,15 +91,15 @@ public class MkTabTree<O, D extends Distance<D>> extends AbstractMkTreeUnified<O
}
@Override
- public List<DistanceResultPair<D>> reverseKNNQuery(DBIDRef id, int k) {
+ public DistanceDBIDResult<D> reverseKNNQuery(DBIDRef id, int k) {
if(k > this.getKmax()) {
throw new IllegalArgumentException("Parameter k has to be less or equal than " + "parameter kmax of the MkTab-Tree!");
}
- List<DistanceResultPair<D>> result = new ArrayList<DistanceResultPair<D>>();
+ GenericDistanceDBIDList<D> result = new GenericDistanceDBIDList<D>();
doReverseKNNQuery(k, id, null, getRoot(), result);
- Collections.sort(result);
+ result.sort();
return result;
}
@@ -122,7 +122,7 @@ public class MkTabTree<O, D extends Distance<D>> extends AbstractMkTreeUnified<O
}
if(dirCapacity < 10) {
- logger.warning("Page size is choosen too small! Maximum number of entries " + "in a directory node = " + (dirCapacity - 1));
+ LOG.warning("Page size is choosen too small! Maximum number of entries " + "in a directory node = " + (dirCapacity - 1));
}
// leafCapacity = (pageSize - overhead) / (objectID + parentDistance + +
@@ -134,19 +134,18 @@ public class MkTabTree<O, D extends Distance<D>> extends AbstractMkTreeUnified<O
}
if(leafCapacity < 10) {
- logger.warning("Page size is choosen too small! Maximum number of entries " + "in a leaf node = " + (leafCapacity - 1));
+ LOG.warning("Page size is choosen too small! Maximum number of entries " + "in a leaf node = " + (leafCapacity - 1));
}
-
}
-
+
@Override
- protected void kNNdistanceAdjustment(MkTabEntry<D> entry, Map<DBID, KNNHeap<D>> knnLists) {
+ protected void kNNdistanceAdjustment(MkTabEntry<D> entry, Map<DBID, KNNResult<D>> knnLists) {
MkTabTreeNode<O, D> node = getNode(entry);
List<D> knnDistances_node = initKnnDistanceList();
if(node.isLeaf()) {
for(int i = 0; i < node.getNumEntries(); i++) {
MkTabEntry<D> leafEntry = node.getEntry(i);
- leafEntry.setKnnDistances(knnLists.get(getPageID(leafEntry)).toKNNList().asDistanceList());
+ leafEntry.setKnnDistances(KNNUtil.asDistanceList(knnLists.get(getPageID(leafEntry))));
knnDistances_node = max(knnDistances_node, leafEntry.getKnnDistances());
}
}
@@ -210,14 +209,14 @@ public class MkTabTree<O, D extends Distance<D>> extends AbstractMkTreeUnified<O
* @param node the root of the subtree
* @param result the list holding the query result
*/
- private void doReverseKNNQuery(int k, DBIDRef q, MkTabEntry<D> node_entry, MkTabTreeNode<O, D> node, List<DistanceResultPair<D>> result) {
+ private void doReverseKNNQuery(int k, DBIDRef q, MkTabEntry<D> node_entry, MkTabTreeNode<O, D> node, GenericDistanceDBIDList<D> result) {
// data node
if(node.isLeaf()) {
for(int i = 0; i < node.getNumEntries(); i++) {
MkTabEntry<D> entry = node.getEntry(i);
D distance = getDistanceQuery().distance(entry.getRoutingObjectID(), q);
if(distance.compareTo(entry.getKnnDistance(k)) <= 0) {
- result.add(new GenericDistanceResultPair<D>(distance, entry.getRoutingObjectID()));
+ result.add(distance, entry.getRoutingObjectID());
}
}
}
@@ -278,6 +277,6 @@ public class MkTabTree<O, D extends Distance<D>> extends AbstractMkTreeUnified<O
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mktab/MkTabTreeIndex.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mktab/MkTabTreeIndex.java
index f1d23bfd..b12ac059 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mktab/MkTabTreeIndex.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mktab/MkTabTreeIndex.java
@@ -28,24 +28,25 @@ import java.util.List;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.query.DatabaseQuery;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.database.query.knn.KNNQuery;
-import de.lmu.ifi.dbs.elki.database.query.knn.KNNResult;
-import de.lmu.ifi.dbs.elki.database.query.knn.KNNUtil;
import de.lmu.ifi.dbs.elki.database.query.range.RangeQuery;
import de.lmu.ifi.dbs.elki.database.query.rknn.RKNNQuery;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNUtil;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
import de.lmu.ifi.dbs.elki.index.KNNIndex;
import de.lmu.ifi.dbs.elki.index.RKNNIndex;
import de.lmu.ifi.dbs.elki.index.RangeIndex;
import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTree;
import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.AbstractMkTreeUnified;
-import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.query.MetricalIndexKNNQuery;
-import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.query.MetricalIndexRangeQuery;
+import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.query.MTreeQueryUtil;
import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.query.MkTreeRKNNQuery;
import de.lmu.ifi.dbs.elki.persistent.PageFile;
import de.lmu.ifi.dbs.elki.utilities.exceptions.ExceptionMessages;
@@ -109,7 +110,7 @@ public class MkTabTreeIndex<O, D extends Distance<D>> extends MkTabTree<O, D> im
}
@Override
- public void insert(DBID id) {
+ public void insert(DBIDRef id) {
throw new UnsupportedOperationException("Insertion of single objects is not supported!");
}
@@ -117,7 +118,7 @@ public class MkTabTreeIndex<O, D extends Distance<D>> extends MkTabTree<O, D> im
public void insertAll(DBIDs ids) {
List<MkTabEntry<D>> objs = new ArrayList<MkTabEntry<D>>(ids.size());
for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
- DBID id = iter.getDBID();
+ DBID id = DBIDUtil.deref(iter);
final O object = relation.get(id);
objs.add(createNewLeafEntry(id, object, getDistanceFactory().undefinedDistance()));
}
@@ -132,7 +133,7 @@ public class MkTabTreeIndex<O, D extends Distance<D>> extends MkTabTree<O, D> im
* implemented yet.
*/
@Override
- public final boolean delete(DBID id) {
+ public final boolean delete(DBIDRef id) {
throw new UnsupportedOperationException(ExceptionMessages.UNSUPPORTED_NOT_YET);
}
@@ -170,7 +171,7 @@ public class MkTabTreeIndex<O, D extends Distance<D>> extends MkTabTree<O, D> im
}
AbstractMTree<O, S, ?, ?> idx = (AbstractMTree<O, S, ?, ?>) this;
DistanceQuery<O, S> dq = distanceFunction.instantiate(relation);
- return new MetricalIndexKNNQuery<O, S>(idx, dq);
+ return MTreeQueryUtil.getKNNQuery(idx, dq, hints);
}
@SuppressWarnings("unchecked")
@@ -195,7 +196,7 @@ public class MkTabTreeIndex<O, D extends Distance<D>> extends MkTabTree<O, D> im
}
AbstractMTree<O, S, ?, ?> idx = (AbstractMTree<O, S, ?, ?>) this;
DistanceQuery<O, S> dq = distanceFunction.instantiate(relation);
- return new MetricalIndexRangeQuery<O, S>(idx, dq);
+ return MTreeQueryUtil.getRangeQuery(idx, dq);
}
@SuppressWarnings("unchecked")
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mtree/MTree.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mtree/MTree.java
index 80955b8a..4276329c 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mtree/MTree.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mtree/MTree.java
@@ -55,7 +55,7 @@ public class MTree<O, D extends Distance<D>> extends AbstractMTree<O, D, MTreeNo
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(MTree.class);
+ private static final Logging LOG = Logging.getLogger(MTree.class);
/**
* Constructor.
@@ -81,7 +81,7 @@ public class MTree<O, D extends Distance<D>> extends AbstractMTree<O, D, MTreeNo
int distanceSize = exampleLeaf.getParentDistance().externalizableSize();
// FIXME: simulate a proper feature size!
- int featuresize = 0; // DatabaseUtil.dimensionality(relation);
+ int featuresize = 0; // RelationUtil.dimensionality(relation);
// overhead = index(4), numEntries(4), id(4), isLeaf(0.125)
double overhead = 12.125;
@@ -103,7 +103,7 @@ public class MTree<O, D extends Distance<D>> extends AbstractMTree<O, D, MTreeNo
}
if(dirCapacity < 10) {
- logger.warning("Page size is choosen too small! Maximum number of entries " + "in a directory node = " + (dirCapacity - 1));
+ LOG.warning("Page size is choosen too small! Maximum number of entries " + "in a directory node = " + (dirCapacity - 1));
}
// leafCapacity = (pageSize - overhead) / (objectID + parentDistance) +
// 1
@@ -118,11 +118,11 @@ public class MTree<O, D extends Distance<D>> extends AbstractMTree<O, D, MTreeNo
}
if(leafCapacity < 10) {
- logger.warning("Page size is choosen too small! Maximum number of entries " + "in a leaf node = " + (leafCapacity - 1));
+ LOG.warning("Page size is choosen too small! Maximum number of entries " + "in a leaf node = " + (leafCapacity - 1));
}
- if(logger.isVerbose()) {
- logger.verbose("Directory Capacity: " + (dirCapacity - 1) + "\nLeaf Capacity: " + (leafCapacity - 1));
+ if(LOG.isVerbose()) {
+ LOG.verbose("Directory Capacity: " + (dirCapacity - 1) + "\nLeaf Capacity: " + (leafCapacity - 1));
}
}
@@ -161,6 +161,6 @@ public class MTree<O, D extends Distance<D>> extends AbstractMTree<O, D, MTreeNo
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mtree/MTreeFactory.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mtree/MTreeFactory.java
index 2567f81f..5c5aac04 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mtree/MTreeFactory.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mtree/MTreeFactory.java
@@ -30,7 +30,6 @@ import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTreeFactor
import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeEntry;
import de.lmu.ifi.dbs.elki.persistent.PageFile;
import de.lmu.ifi.dbs.elki.utilities.ClassGenericsUtil;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
/**
* Factory for a M-Tree
@@ -75,11 +74,6 @@ public class MTreeFactory<O, D extends Distance<D>> extends AbstractMTreeFactory
*/
public static class Parameterizer<O, D extends Distance<D>> extends AbstractMTreeFactory.Parameterizer<O, D> {
@Override
- protected void makeOptions(Parameterization config) {
- super.makeOptions(config);
- }
-
- @Override
protected MTreeFactory<O, D> makeInstance() {
return new MTreeFactory<O, D>(fileName, pageSize, cacheSize, distanceFunction);
}
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mtree/MTreeIndex.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mtree/MTreeIndex.java
index fe60c04d..8afe1f2a 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mtree/MTreeIndex.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mtree/MTreeIndex.java
@@ -28,6 +28,8 @@ import java.util.List;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.query.DatabaseQuery;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
@@ -41,8 +43,7 @@ import de.lmu.ifi.dbs.elki.index.RangeIndex;
import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTree;
import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeEntry;
import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeLeafEntry;
-import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.query.MetricalIndexKNNQuery;
-import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.query.MetricalIndexRangeQuery;
+import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.query.MTreeQueryUtil;
import de.lmu.ifi.dbs.elki.persistent.PageFile;
import de.lmu.ifi.dbs.elki.utilities.exceptions.ExceptionMessages;
@@ -82,15 +83,15 @@ public class MTreeIndex<O, D extends Distance<D>> extends MTree<O, D> implements
}
@Override
- public void insert(DBID id) {
- insert(createNewLeafEntry(id, relation.get(id), getDistanceFactory().undefinedDistance()), false);
+ public void insert(DBIDRef id) {
+ insert(createNewLeafEntry(DBIDUtil.deref(id), relation.get(id), getDistanceFactory().undefinedDistance()), false);
}
@Override
public void insertAll(DBIDs ids) {
List<MTreeEntry<D>> objs = new ArrayList<MTreeEntry<D>>(ids.size());
for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
- DBID id = iter.getDBID();
+ DBID id = DBIDUtil.deref(iter);
final O object = relation.get(id);
objs.add(createNewLeafEntry(id, object, getDistanceFactory().undefinedDistance()));
}
@@ -105,7 +106,7 @@ public class MTreeIndex<O, D extends Distance<D>> extends MTree<O, D> implements
* implemented yet.
*/
@Override
- public final boolean delete(DBID id) {
+ public final boolean delete(DBIDRef id) {
throw new UnsupportedOperationException(ExceptionMessages.UNSUPPORTED_NOT_YET);
}
@@ -143,7 +144,7 @@ public class MTreeIndex<O, D extends Distance<D>> extends MTree<O, D> implements
}
AbstractMTree<O, S, ?, ?> idx = (AbstractMTree<O, S, ?, ?>) this;
DistanceQuery<O, S> dq = distanceFunction.instantiate(relation);
- return new MetricalIndexKNNQuery<O, S>(idx, dq);
+ return MTreeQueryUtil.getKNNQuery(idx, dq, hints);
}
@SuppressWarnings("unchecked")
@@ -168,7 +169,7 @@ public class MTreeIndex<O, D extends Distance<D>> extends MTree<O, D> implements
}
AbstractMTree<O, S, ?, ?> idx = (AbstractMTree<O, S, ?, ?>) this;
DistanceQuery<O, S> dq = distanceFunction.instantiate(relation);
- return new MetricalIndexRangeQuery<O, S>(idx, dq);
+ return MTreeQueryUtil.getRangeQuery(idx, dq);
}
@Override
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/DoubleDistanceMetricalIndexKNNQuery.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/DoubleDistanceMetricalIndexKNNQuery.java
new file mode 100644
index 00000000..52f777ba
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/DoubleDistanceMetricalIndexKNNQuery.java
@@ -0,0 +1,155 @@
+package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.query;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.List;
+
+import de.lmu.ifi.dbs.elki.database.ids.ArrayDBIDs;
+import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
+import de.lmu.ifi.dbs.elki.database.query.knn.AbstractDistanceKNNQuery;
+import de.lmu.ifi.dbs.elki.distance.distancefunction.PrimitiveDoubleDistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DoubleDistanceKNNHeap;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
+import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
+import de.lmu.ifi.dbs.elki.index.tree.DirectoryEntry;
+import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTree;
+import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTreeNode;
+import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeEntry;
+import de.lmu.ifi.dbs.elki.index.tree.query.DoubleMTreeDistanceSearchCandidate;
+import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.Heap;
+import de.lmu.ifi.dbs.elki.utilities.exceptions.ExceptionMessages;
+
+/**
+ * Instance of a KNN query for a particular spatial index.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.uses AbstractMTree
+ *
+ * @param <O> Object type
+ */
+public class DoubleDistanceMetricalIndexKNNQuery<O> extends AbstractDistanceKNNQuery<O, DoubleDistance> {
+ /**
+ * The index to use
+ */
+ protected final AbstractMTree<O, DoubleDistance, ?, ?> index;
+
+ /**
+ * Distance function
+ */
+ protected PrimitiveDoubleDistanceFunction<? super O> distf;
+
+ /**
+ * Constructor.
+ *
+ * @param index Index to use
+ * @param distanceQuery Distance query used
+ * @param distf Distance function
+ */
+ public DoubleDistanceMetricalIndexKNNQuery(AbstractMTree<O, DoubleDistance, ?, ?> index, DistanceQuery<O, DoubleDistance> distanceQuery, PrimitiveDoubleDistanceFunction<? super O> distf) {
+ super(distanceQuery);
+ this.index = index;
+ this.distf = distf;
+ }
+
+ @Override
+ public KNNResult<DoubleDistance> getKNNForObject(O q, int k) {
+ if (k < 1) {
+ throw new IllegalArgumentException("At least one object has to be requested!");
+ }
+
+ DoubleDistanceKNNHeap knnList = new DoubleDistanceKNNHeap(k);
+ double d_k = Double.POSITIVE_INFINITY;
+
+ final Heap<DoubleMTreeDistanceSearchCandidate> pq = new Heap<DoubleMTreeDistanceSearchCandidate>();
+
+ // Push the root node
+ pq.add(new DoubleMTreeDistanceSearchCandidate(0, index.getRootID(), null, 0));
+
+ // search in tree
+ while (!pq.isEmpty()) {
+ DoubleMTreeDistanceSearchCandidate pqNode = pq.poll();
+ DBID id_p = pqNode.routingObjectID;
+ double d1 = pqNode.routingDistance;
+
+ if (knnList.size() >= k && pqNode.mindist > d_k) {
+ break;
+ }
+
+ AbstractMTreeNode<?, DoubleDistance, ?, ?> node = index.getNode(pqNode.nodeID);
+
+ // directory node
+ if (!node.isLeaf()) {
+ for (int i = 0; i < node.getNumEntries(); i++) {
+ final MTreeEntry<DoubleDistance> entry = node.getEntry(i);
+ final DBID id_i = entry.getRoutingObjectID();
+ double or_i = entry.getCoveringRadius().doubleValue();
+ double d2 = id_p != null ? entry.getParentDistance().doubleValue() : 0;
+ double diff = Math.abs(d1 - d2);
+
+ if (diff <= d_k + or_i) {
+ final O ob_i = relation.get(id_i);
+ double d3 = distf.doubleDistance(ob_i, q);
+ double d_min = Math.max(d3 - or_i, 0);
+ if (d_min <= d_k) {
+ pq.add(new DoubleMTreeDistanceSearchCandidate(d_min, ((DirectoryEntry) entry).getPageID(), id_i, d3));
+ }
+ }
+ }
+ }
+ // data node
+ else {
+ for (int i = 0; i < node.getNumEntries(); i++) {
+ final MTreeEntry<DoubleDistance> entry = node.getEntry(i);
+ final DBID id_i = entry.getRoutingObjectID();
+ double d2 = id_p != null ? entry.getParentDistance().doubleValue() : 0;
+ double diff = Math.abs(d1 - d2);
+
+ if (diff <= d_k) {
+ final O o_i = relation.get(id_i);
+ double d3 = distf.doubleDistance(o_i, q);
+ if (d3 <= d_k) {
+ knnList.add(d3, id_i);
+ d_k = knnList.doubleKNNDistance();
+ }
+ }
+ }
+ }
+ }
+ return knnList.toKNNList();
+ }
+
+ @Override
+ public KNNResult<DoubleDistance> getKNNForDBID(DBIDRef id, int k) {
+ return getKNNForObject(relation.get(id), k);
+ }
+
+ @Override
+ public List<KNNResult<DoubleDistance>> getKNNForBulkDBIDs(ArrayDBIDs ids, int k) {
+ // TODO: implement
+ throw new UnsupportedOperationException(ExceptionMessages.UNSUPPORTED_NOT_YET);
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/DoubleDistanceMetricalIndexRangeQuery.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/DoubleDistanceMetricalIndexRangeQuery.java
new file mode 100644
index 00000000..c3680111
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/DoubleDistanceMetricalIndexRangeQuery.java
@@ -0,0 +1,135 @@
+package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.query;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
+import de.lmu.ifi.dbs.elki.database.query.range.AbstractDistanceRangeQuery;
+import de.lmu.ifi.dbs.elki.distance.distancefunction.PrimitiveDoubleDistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DoubleDistanceDBIDList;
+import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
+import de.lmu.ifi.dbs.elki.index.tree.DirectoryEntry;
+import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTree;
+import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTreeNode;
+import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeEntry;
+
+/**
+ * Instance of a range query for a particular spatial index.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.uses AbstractMTree
+ */
+public class DoubleDistanceMetricalIndexRangeQuery<O> extends AbstractDistanceRangeQuery<O, DoubleDistance> {
+ /**
+ * The index to use
+ */
+ protected final AbstractMTree<O, DoubleDistance, ?, ?> index;
+
+ /**
+ * Distance function
+ */
+ protected PrimitiveDoubleDistanceFunction<? super O> distf;
+
+ /**
+ * Constructor.
+ *
+ * @param index Index to use
+ * @param distanceQuery Distance query used
+ * @param distf Distance function
+ */
+ public DoubleDistanceMetricalIndexRangeQuery(AbstractMTree<O, DoubleDistance, ?, ?> index, DistanceQuery<O, DoubleDistance> distanceQuery, PrimitiveDoubleDistanceFunction<? super O> distf) {
+ super(distanceQuery);
+ this.index = index;
+ this.distf = distf;
+ }
+
+ /**
+ * Performs a range query on the specified subtree. It recursively traverses
+ * all paths from the specified node, which cannot be excluded from leading to
+ * qualifying objects.
+ *
+ * @param id_p the routing object of the specified node
+ * @param node the root of the subtree to be traversed
+ * @param q the query object
+ * @param r_q the query range
+ * @param result the list holding the query results
+ */
+ private void doRangeQuery(DBID id_p, AbstractMTreeNode<O, DoubleDistance, ?, ?> node, O q, double r_q, DoubleDistanceDBIDList result) {
+ final O o_p = id_p != null ? relation.get(id_p) : null;
+ double d1 = id_p != null ? distf.doubleDistance(o_p, q) : 0;
+ if (!node.isLeaf()) {
+ for (int i = 0; i < node.getNumEntries(); i++) {
+ MTreeEntry<DoubleDistance> entry = node.getEntry(i);
+
+ double r_or = entry.getCoveringRadius().doubleValue();
+ double d2 = id_p != null ? entry.getParentDistance().doubleValue() : 0;
+ double diff = Math.abs(d1 - d2);
+
+ double sum = r_q + r_or;
+
+ if (diff <= sum) {
+ DBID id_r = entry.getRoutingObjectID();
+ double d3 = distf.doubleDistance(relation.get(id_r), q);
+ if (d3 <= sum) {
+ AbstractMTreeNode<O, DoubleDistance, ?, ?> child = index.getNode(((DirectoryEntry) entry).getPageID());
+ doRangeQuery(id_r, child, q, r_q, result);
+ }
+ }
+ }
+ } else {
+ for (int i = 0; i < node.getNumEntries(); i++) {
+ MTreeEntry<DoubleDistance> entry = node.getEntry(i);
+
+ double d2 = id_p != null ? entry.getParentDistance().doubleValue() : 0;
+ double diff = Math.abs(d1 - d2);
+
+ if (diff <= r_q) {
+ DBID id_j = entry.getRoutingObjectID();
+ O o_j = relation.get(id_j);
+ double d3 = distf.doubleDistance(o_j, q);
+ if (d3 <= r_q) {
+ result.add(d3, id_j);
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public DistanceDBIDResult<DoubleDistance> getRangeForObject(O obj, DoubleDistance range) {
+ final DoubleDistanceDBIDList result = new DoubleDistanceDBIDList();
+
+ doRangeQuery(null, index.getRoot(), obj, range.doubleValue(), result);
+ result.sort();
+ return result;
+ }
+
+ @Override
+ public DistanceDBIDResult<DoubleDistance> getRangeForDBID(DBIDRef id, DoubleDistance range) {
+ return getRangeForObject(relation.get(id), range);
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/MTreeQueryUtil.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/MTreeQueryUtil.java
new file mode 100644
index 00000000..34980542
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/MTreeQueryUtil.java
@@ -0,0 +1,90 @@
+package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.query;
+
+import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
+import de.lmu.ifi.dbs.elki.database.query.knn.KNNQuery;
+import de.lmu.ifi.dbs.elki.database.query.range.RangeQuery;
+import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distancefunction.PrimitiveDoubleDistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
+import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
+import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTree;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * Query utility classes for MTrees.
+ *
+ * @author Erich Schubert
+ */
+public final class MTreeQueryUtil {
+ /**
+ * Get an RTree knn query, using an optimized double implementation when
+ * possible.
+ *
+ * @param <O> Object type
+ * @param <D> Distance type
+ * @param tree Tree to query
+ * @param distanceQuery distance query
+ * @param hints Optimizer hints
+ * @return Query object
+ */
+ @SuppressWarnings({ "cast", "unchecked" })
+ public static <O, D extends Distance<D>> KNNQuery<O, D> getKNNQuery(AbstractMTree<O, D, ?, ?> tree, DistanceQuery<O, D> distanceQuery, Object... hints) {
+ DistanceFunction<? super O, D> df = distanceQuery.getDistanceFunction();
+ // Can we use an optimized query?
+ if(df instanceof PrimitiveDoubleDistanceFunction) {
+ PrimitiveDoubleDistanceFunction<? super O> dfc = (PrimitiveDoubleDistanceFunction<? super O>) df;
+ AbstractMTree<O, DoubleDistance, ?, ?> treec = (AbstractMTree<O, DoubleDistance, ?, ?>) tree;
+ DistanceQuery<O, DoubleDistance> dqc = (DistanceQuery<O, DoubleDistance>) distanceQuery;
+ KNNQuery<O, ?> q = new DoubleDistanceMetricalIndexKNNQuery<O>(treec, dqc, dfc);
+ return (KNNQuery<O, D>) q;
+ }
+ return new MetricalIndexKNNQuery<O, D>(tree, distanceQuery);
+ }
+
+ /**
+ * Get an RTree knn query, using an optimized double implementation when
+ * possible.
+ *
+ * @param <O> Object type
+ * @param <D> Distance type
+ * @param tree Tree to query
+ * @param distanceQuery distance query
+ * @param hints Optimizer hints
+ * @return Query object
+ */
+ @SuppressWarnings({ "cast", "unchecked" })
+ public static <O, D extends Distance<D>> RangeQuery<O, D> getRangeQuery(AbstractMTree<O, D, ?, ?> tree, DistanceQuery<O, D> distanceQuery, Object... hints) {
+ DistanceFunction<? super O, D> df = distanceQuery.getDistanceFunction();
+ // Can we use an optimized query?
+ if(df instanceof PrimitiveDoubleDistanceFunction) {
+ PrimitiveDoubleDistanceFunction<? super O> dfc = (PrimitiveDoubleDistanceFunction<? super O>) df;
+ AbstractMTree<O, DoubleDistance, ?, ?> treec = (AbstractMTree<O, DoubleDistance, ?, ?>) tree;
+ DistanceQuery<O, DoubleDistance> dqc = (DistanceQuery<O, DoubleDistance>) distanceQuery;
+ RangeQuery<O, ?> q = new DoubleDistanceMetricalIndexRangeQuery<O>(treec, dqc, dfc);
+ return (RangeQuery<O, D>) q;
+ }
+ return new MetricalIndexRangeQuery<O, D>(tree, distanceQuery);
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/MetricalIndexKNNQuery.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/MetricalIndexKNNQuery.java
index c2450988..c2fafdae 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/MetricalIndexKNNQuery.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/MetricalIndexKNNQuery.java
@@ -24,15 +24,16 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.query;
*/
import java.util.List;
-import java.util.Map;
import de.lmu.ifi.dbs.elki.database.ids.ArrayDBIDs;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.database.query.knn.AbstractDistanceKNNQuery;
-import de.lmu.ifi.dbs.elki.database.query.knn.KNNResult;
import de.lmu.ifi.dbs.elki.distance.DistanceUtil;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNHeap;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNUtil;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
import de.lmu.ifi.dbs.elki.index.tree.DirectoryEntry;
import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTree;
@@ -40,8 +41,6 @@ import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTreeNode;
import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeEntry;
import de.lmu.ifi.dbs.elki.index.tree.query.GenericMTreeDistanceSearchCandidate;
import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.Heap;
-import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.KNNHeap;
-import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.UpdatableHeap;
import de.lmu.ifi.dbs.elki.utilities.exceptions.ExceptionMessages;
/**
@@ -62,7 +61,7 @@ public class MetricalIndexKNNQuery<O, D extends Distance<D>> extends AbstractDis
/**
* Constructor.
- *
+ *
* @param index Index to use
* @param distanceQuery Distance query used
*/
@@ -71,70 +70,67 @@ public class MetricalIndexKNNQuery<O, D extends Distance<D>> extends AbstractDis
this.index = index;
}
- /**
- * Performs a k-nearest neighbor query for the given FeatureVector with the
- * given parameter k and the according distance function. The query result is
- * in ascending order to the distance to the query object.
- *
- * @param q the id of the query object
- * @param knnList the query result list
- */
- protected final void doKNNQuery(O q, KNNHeap<D> knnList) {
- final Heap<GenericMTreeDistanceSearchCandidate<D>> pq = new UpdatableHeap<GenericMTreeDistanceSearchCandidate<D>>();
+ @Override
+ public KNNResult<D> getKNNForObject(O q, int k) {
+ if (k < 1) {
+ throw new IllegalArgumentException("At least one object has to be requested!");
+ }
- // push root
- pq.add(new GenericMTreeDistanceSearchCandidate<D>(distanceQuery.nullDistance(), index.getRootID(), null));
+ final D nullDistance = index.getDistanceFactory().nullDistance();
+ KNNHeap<D> knnList = KNNUtil.newHeap(distanceQuery.getDistanceFactory(), k);
D d_k = knnList.getKNNDistance();
+ final Heap<GenericMTreeDistanceSearchCandidate<D>> pq = new Heap<GenericMTreeDistanceSearchCandidate<D>>();
+
+ // push root
+ pq.add(new GenericMTreeDistanceSearchCandidate<D>(nullDistance, index.getRootID(), null, nullDistance));
+
// search in tree
- while(!pq.isEmpty()) {
+ while (!pq.isEmpty()) {
GenericMTreeDistanceSearchCandidate<D> pqNode = pq.poll();
- if(pqNode.mindist.compareTo(d_k) > 0) {
- return;
+ if (knnList.size() >= k && pqNode.mindist.compareTo(d_k) > 0) {
+ break;
}
- AbstractMTreeNode<O, D, ?, ?> node = index.getNode(pqNode.nodeID);
- DBID o_p = pqNode.routingObjectID;
+ AbstractMTreeNode<?, D, ?, ?> node = index.getNode(pqNode.nodeID);
+ DBID id_p = pqNode.routingObjectID;
+ D d1 = pqNode.routingDistance;
// directory node
- if(!node.isLeaf()) {
- for(int i = 0; i < node.getNumEntries(); i++) {
+ if (!node.isLeaf()) {
+ for (int i = 0; i < node.getNumEntries(); i++) {
MTreeEntry<D> entry = node.getEntry(i);
DBID o_r = entry.getRoutingObjectID();
D r_or = entry.getCoveringRadius();
- D d1 = o_p != null ? distanceQuery.distance(o_p, q) : distanceQuery.nullDistance();
- D d2 = o_p != null ? distanceQuery.distance(o_r, o_p) : distanceQuery.nullDistance();
+ D d2 = id_p != null ? entry.getParentDistance() : nullDistance;
D diff = d1.compareTo(d2) > 0 ? d1.minus(d2) : d2.minus(d1);
D sum = d_k.plus(r_or);
- if(diff.compareTo(sum) <= 0) {
+ if (diff.compareTo(sum) <= 0) {
D d3 = distanceQuery.distance(o_r, q);
- D d_min = DistanceUtil.max(d3.minus(r_or), distanceQuery.nullDistance());
- if(d_min.compareTo(d_k) <= 0) {
- pq.add(new GenericMTreeDistanceSearchCandidate<D>(d_min, ((DirectoryEntry)entry).getPageID(), o_r));
+ D d_min = DistanceUtil.max(d3.minus(r_or), index.getDistanceFactory().nullDistance());
+ if (d_min.compareTo(d_k) <= 0) {
+ pq.add(new GenericMTreeDistanceSearchCandidate<D>(d_min, ((DirectoryEntry) entry).getPageID(), o_r, d3));
}
}
}
-
}
-
// data node
else {
- for(int i = 0; i < node.getNumEntries(); i++) {
+ for (int i = 0; i < node.getNumEntries(); i++) {
MTreeEntry<D> entry = node.getEntry(i);
DBID o_j = entry.getRoutingObjectID();
- D d1 = o_p != null ? distanceQuery.distance(o_p, q) : distanceQuery.nullDistance();
- D d2 = o_p != null ? distanceQuery.distance(o_j, o_p) : distanceQuery.nullDistance();
+ D d2 = id_p != null ? entry.getParentDistance() : nullDistance;
- D diff = d1.compareTo(d2) > 0 ? d1.minus(d2) : d2.minus(d1);
+ D diff = (d1.compareTo(d2) > 0) ? d1.minus(d2) : d2.minus(d1);
- if(diff.compareTo(d_k) <= 0) {
+ if (diff.compareTo(d_k) <= 0) {
D d3 = distanceQuery.distance(o_j, q);
- if(d3.compareTo(d_k) <= 0) {
+ if (d3.compareTo(d_k) <= 0) {
knnList.add(d3, o_j);
d_k = knnList.getKNNDistance();
}
@@ -142,16 +138,6 @@ public class MetricalIndexKNNQuery<O, D extends Distance<D>> extends AbstractDis
}
}
}
- }
-
- @Override
- public KNNResult<D> getKNNForObject(O obj, int k) {
- if(k < 1) {
- throw new IllegalArgumentException("At least one object has to be requested!");
- }
-
- final KNNHeap<D> knnList = new KNNHeap<D>(k, distanceQuery.getDistanceFactory().infiniteDistance());
- doKNNQuery(obj, knnList);
return knnList.toKNNList();
}
@@ -165,10 +151,4 @@ public class MetricalIndexKNNQuery<O, D extends Distance<D>> extends AbstractDis
// TODO: implement
throw new UnsupportedOperationException(ExceptionMessages.UNSUPPORTED_NOT_YET);
}
-
- @Override
- public void getKNNForBulkHeaps(Map<DBID, KNNHeap<D>> heaps) {
- // TODO: implement
- throw new UnsupportedOperationException(ExceptionMessages.UNSUPPORTED_NOT_YET);
- }
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/MetricalIndexRangeQuery.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/MetricalIndexRangeQuery.java
index e2df2dc7..2ca19877 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/MetricalIndexRangeQuery.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/MetricalIndexRangeQuery.java
@@ -23,17 +23,12 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.query;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.util.Collections;
-import java.util.List;
-
import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
-import de.lmu.ifi.dbs.elki.database.query.DistanceDBIDResult;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
-import de.lmu.ifi.dbs.elki.database.query.GenericDistanceDBIDList;
-import de.lmu.ifi.dbs.elki.database.query.GenericDistanceResultPair;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.database.query.range.AbstractDistanceRangeQuery;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.GenericDistanceDBIDList;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
import de.lmu.ifi.dbs.elki.index.tree.DirectoryEntry;
import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTree;
@@ -75,109 +70,47 @@ public class MetricalIndexRangeQuery<O, D extends Distance<D>> extends AbstractD
* @param r_q the query range
* @param result the list holding the query results
*/
- private void doRangeQuery(DBID o_p, AbstractMTreeNode<O, D, ?, ?> node, DBID q, D r_q, List<DistanceResultPair<D>> result) {
- if(!node.isLeaf()) {
- for(int i = 0; i < node.getNumEntries(); i++) {
+ private void doRangeQuery(DBID o_p, AbstractMTreeNode<O, D, ?, ?> node, O q, D r_q, GenericDistanceDBIDList<D> result) {
+ final D nullDistance = distanceQuery.nullDistance();
+ D d1 = o_p != null ? distanceQuery.distance(o_p, q) : nullDistance;
+ if (!node.isLeaf()) {
+ for (int i = 0; i < node.getNumEntries(); i++) {
MTreeEntry<D> entry = node.getEntry(i);
DBID o_r = entry.getRoutingObjectID();
D r_or = entry.getCoveringRadius();
- D d1 = o_p != null ? distanceQuery.distance(o_p, q) : distanceQuery.nullDistance();
- D d2 = o_p != null ? entry.getParentDistance() : distanceQuery.nullDistance();
- // o_p != null ? distanceFunction.distance(o_r, o_p) :/ distanceFunction.nullDistance();
-
+ D d2 = o_p != null ? entry.getParentDistance() : nullDistance;
D diff = d1.compareTo(d2) > 0 ? d1.minus(d2) : d2.minus(d1);
D sum = r_q.plus(r_or);
- if(diff.compareTo(sum) <= 0) {
+ if (diff.compareTo(sum) <= 0) {
D d3 = distanceQuery.distance(o_r, q);
- if(d3.compareTo(sum) <= 0) {
- AbstractMTreeNode<O, D, ?, ?> child = index.getNode(((DirectoryEntry)entry).getPageID());
+ if (d3.compareTo(sum) <= 0) {
+ AbstractMTreeNode<O, D, ?, ?> child = index.getNode(((DirectoryEntry) entry).getPageID());
doRangeQuery(o_r, child, q, r_q, result);
}
}
-
}
- }
-
- else {
- for(int i = 0; i < node.getNumEntries(); i++) {
+ } else {
+ for (int i = 0; i < node.getNumEntries(); i++) {
MTreeEntry<D> entry = node.getEntry(i);
DBID o_j = entry.getRoutingObjectID();
- D d1 = o_p != null ? distanceQuery.distance(o_p, q) : distanceQuery.nullDistance();
- D d2 = o_p != null ? distanceQuery.distance(o_j, o_p) : distanceQuery.nullDistance();
+ D d2 = o_p != null ? entry.getParentDistance() : nullDistance;
D diff = d1.compareTo(d2) > 0 ? d1.minus(d2) : d2.minus(d1);
- if(diff.compareTo(r_q) <= 0) {
+ if (diff.compareTo(r_q) <= 0) {
D d3 = distanceQuery.distance(o_j, q);
- if(d3.compareTo(r_q) <= 0) {
- DistanceResultPair<D> queryResult = new GenericDistanceResultPair<D>(d3, o_j);
- result.add(queryResult);
+ if (d3.compareTo(r_q) <= 0) {
+ result.add(d3, o_j);
}
}
}
}
}
- /**
- * Performs a range query on the specified subtree. It recursively traverses
- * all paths from the specified node, which cannot be excluded from leading to
- * qualifying objects.
- *
- * @param o_p the routing object of the specified node
- * @param node the root of the subtree to be traversed
- * @param q the id of the query object
- * @param r_q the query range
- * @param result the list holding the query results
- */
- private void doRangeQuery(DBID o_p, AbstractMTreeNode<O, D, ?, ?> node, O q, D r_q, List<DistanceResultPair<D>> result) {
- if(!node.isLeaf()) {
- for(int i = 0; i < node.getNumEntries(); i++) {
- MTreeEntry<D> entry = node.getEntry(i);
- DBID o_r = entry.getRoutingObjectID();
-
- D r_or = entry.getCoveringRadius();
- D d1 = o_p != null ? distanceQuery.distance(o_p, q) : distanceQuery.nullDistance();
- D d2 = o_p != null ? entry.getParentDistance() : distanceQuery.nullDistance();
- // o_p != null ? distanceFunction.distance(o_r, o_p) : distanceFunction.nullDistance();
-
- D diff = d1.compareTo(d2) > 0 ? d1.minus(d2) : d2.minus(d1);
-
- D sum = r_q.plus(r_or);
-
- if(diff.compareTo(sum) <= 0) {
- D d3 = distanceQuery.distance(o_r, q);
- if(d3.compareTo(sum) <= 0) {
- AbstractMTreeNode<O, D, ?, ?> child = index.getNode(((DirectoryEntry)entry).getPageID());
- doRangeQuery(o_r, child, q, r_q, result);
- }
- }
- }
- }
- else {
- for(int i = 0; i < node.getNumEntries(); i++) {
- MTreeEntry<D> entry = node.getEntry(i);
- DBID o_j = entry.getRoutingObjectID();
-
- D d1 = o_p != null ? distanceQuery.distance(o_p, q) : distanceQuery.nullDistance();
- D d2 = o_p != null ? distanceQuery.distance(o_j, o_p) : distanceQuery.nullDistance();
-
- D diff = d1.compareTo(d2) > 0 ? d1.minus(d2) : d2.minus(d1);
-
- if(diff.compareTo(r_q) <= 0) {
- D d3 = distanceQuery.distance(o_j, q);
- if(d3.compareTo(r_q) <= 0) {
- DistanceResultPair<D> queryResult = new GenericDistanceResultPair<D>(d3, o_j);
- result.add(queryResult);
- }
- }
- }
- }
- }
-
@Override
public DistanceDBIDResult<D> getRangeForObject(O obj, D range) {
final GenericDistanceDBIDList<D> result = new GenericDistanceDBIDList<D>();
@@ -185,7 +118,7 @@ public class MetricalIndexRangeQuery<O, D extends Distance<D>> extends AbstractD
doRangeQuery(null, index.getRoot(), obj, range, result);
// sort the result according to the distances
- Collections.sort(result);
+ result.sort();
return result;
}
@@ -193,4 +126,4 @@ public class MetricalIndexRangeQuery<O, D extends Distance<D>> extends AbstractD
public DistanceDBIDResult<D> getRangeForDBID(DBIDRef id, D range) {
return getRangeForObject(relation.get(id), range);
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/MkTreeRKNNQuery.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/MkTreeRKNNQuery.java
index 067c3abe..e3f35c50 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/MkTreeRKNNQuery.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/MkTreeRKNNQuery.java
@@ -27,9 +27,9 @@ import java.util.List;
import de.lmu.ifi.dbs.elki.database.ids.ArrayDBIDs;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.database.query.rknn.AbstractRKNNQuery;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.AbstractMkTree;
import de.lmu.ifi.dbs.elki.utilities.exceptions.AbortException;
@@ -60,17 +60,17 @@ public class MkTreeRKNNQuery<O, D extends Distance<D>> extends AbstractRKNNQuery
}
@Override
- public List<DistanceResultPair<D>> getRKNNForObject(O obj, int k) {
+ public DistanceDBIDResult<D> getRKNNForObject(O obj, int k) {
throw new AbortException("Preprocessor KNN query only supports ID queries.");
}
@Override
- public List<DistanceResultPair<D>> getRKNNForDBID(DBIDRef id, int k) {
+ public DistanceDBIDResult<D> getRKNNForDBID(DBIDRef id, int k) {
return index.reverseKNNQuery(id, k);
}
@Override
- public List<List<DistanceResultPair<D>>> getRKNNForBulkDBIDs(ArrayDBIDs ids, int k) {
+ public List<? extends DistanceDBIDResult<D>> getRKNNForBulkDBIDs(ArrayDBIDs ids, int k) {
// TODO: implement
throw new UnsupportedOperationException(ExceptionMessages.UNSUPPORTED_NOT_YET);
}
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/query/DoubleDistanceSearchCandidate.java b/src/de/lmu/ifi/dbs/elki/index/tree/query/DoubleDistanceSearchCandidate.java
index 699c8290..1a3fc948 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/query/DoubleDistanceSearchCandidate.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/query/DoubleDistanceSearchCandidate.java
@@ -23,10 +23,9 @@ package de.lmu.ifi.dbs.elki.index.tree.query;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-
-
/**
- * Candidate for expansion in a distance search (generic implementation).
+ * Candidate for expansion in a distance search (double optimized
+ * implementation).
*
* @author Erich Schubert
*/
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/query/DoubleMTreeDistanceSearchCandidate.java b/src/de/lmu/ifi/dbs/elki/index/tree/query/DoubleMTreeDistanceSearchCandidate.java
new file mode 100644
index 00000000..c26ac801
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/query/DoubleMTreeDistanceSearchCandidate.java
@@ -0,0 +1,62 @@
+package de.lmu.ifi.dbs.elki.index.tree.query;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import de.lmu.ifi.dbs.elki.database.ids.DBID;
+
+/**
+ * Encapsulates the attributes for a object that can be stored in a heap. The
+ * object to be stored represents a node in a M-Tree and some additional
+ * information. Additionally to the regular expansion candidate, this object
+ * holds the id of the routing object of the underlying M-Tree node and its
+ * covering radius.
+ *
+ * @author Elke Achtert
+ */
+public class DoubleMTreeDistanceSearchCandidate extends DoubleDistanceSearchCandidate {
+ /**
+ * The id of the routing object.
+ */
+ public DBID routingObjectID;
+
+ /**
+ * The distance from the query object to the routing object
+ */
+ public double routingDistance;
+
+ /**
+ * Creates a new heap node with the specified parameters.
+ *
+ * @param mindist the minimum distance of the node
+ * @param nodeID the id of the node
+ * @param routingObjectID the id of the routing object of the node
+ * @param routingDistance the distance from the query object to the query
+ * object
+ */
+ public DoubleMTreeDistanceSearchCandidate(final double mindist, final Integer nodeID, final DBID routingObjectID, double routingDistance) {
+ super(mindist, nodeID);
+ this.routingObjectID = routingObjectID;
+ this.routingDistance = routingDistance;
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/query/GenericMTreeDistanceSearchCandidate.java b/src/de/lmu/ifi/dbs/elki/index/tree/query/GenericMTreeDistanceSearchCandidate.java
index e7fc0b19..94335860 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/query/GenericMTreeDistanceSearchCandidate.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/query/GenericMTreeDistanceSearchCandidate.java
@@ -42,6 +42,11 @@ public class GenericMTreeDistanceSearchCandidate<D extends Distance<D>> extends
* The id of the routing object.
*/
public DBID routingObjectID;
+
+ /**
+ * The distance from the query to the routing object.
+ */
+ public D routingDistance;
/**
* Creates a new heap node with the specified parameters.
@@ -49,9 +54,11 @@ public class GenericMTreeDistanceSearchCandidate<D extends Distance<D>> extends
* @param mindist the minimum distance of the node
* @param nodeID the id of the node
* @param routingObjectID the id of the routing object of the node
+ * @param routingDistance the distance from query to routing object
*/
- public GenericMTreeDistanceSearchCandidate(final D mindist, final Integer nodeID, final DBID routingObjectID) {
+ public GenericMTreeDistanceSearchCandidate(final D mindist, final Integer nodeID, final DBID routingObjectID, final D routingDistance) {
super(mindist, nodeID);
this.routingObjectID = routingObjectID;
+ this.routingDistance = routingDistance;
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/SpatialPointLeafEntry.java b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/SpatialPointLeafEntry.java
index 0911a9c0..60981677 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/SpatialPointLeafEntry.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/SpatialPointLeafEntry.java
@@ -39,6 +39,9 @@ import de.lmu.ifi.dbs.elki.index.tree.AbstractLeafEntry;
* @author Elke Achtert
*/
public class SpatialPointLeafEntry extends AbstractLeafEntry implements SpatialEntry {
+ /**
+ * Serial version.
+ */
private static final long serialVersionUID = 1;
/**
@@ -65,17 +68,17 @@ public class SpatialPointLeafEntry extends AbstractLeafEntry implements SpatialE
}
/**
- * Constructor from number vector
+ * Constructor from number vector.
*
* @param id Object id
* @param vector Number vector
*/
- public SpatialPointLeafEntry(DBID id, NumberVector<?, ?> vector) {
+ public SpatialPointLeafEntry(DBID id, NumberVector<?> vector) {
super(id);
int dim = vector.getDimensionality();
this.values = new double[dim];
for(int i = 0; i < dim; i++) {
- values[i] = vector.doubleValue(i + 1);
+ values[i] = vector.doubleValue(i);
}
}
@@ -84,20 +87,14 @@ public class SpatialPointLeafEntry extends AbstractLeafEntry implements SpatialE
return values.length;
}
- /**
- * @return the value at the specified dimension
- */
@Override
public double getMin(int dimension) {
- return values[dimension - 1];
+ return values[dimension];
}
- /**
- * @return the value at the specified dimension
- */
@Override
public double getMax(int dimension) {
- return values[dimension - 1];
+ return values[dimension];
}
/**
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/AbstractRStarTree.java b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/AbstractRStarTree.java
index 3bac751c..8ede52d7 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/AbstractRStarTree.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/AbstractRStarTree.java
@@ -80,7 +80,7 @@ public abstract class AbstractRStarTree<N extends AbstractRStarTreeNode<N, E>, E
/**
* Development flag: This will enable some extra integrity checks on the tree.
*/
- protected final static boolean extraIntegrityChecks = false;
+ protected static final boolean EXTRA_INTEGRITY_CHECKS = false;
/**
* The height of this R*-Tree.
@@ -93,7 +93,7 @@ public abstract class AbstractRStarTree<N extends AbstractRStarTreeNode<N, E>, E
public int distanceCalcs = 0;
/**
- * The last inserted entry
+ * The last inserted entry.
*/
E lastInsertedEntry = null;
@@ -103,27 +103,27 @@ public abstract class AbstractRStarTree<N extends AbstractRStarTreeNode<N, E>, E
protected BulkSplit bulkSplitter;
/**
- * The split strategy
+ * The split strategy.
*/
protected SplitStrategy nodeSplitter = TopologicalSplitter.STATIC;
/**
- * The insertion strategy to use
+ * The insertion strategy to use.
*/
protected InsertionStrategy insertionStrategy = LeastOverlapInsertionStrategy.STATIC;
/**
- * Overflow treatment
+ * Overflow treatment.
*/
protected OverflowTreatment overflowTreatment = LimitedReinsertOverflowTreatment.RSTAR_OVERFLOW;
/**
- * Relative minimum fill
+ * Relative minimum fill.
*/
protected double relativeMinFill = 0.4;
/**
- * Constructor
+ * Constructor.
*
* @param pagefile Page file
*/
@@ -132,7 +132,7 @@ public abstract class AbstractRStarTree<N extends AbstractRStarTreeNode<N, E>, E
}
/**
- * Set the bulk loading strategy
+ * Set the bulk loading strategy.
*
* @param bulkSplitter Bulk loading strategy
*/
@@ -155,7 +155,7 @@ public abstract class AbstractRStarTree<N extends AbstractRStarTreeNode<N, E>, E
}
/**
- * Set insertion strategy
+ * Set insertion strategy.
*
* @param insertionStrategy the insertion strategy to set
*/
@@ -200,7 +200,7 @@ public abstract class AbstractRStarTree<N extends AbstractRStarTreeNode<N, E>, E
N node = getNode(subtree.getLastPathComponent().getEntry());
if(node.isLeaf()) {
for(int i = 0; i < node.getNumEntries(); i++) {
- if(((LeafEntry) node.getEntry(i)).getDBID().sameDBID(id)) {
+ if(DBIDUtil.equal(((LeafEntry) node.getEntry(i)).getDBID(), id)) {
return subtree.pathByAddingChild(new TreeIndexPathComponent<E>(node.getEntry(i), i));
}
}
@@ -319,6 +319,8 @@ public abstract class AbstractRStarTree<N extends AbstractRStarTreeNode<N, E>, E
/**
* Initializes this R*-Tree from an existing persistent file.
+ *
+ * {@inheritDoc}
*/
@Override
public void initializeFromFile(TreeIndexHeader header, PageFile<N> file) {
@@ -327,7 +329,7 @@ public abstract class AbstractRStarTree<N extends AbstractRStarTreeNode<N, E>, E
this.height = computeHeight();
if(getLogger().isDebugging()) {
- StringBuffer msg = new StringBuffer();
+ StringBuilder msg = new StringBuilder();
msg.append(getClass());
msg.append("\n height = ").append(height);
getLogger().debugFine(msg.toString());
@@ -454,8 +456,10 @@ public abstract class AbstractRStarTree<N extends AbstractRStarTreeNode<N, E>, E
/**
* Performs a bulk load on this RTree with the specified data. Is called by
* the constructor.
+ *
+ * @param entries Entries to bulk load
*/
- abstract protected void bulkLoad(List<E> entrys);
+ protected abstract void bulkLoad(List<E> entries);
/**
* Returns the height of this R*-Tree.
@@ -480,7 +484,7 @@ public abstract class AbstractRStarTree<N extends AbstractRStarTreeNode<N, E>, E
*
* @return the height of this RTree
*/
- abstract protected int computeHeight();
+ protected abstract int computeHeight();
/**
* Returns true if in the specified node an overflow occurred, false
@@ -489,7 +493,7 @@ public abstract class AbstractRStarTree<N extends AbstractRStarTreeNode<N, E>, E
* @param node the node to be tested for overflow
* @return true if in the specified node an overflow occurred, false otherwise
*/
- abstract protected boolean hasOverflow(N node);
+ protected abstract boolean hasOverflow(N node);
/**
* Returns true if in the specified node an underflow occurred, false
@@ -499,7 +503,7 @@ public abstract class AbstractRStarTree<N extends AbstractRStarTreeNode<N, E>, E
* @return true if in the specified node an underflow occurred, false
* otherwise
*/
- abstract protected boolean hasUnderflow(N node);
+ protected abstract boolean hasUnderflow(N node);
/**
* Creates a new directory entry representing the specified node.
@@ -507,7 +511,7 @@ public abstract class AbstractRStarTree<N extends AbstractRStarTreeNode<N, E>, E
* @param node the node to be represented by the new entry
* @return the newly created directory entry
*/
- abstract protected E createNewDirectoryEntry(N node);
+ protected abstract E createNewDirectoryEntry(N node);
/**
* Creates a new root node that points to the two specified child nodes and
@@ -852,7 +856,7 @@ public abstract class AbstractRStarTree<N extends AbstractRStarTreeNode<N, E>, E
// node is root
else {
- if(hasUnderflow(node) & node.getNumEntries() == 1 && !node.isLeaf()) {
+ if(hasUnderflow(node) && node.getNumEntries() == 1 && !node.isLeaf()) {
N child = getNode(node.getEntry(0));
N newRoot;
if(child.isLeaf()) {
@@ -889,7 +893,7 @@ public abstract class AbstractRStarTree<N extends AbstractRStarTreeNode<N, E>, E
}
/**
- * Determines the entries pointing to the leaf nodes of the specified subtree
+ * Determines the entries pointing to the leaf nodes of the specified subtree.
*
* @param node the subtree
* @param result the result to store the ids in
@@ -914,7 +918,7 @@ public abstract class AbstractRStarTree<N extends AbstractRStarTreeNode<N, E>, E
* Perform additional integrity checks.
*/
public void doExtraIntegrityChecks() {
- if(extraIntegrityChecks) {
+ if(EXTRA_INTEGRITY_CHECKS) {
getRoot().integrityCheck(this);
}
}
@@ -926,7 +930,7 @@ public abstract class AbstractRStarTree<N extends AbstractRStarTreeNode<N, E>, E
*/
@Override
public String toString() {
- StringBuffer result = new StringBuffer();
+ StringBuilder result = new StringBuilder();
int dirNodes = 0;
int leafNodes = 0;
int objects = 0;
@@ -964,7 +968,7 @@ public abstract class AbstractRStarTree<N extends AbstractRStarTreeNode<N, E>, E
result.append(getClass().getName()).append(" has ").append((levels + 1)).append(" levels.\n");
result.append(dirNodes).append(" Directory Knoten (max = ").append(dirCapacity - 1).append(", min = ").append(dirMinimum).append(")\n");
result.append(leafNodes).append(" Daten Knoten (max = ").append(leafCapacity - 1).append(", min = ").append(leafMinimum).append(")\n");
- result.append(objects).append(" ").append(dim).append("-dim. Punkte im Baum \n");
+ result.append(objects).append(' ').append(dim).append("-dim. Punkte im Baum \n");
PageFileUtil.appendPageFileStatistics(result, getPageFileStatistics());
}
else {
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/AbstractRStarTreeFactory.java b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/AbstractRStarTreeFactory.java
index e2bb3abb..ace5ad41 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/AbstractRStarTreeFactory.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/AbstractRStarTreeFactory.java
@@ -37,8 +37,8 @@ import de.lmu.ifi.dbs.elki.index.tree.spatial.rstarvariants.strategies.overflow.
import de.lmu.ifi.dbs.elki.index.tree.spatial.rstarvariants.strategies.split.SplitStrategy;
import de.lmu.ifi.dbs.elki.index.tree.spatial.rstarvariants.strategies.split.TopologicalSplitter;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.IntervalConstraint;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.IntervalConstraint.IntervalBoundary;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.LessConstraint;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.DoubleParameter;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ObjectParameter;
@@ -56,31 +56,31 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ObjectParameter;
* @param <E> Entry type
* @param <I> Index type
*/
-public abstract class AbstractRStarTreeFactory<O extends NumberVector<O, ?>, N extends AbstractRStarTreeNode<N, E>, E extends SpatialEntry, I extends AbstractRStarTree<N, E> & Index> extends TreeIndexFactory<O, I> {
+public abstract class AbstractRStarTreeFactory<O extends NumberVector<?>, N extends AbstractRStarTreeNode<N, E>, E extends SpatialEntry, I extends AbstractRStarTree<N, E> & Index> extends TreeIndexFactory<O, I> {
/**
* Fast-insertion parameter. Optional.
*/
- public static OptionID INSERTION_STRATEGY_ID = OptionID.getOrCreateOptionID("rtree.insertionstrategy", "The strategy to use for object insertion.");
+ public static OptionID INSERTION_STRATEGY_ID = new OptionID("rtree.insertionstrategy", "The strategy to use for object insertion.");
/**
* Split strategy parameter. Optional.
*/
- public static OptionID SPLIT_STRATEGY_ID = OptionID.getOrCreateOptionID("rtree.splitstrategy", "The strategy to use for node splitting.");
+ public static OptionID SPLIT_STRATEGY_ID = new OptionID("rtree.splitstrategy", "The strategy to use for node splitting.");
/**
* Parameter for bulk strategy
*/
- public static final OptionID BULK_SPLIT_ID = OptionID.getOrCreateOptionID("spatial.bulkstrategy", "The class to perform the bulk split with.");
+ public static final OptionID BULK_SPLIT_ID = new OptionID("spatial.bulkstrategy", "The class to perform the bulk split with.");
/**
* Parameter for the relative minimum fill.
*/
- public static final OptionID MINIMUM_FILL_ID = OptionID.getOrCreateOptionID("rtree.minimum-fill", "Minimum relative fill required for data pages.");
+ public static final OptionID MINIMUM_FILL_ID = new OptionID("rtree.minimum-fill", "Minimum relative fill required for data pages.");
/**
* Overflow treatment.
*/
- public static OptionID OVERFLOW_STRATEGY_ID = OptionID.getOrCreateOptionID("rtree.overflowtreatment", "The strategy to use for handling overflows.");
+ public static OptionID OVERFLOW_STRATEGY_ID = new OptionID("rtree.overflowtreatment", "The strategy to use for handling overflows.");
/**
* Strategy to find the insertion node with.
@@ -140,7 +140,7 @@ public abstract class AbstractRStarTreeFactory<O extends NumberVector<O, ?>, N e
*
* @apiviz.exclude
*/
- public static abstract class Parameterizer<O extends NumberVector<O, ?>> extends TreeIndexFactory.Parameterizer<O> {
+ public abstract static class Parameterizer<O extends NumberVector<?>> extends TreeIndexFactory.Parameterizer<O> {
/**
* Insertion strategy
*/
@@ -170,19 +170,21 @@ public abstract class AbstractRStarTreeFactory<O extends NumberVector<O, ?>, N e
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
ObjectParameter<InsertionStrategy> insertionStrategyP = new ObjectParameter<InsertionStrategy>(INSERTION_STRATEGY_ID, InsertionStrategy.class, CombinedInsertionStrategy.class);
- if(config.grab(insertionStrategyP)) {
+ if (config.grab(insertionStrategyP)) {
insertionStrategy = insertionStrategyP.instantiateClass(config);
}
ObjectParameter<SplitStrategy> splitStrategyP = new ObjectParameter<SplitStrategy>(SPLIT_STRATEGY_ID, SplitStrategy.class, TopologicalSplitter.class);
- if(config.grab(splitStrategyP)) {
+ if (config.grab(splitStrategyP)) {
nodeSplitter = splitStrategyP.instantiateClass(config);
}
- DoubleParameter minimumFillP = new DoubleParameter(MINIMUM_FILL_ID, new IntervalConstraint(0.0, IntervalBoundary.OPEN, 0.5, IntervalBoundary.OPEN), 0.4);
+ DoubleParameter minimumFillP = new DoubleParameter(MINIMUM_FILL_ID, 0.4);
+ minimumFillP.addConstraint(new GreaterConstraint(0.0));
+ minimumFillP.addConstraint(new LessConstraint(0.5));
if (config.grab(minimumFillP)) {
minimumFill = minimumFillP.getValue();
}
ObjectParameter<OverflowTreatment> overflowP = new ObjectParameter<OverflowTreatment>(OVERFLOW_STRATEGY_ID, OverflowTreatment.class, LimitedReinsertOverflowTreatment.class);
- if(config.grab(overflowP)) {
+ if (config.grab(overflowP)) {
overflowTreatment = overflowP.instantiateClass(config);
}
configBulkLoad(config);
@@ -195,7 +197,7 @@ public abstract class AbstractRStarTreeFactory<O extends NumberVector<O, ?>, N e
*/
protected void configBulkLoad(Parameterization config) {
ObjectParameter<BulkSplit> bulkSplitP = new ObjectParameter<BulkSplit>(BULK_SPLIT_ID, BulkSplit.class, true);
- if(config.grab(bulkSplitP)) {
+ if (config.grab(bulkSplitP)) {
bulkSplitter = bulkSplitP.instantiateClass(config);
}
}
@@ -203,4 +205,4 @@ public abstract class AbstractRStarTreeFactory<O extends NumberVector<O, ?>, N e
@Override
protected abstract AbstractRStarTreeFactory<O, ?, ?, ?> makeInstance();
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/AbstractRStarTreeNode.java b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/AbstractRStarTreeNode.java
index 46e3003e..80666ebb 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/AbstractRStarTreeNode.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/AbstractRStarTreeNode.java
@@ -96,7 +96,7 @@ public abstract class AbstractRStarTreeNode<N extends AbstractRStarTreeNode<N, E
if(se.hasMBR()) {
final int dim = se.getDimensionality();
// Test for changes
- for(int i = 1; i <= dim; i++) {
+ for(int i = 0; i < dim; i++) {
if(Math.abs(se.getMin(i) - mbr.getMin(i)) > Float.MIN_NORMAL) {
changed = true;
break;
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/NonFlatRStarTree.java b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/NonFlatRStarTree.java
index 41882ce2..fd7d3d8b 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/NonFlatRStarTree.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/NonFlatRStarTree.java
@@ -120,7 +120,7 @@ public abstract class NonFlatRStarTree<N extends AbstractRStarTreeNode<N, E>, E
initialize(spatialObjects.get(0));
}
- StringBuffer msg = getLogger().isDebuggingFine() ? new StringBuffer() : null;
+ StringBuilder msg = getLogger().isDebuggingFine() ? new StringBuilder() : null;
// Tiny tree that fits into a single page
if(spatialObjects.size() <= leafCapacity) {
@@ -165,8 +165,8 @@ public abstract class NonFlatRStarTree<N extends AbstractRStarTreeNode<N, E>, E
}
if(msg != null) {
msg.append("\n height = ").append(getHeight());
- msg.append("\n root " + getRoot());
- getLogger().debugFine(msg.toString() + "\n");
+ msg.append("\n root ").append(getRoot());
+ getLogger().debugFine(msg.toString());
}
}
@@ -228,9 +228,9 @@ public abstract class NonFlatRStarTree<N extends AbstractRStarTreeNode<N, E>, E
// write to file
writeNode(root);
if(getLogger().isDebuggingFiner()) {
- StringBuffer msg = new StringBuffer();
+ StringBuilder msg = new StringBuilder();
msg.append("pageNo ").append(root.getPageID());
- getLogger().debugFiner(msg.toString() + "\n");
+ getLogger().debugFiner(msg.toString());
}
return root;
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/deliclu/DeLiCluLeafEntry.java b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/deliclu/DeLiCluLeafEntry.java
index 9848f121..bc701185 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/deliclu/DeLiCluLeafEntry.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/deliclu/DeLiCluLeafEntry.java
@@ -29,23 +29,23 @@ import de.lmu.ifi.dbs.elki.index.tree.spatial.SpatialPointLeafEntry;
/**
* Defines the requirements for a leaf entry in an DeLiClu-Tree node.
- * Additionally to a leaf entry in an R*-Tree two boolean flags that indicate whether this entry's node
- * contains handled or unhandled data objects.
- *
+ * Additionally to a leaf entry in an R*-Tree two boolean flags that indicate
+ * whether this entry's node contains handled or unhandled data objects.
+ *
* @author Elke Achtert
*/
public class DeLiCluLeafEntry extends SpatialPointLeafEntry implements DeLiCluEntry {
private static final long serialVersionUID = 1;
/**
- * Indicates that the node (or its child nodes) which is represented by this entry
- * contains handled data objects.
+ * Indicates that the node (or its child nodes) which is represented by this
+ * entry contains handled data objects.
*/
private boolean hasHandled;
/**
- * Indicates that the node (or its child nodes) which is represented by this entry
- * contains unhandled data objects.
+ * Indicates that the node (or its child nodes) which is represented by this
+ * entry contains unhandled data objects.
*/
private boolean hasUnhandled;
@@ -53,16 +53,16 @@ public class DeLiCluLeafEntry extends SpatialPointLeafEntry implements DeLiCluEn
* Empty constructor for serialization purposes.
*/
public DeLiCluLeafEntry() {
- // empty constructor
+ // empty constructor
}
/**
* Constructs a new LeafEntry object with the given parameters.
- *
- * @param id the unique id of the underlying data object
+ *
+ * @param id the unique id of the underlying data object
* @param vector the vector to store
*/
- public DeLiCluLeafEntry(DBID id, NumberVector<?,?> vector) {
+ public DeLiCluLeafEntry(DBID id, NumberVector<?> vector) {
super(id, vector);
this.hasHandled = false;
this.hasUnhandled = true;
@@ -90,7 +90,7 @@ public class DeLiCluLeafEntry extends SpatialPointLeafEntry implements DeLiCluEn
/**
* Returns the id as a string representation of this entry.
- *
+ *
* @return a string representation of this entry
*/
@Override
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/deliclu/DeLiCluTree.java b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/deliclu/DeLiCluTree.java
index 6f4f782d..0cd74a14 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/deliclu/DeLiCluTree.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/deliclu/DeLiCluTree.java
@@ -48,7 +48,7 @@ public class DeLiCluTree extends NonFlatRStarTree<DeLiCluNode, DeLiCluEntry> {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(DeLiCluTree.class);
+ private static final Logging LOG = Logging.getLogger(DeLiCluTree.class);
/**
* Holds the ids of the expanded nodes.
@@ -168,6 +168,6 @@ public class DeLiCluTree extends NonFlatRStarTree<DeLiCluNode, DeLiCluEntry> {
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/deliclu/DeLiCluTreeFactory.java b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/deliclu/DeLiCluTreeFactory.java
index 3f9627a9..b64192c1 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/deliclu/DeLiCluTreeFactory.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/deliclu/DeLiCluTreeFactory.java
@@ -42,7 +42,7 @@ import de.lmu.ifi.dbs.elki.persistent.PageFile;
*
* @param <O> Object type
*/
-public class DeLiCluTreeFactory<O extends NumberVector<O, ?>> extends AbstractRStarTreeFactory<O, DeLiCluNode, DeLiCluEntry, DeLiCluTreeIndex<O>> {
+public class DeLiCluTreeFactory<O extends NumberVector<?>> extends AbstractRStarTreeFactory<O, DeLiCluNode, DeLiCluEntry, DeLiCluTreeIndex<O>> {
/**
* Constructor.
*
@@ -82,7 +82,7 @@ public class DeLiCluTreeFactory<O extends NumberVector<O, ?>> extends AbstractRS
*
* @apiviz.exclude
*/
- public static class Parameterizer<O extends NumberVector<O, ?>> extends AbstractRStarTreeFactory.Parameterizer<O> {
+ public static class Parameterizer<O extends NumberVector<?>> extends AbstractRStarTreeFactory.Parameterizer<O> {
@Override
protected DeLiCluTreeFactory<O> makeInstance() {
return new DeLiCluTreeFactory<O>(fileName, pageSize, cacheSize, bulkSplitter, insertionStrategy, nodeSplitter, overflowTreatment, minimumFill);
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/deliclu/DeLiCluTreeIndex.java b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/deliclu/DeLiCluTreeIndex.java
index dd3b4d6b..b1216a51 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/deliclu/DeLiCluTreeIndex.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/deliclu/DeLiCluTreeIndex.java
@@ -29,6 +29,8 @@ import java.util.List;
import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.database.query.distance.SpatialDistanceQuery;
@@ -52,9 +54,9 @@ import de.lmu.ifi.dbs.elki.utilities.exceptions.AbortException;
*
* @param <O> Object type
*/
-public class DeLiCluTreeIndex<O extends NumberVector<?, ?>> extends DeLiCluTree implements KNNIndex<O>, RangeIndex<O> {
+public class DeLiCluTreeIndex<O extends NumberVector<?>> extends DeLiCluTree implements KNNIndex<O>, RangeIndex<O> {
/**
- * The relation we index
+ * The relation we index.
*/
private Relation<O> relation;
@@ -73,7 +75,7 @@ public class DeLiCluTreeIndex<O extends NumberVector<?, ?>> extends DeLiCluTree
/**
* The appropriate logger for this index.
*/
- private static final Logging logger = Logging.getLogger(DeLiCluTreeIndex.class);
+ private static final Logging LOG = Logging.getLogger(DeLiCluTreeIndex.class);
/**
* Creates a new leaf entry representing the specified data object.
@@ -93,14 +95,14 @@ public class DeLiCluTreeIndex<O extends NumberVector<?, ?>> extends DeLiCluTree
* @return the path of node ids from the root to the objects's parent
*/
public synchronized List<TreeIndexPathComponent<DeLiCluEntry>> setHandled(DBID id, O obj) {
- if(logger.isDebugging()) {
- logger.debugFine("setHandled " + id + ", " + obj + "\n");
+ if (LOG.isDebugging()) {
+ LOG.debugFine("setHandled " + id + ", " + obj + "\n");
}
// find the leaf node containing o
IndexTreePath<DeLiCluEntry> pathToObject = findPathToObject(getRootPath(), obj, id);
- if(pathToObject == null) {
+ if (pathToObject == null) {
throw new AbortException("Object not found in setHandled.");
}
@@ -109,12 +111,12 @@ public class DeLiCluTreeIndex<O extends NumberVector<?, ?>> extends DeLiCluTree
entry.setHasHandled(true);
entry.setHasUnhandled(false);
- for(IndexTreePath<DeLiCluEntry> path = pathToObject; path.getParentPath() != null; path = path.getParentPath()) {
+ for (IndexTreePath<DeLiCluEntry> path = pathToObject; path.getParentPath() != null; path = path.getParentPath()) {
DeLiCluEntry parentEntry = path.getParentPath().getLastPathComponent().getEntry();
DeLiCluNode node = getNode(parentEntry);
boolean hasHandled = false;
boolean hasUnhandled = false;
- for(int i = 0; i < node.getNumEntries(); i++) {
+ for (int i = 0; i < node.getNumEntries(); i++) {
final DeLiCluEntry nodeEntry = node.getEntry(i);
hasHandled = hasHandled || nodeEntry.hasHandled();
hasUnhandled = hasUnhandled || nodeEntry.hasUnhandled();
@@ -132,8 +134,8 @@ public class DeLiCluTreeIndex<O extends NumberVector<?, ?>> extends DeLiCluTree
* @param id the object id that was inserted
*/
@Override
- public final void insert(DBID id) {
- insertLeaf(createNewLeafEntry(id));
+ public final void insert(DBIDRef id) {
+ insertLeaf(createNewLeafEntry(DBIDUtil.deref(id)));
}
/**
@@ -144,21 +146,20 @@ public class DeLiCluTreeIndex<O extends NumberVector<?, ?>> extends DeLiCluTree
*/
@Override
public final void insertAll(DBIDs ids) {
- if(ids.isEmpty() || (ids.size() == 1)) {
+ if (ids.isEmpty() || (ids.size() == 1)) {
return;
}
// Make an example leaf
- if(canBulkLoad()) {
+ if (canBulkLoad()) {
List<DeLiCluEntry> leafs = new ArrayList<DeLiCluEntry>(ids.size());
for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
- leafs.add(createNewLeafEntry(iter.getDBID()));
+ leafs.add(createNewLeafEntry(DBIDUtil.deref(iter)));
}
bulkLoad(leafs);
- }
- else {
+ } else {
for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
- insert(iter.getDBID());
+ insert(iter);
}
}
@@ -172,11 +173,11 @@ public class DeLiCluTreeIndex<O extends NumberVector<?, ?>> extends DeLiCluTree
* false otherwise
*/
@Override
- public final boolean delete(DBID id) {
+ public final boolean delete(DBIDRef id) {
// find the leaf node containing o
O obj = relation.get(id);
IndexTreePath<DeLiCluEntry> deletionPath = findPathToObject(getRootPath(), obj, id);
- if(deletionPath == null) {
+ if (deletionPath == null) {
return false;
}
deletePath(deletionPath);
@@ -186,18 +187,18 @@ public class DeLiCluTreeIndex<O extends NumberVector<?, ?>> extends DeLiCluTree
@Override
public void deleteAll(DBIDs ids) {
for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
- delete(iter.getDBID());
+ delete(DBIDUtil.deref(iter));
}
}
@Override
public <D extends Distance<D>> RangeQuery<O, D> getRangeQuery(DistanceQuery<O, D> distanceQuery, Object... hints) {
// Query on the relation we index
- if(distanceQuery.getRelation() != relation) {
+ if (distanceQuery.getRelation() != relation) {
return null;
}
// Can we support this distance function - spatial distances only!
- if(!(distanceQuery instanceof SpatialDistanceQuery)) {
+ if (!(distanceQuery instanceof SpatialDistanceQuery)) {
return null;
}
SpatialDistanceQuery<O, D> dq = (SpatialDistanceQuery<O, D>) distanceQuery;
@@ -207,11 +208,11 @@ public class DeLiCluTreeIndex<O extends NumberVector<?, ?>> extends DeLiCluTree
@Override
public <D extends Distance<D>> KNNQuery<O, D> getKNNQuery(DistanceQuery<O, D> distanceQuery, Object... hints) {
// Query on the relation we index
- if(distanceQuery.getRelation() != relation) {
+ if (distanceQuery.getRelation() != relation) {
return null;
}
// Can we support this distance function - spatial distances only!
- if(!(distanceQuery instanceof SpatialDistanceQuery)) {
+ if (!(distanceQuery instanceof SpatialDistanceQuery)) {
return null;
}
SpatialDistanceQuery<O, D> dq = (SpatialDistanceQuery<O, D>) distanceQuery;
@@ -230,6 +231,6 @@ public class DeLiCluTreeIndex<O extends NumberVector<?, ?>> extends DeLiCluTree
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/query/DoubleDistanceRStarTreeKNNQuery.java b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/query/DoubleDistanceRStarTreeKNNQuery.java
index f0acc233..6d539f25 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/query/DoubleDistanceRStarTreeKNNQuery.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/query/DoubleDistanceRStarTreeKNNQuery.java
@@ -38,11 +38,11 @@ import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.ids.ModifiableDBIDs;
-import de.lmu.ifi.dbs.elki.database.query.DoubleDistanceResultPair;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.database.query.knn.AbstractDistanceKNNQuery;
-import de.lmu.ifi.dbs.elki.database.query.knn.KNNResult;
import de.lmu.ifi.dbs.elki.distance.distancefunction.SpatialPrimitiveDoubleDistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DoubleDistanceKNNHeap;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DoubleDistanceKNNList;
import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
import de.lmu.ifi.dbs.elki.index.tree.DirectoryEntry;
import de.lmu.ifi.dbs.elki.index.tree.LeafEntry;
@@ -51,7 +51,6 @@ import de.lmu.ifi.dbs.elki.index.tree.spatial.SpatialEntry;
import de.lmu.ifi.dbs.elki.index.tree.spatial.rstarvariants.AbstractRStarTree;
import de.lmu.ifi.dbs.elki.index.tree.spatial.rstarvariants.AbstractRStarTreeNode;
import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.Heap;
-import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.KNNHeap;
import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
/**
@@ -102,8 +101,8 @@ public class DoubleDistanceRStarTreeKNNQuery<O extends SpatialComparable> extend
* @param object the query object
* @param knnList the knn list containing the result
*/
- protected void doKNNQuery(O object, KNNHeap<DoubleDistance> knnList) {
- final Heap<DoubleDistanceSearchCandidate> pq = new Heap<DoubleDistanceSearchCandidate>(Math.min(knnList.getK() * 2, 20));
+ protected void doKNNQuery(O object, DoubleDistanceKNNHeap knnList) {
+ final Heap<DoubleDistanceSearchCandidate> pq = new Heap<DoubleDistanceSearchCandidate>(Math.min(knnList.getK() << 1, 20));
// push root
pq.add(new DoubleDistanceSearchCandidate(0.0, tree.getRootID()));
@@ -120,7 +119,7 @@ public class DoubleDistanceRStarTreeKNNQuery<O extends SpatialComparable> extend
}
}
- private double expandNode(O object, KNNHeap<DoubleDistance> knnList, final Heap<DoubleDistanceSearchCandidate> pq, double maxDist, final Integer nodeID) {
+ private double expandNode(O object, DoubleDistanceKNNHeap knnList, final Heap<DoubleDistanceSearchCandidate> pq, double maxDist, final int nodeID) {
AbstractRStarTreeNode<?, ?> node = tree.getNode(nodeID);
// data node
if(node.isLeaf()) {
@@ -129,8 +128,8 @@ public class DoubleDistanceRStarTreeKNNQuery<O extends SpatialComparable> extend
double distance = distanceFunction.doubleMinDist(entry, object);
tree.distanceCalcs++;
if(distance <= maxDist) {
- knnList.add(new DoubleDistanceResultPair(distance, ((LeafEntry) entry).getDBID()));
- maxDist = knnList.getKNNDistance().doubleValue();
+ knnList.add(distance, ((LeafEntry) entry).getDBID());
+ maxDist = knnList.doubleKNNDistance();
}
}
}
@@ -160,20 +159,20 @@ public class DoubleDistanceRStarTreeKNNQuery<O extends SpatialComparable> extend
* @param node the node for which the query should be performed
* @param knnLists a map containing the knn lists for each query objects
*/
- protected void batchNN(AbstractRStarTreeNode<?, ?> node, Map<DBID, KNNHeap<DoubleDistance>> knnLists) {
+ protected void batchNN(AbstractRStarTreeNode<?, ?> node, Map<DBID, DoubleDistanceKNNHeap> knnLists) {
if(node.isLeaf()) {
for(int i = 0; i < node.getNumEntries(); i++) {
SpatialEntry p = node.getEntry(i);
- for(Entry<DBID, KNNHeap<DoubleDistance>> ent : knnLists.entrySet()) {
+ for(Entry<DBID, DoubleDistanceKNNHeap> ent : knnLists.entrySet()) {
final DBID q = ent.getKey();
- final KNNHeap<DoubleDistance> knns_q = ent.getValue();
- DoubleDistance knn_q_maxDist = knns_q.getKNNDistance();
+ final DoubleDistanceKNNHeap knns_q = ent.getValue();
+ double knn_q_maxDist = knns_q.doubleKNNDistance();
DBID pid = ((LeafEntry) p).getDBID();
// FIXME: objects are NOT accessible by DBID in a plain rtree context!
- DoubleDistance dist_pq = distanceFunction.distance(relation.get(pid), relation.get(q));
+ double dist_pq = distanceFunction.doubleDistance(relation.get(pid), relation.get(q));
tree.distanceCalcs++;
- if(dist_pq.compareTo(knn_q_maxDist) <= 0) {
+ if(dist_pq <= knn_q_maxDist) {
knns_q.add(dist_pq, pid);
}
}
@@ -187,13 +186,13 @@ public class DoubleDistanceRStarTreeKNNQuery<O extends SpatialComparable> extend
List<DoubleDistanceEntry> entries = getSortedEntries(node, ids);
for(DoubleDistanceEntry distEntry : entries) {
double minDist = distEntry.distance;
- for(Entry<DBID, KNNHeap<DoubleDistance>> ent : knnLists.entrySet()) {
- final KNNHeap<DoubleDistance> knns_q = ent.getValue();
- double knn_q_maxDist = knns_q.getKNNDistance().doubleValue();
+ for(Entry<DBID, DoubleDistanceKNNHeap> ent : knnLists.entrySet()) {
+ final DoubleDistanceKNNHeap knns_q = ent.getValue();
+ double knn_q_maxDist = knns_q.doubleKNNDistance();
if(minDist <= knn_q_maxDist) {
SpatialEntry entry = distEntry.entry;
- AbstractRStarTreeNode<?, ?> child = tree.getNode(((DirectoryEntry) entry).getPageID());
+ AbstractRStarTreeNode<?, ?> child = tree.getNode(((DirectoryEntry) entry).getPageID().intValue());
batchNN(child, knnLists);
break;
}
@@ -264,47 +263,41 @@ public class DoubleDistanceRStarTreeKNNQuery<O extends SpatialComparable> extend
}
@Override
- public KNNResult<DoubleDistance> getKNNForObject(O obj, int k) {
+ public DoubleDistanceKNNList getKNNForObject(O obj, int k) {
if(k < 1) {
throw new IllegalArgumentException("At least one enumeration has to be requested!");
}
- final KNNHeap<DoubleDistance> knnList = new KNNHeap<DoubleDistance>(k, distanceFunction.getDistanceFactory().infiniteDistance());
+ final DoubleDistanceKNNHeap knnList = new DoubleDistanceKNNHeap(k);
doKNNQuery(obj, knnList);
return knnList.toKNNList();
}
@Override
- public KNNResult<DoubleDistance> getKNNForDBID(DBIDRef id, int k) {
+ public DoubleDistanceKNNList getKNNForDBID(DBIDRef id, int k) {
return getKNNForObject(relation.get(id), k);
}
@Override
- public List<KNNResult<DoubleDistance>> getKNNForBulkDBIDs(ArrayDBIDs ids, int k) {
+ public List<DoubleDistanceKNNList> getKNNForBulkDBIDs(ArrayDBIDs ids, int k) {
if(k < 1) {
throw new IllegalArgumentException("At least one enumeration has to be requested!");
}
// While this works, it seems to be slow at least for large sets!
- final Map<DBID, KNNHeap<DoubleDistance>> knnLists = new HashMap<DBID, KNNHeap<DoubleDistance>>(ids.size());
+ final Map<DBID, DoubleDistanceKNNHeap> knnLists = new HashMap<DBID, DoubleDistanceKNNHeap>(ids.size());
for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
- DBID id = iter.getDBID();
- knnLists.put(id, new KNNHeap<DoubleDistance>(k, distanceFunction.getDistanceFactory().infiniteDistance()));
+ DBID id = DBIDUtil.deref(iter);
+ knnLists.put(id, new DoubleDistanceKNNHeap(k));
}
batchNN(tree.getRoot(), knnLists);
- List<KNNResult<DoubleDistance>> result = new ArrayList<KNNResult<DoubleDistance>>();
+ List<DoubleDistanceKNNList> result = new ArrayList<DoubleDistanceKNNList>();
for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
- DBID id = iter.getDBID();
+ DBID id = DBIDUtil.deref(iter);
result.add(knnLists.get(id).toKNNList());
}
return result;
}
-
- @Override
- public void getKNNForBulkHeaps(Map<DBID, KNNHeap<DoubleDistance>> heaps) {
- AbstractRStarTreeNode<?, ?> root = tree.getRoot();
- batchNN(root, heaps);
- }
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/query/DoubleDistanceRStarTreeRangeQuery.java b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/query/DoubleDistanceRStarTreeRangeQuery.java
index 1a861c3e..715b9552 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/query/DoubleDistanceRStarTreeRangeQuery.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/query/DoubleDistanceRStarTreeRangeQuery.java
@@ -23,16 +23,13 @@ package de.lmu.ifi.dbs.elki.index.tree.spatial.rstarvariants.query;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.util.Collections;
-
import de.lmu.ifi.dbs.elki.data.spatial.SpatialComparable;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
-import de.lmu.ifi.dbs.elki.database.query.DistanceDBIDResult;
-import de.lmu.ifi.dbs.elki.database.query.DoubleDistanceResultPair;
-import de.lmu.ifi.dbs.elki.database.query.GenericDistanceDBIDList;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.database.query.range.AbstractDistanceRangeQuery;
import de.lmu.ifi.dbs.elki.distance.distancefunction.SpatialPrimitiveDoubleDistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DoubleDistanceDBIDList;
import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
import de.lmu.ifi.dbs.elki.index.tree.DirectoryEntry;
import de.lmu.ifi.dbs.elki.index.tree.LeafEntry;
@@ -90,8 +87,8 @@ public class DoubleDistanceRStarTreeRangeQuery<O extends SpatialComparable> exte
* @param epsilon Query range
* @return Objects contained in query range.
*/
- protected GenericDistanceDBIDList<DoubleDistance> doRangeQuery(O object, double epsilon) {
- final GenericDistanceDBIDList<DoubleDistance> result = new GenericDistanceDBIDList<DoubleDistance>();
+ protected DoubleDistanceDBIDList doRangeQuery(O object, double epsilon) {
+ final DoubleDistanceDBIDList result = new DoubleDistanceDBIDList();
final Heap<DoubleDistanceSearchCandidate> pq = new Heap<DoubleDistanceSearchCandidate>();
// push root
@@ -104,7 +101,7 @@ public class DoubleDistanceRStarTreeRangeQuery<O extends SpatialComparable> exte
break;
}
- AbstractRStarTreeNode<?, ?> node = tree.getNode(pqNode.nodeID);
+ AbstractRStarTreeNode<?, ?> node = tree.getNode(pqNode.nodeID.intValue());
final int numEntries = node.getNumEntries();
for(int i = 0; i < numEntries; i++) {
@@ -113,7 +110,7 @@ public class DoubleDistanceRStarTreeRangeQuery<O extends SpatialComparable> exte
if(distance <= epsilon) {
if(node.isLeaf()) {
LeafEntry entry = (LeafEntry) node.getEntry(i);
- result.add(new DoubleDistanceResultPair(distance, entry.getDBID()));
+ result.add(distance, entry.getDBID());
}
else {
DirectoryEntry entry = (DirectoryEntry) node.getEntry(i);
@@ -124,7 +121,7 @@ public class DoubleDistanceRStarTreeRangeQuery<O extends SpatialComparable> exte
}
// sort the result according to the distances
- Collections.sort(result);
+ result.sort();
return result;
}
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/query/GenericRStarTreeKNNQuery.java b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/query/GenericRStarTreeKNNQuery.java
index 09ebb61a..ed7f5949 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/query/GenericRStarTreeKNNQuery.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/query/GenericRStarTreeKNNQuery.java
@@ -40,9 +40,11 @@ import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.ids.ModifiableDBIDs;
import de.lmu.ifi.dbs.elki.database.query.distance.SpatialDistanceQuery;
import de.lmu.ifi.dbs.elki.database.query.knn.AbstractDistanceKNNQuery;
-import de.lmu.ifi.dbs.elki.database.query.knn.KNNResult;
import de.lmu.ifi.dbs.elki.distance.DistanceUtil;
import de.lmu.ifi.dbs.elki.distance.distancefunction.SpatialPrimitiveDistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNHeap;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNUtil;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
import de.lmu.ifi.dbs.elki.index.tree.DirectoryEntry;
import de.lmu.ifi.dbs.elki.index.tree.DistanceEntry;
@@ -52,7 +54,6 @@ import de.lmu.ifi.dbs.elki.index.tree.spatial.SpatialEntry;
import de.lmu.ifi.dbs.elki.index.tree.spatial.rstarvariants.AbstractRStarTree;
import de.lmu.ifi.dbs.elki.index.tree.spatial.rstarvariants.AbstractRStarTreeNode;
import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.Heap;
-import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.KNNHeap;
import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
/**
@@ -103,7 +104,7 @@ public class GenericRStarTreeKNNQuery<O extends SpatialComparable, D extends Dis
* @param knnList the knn list containing the result
*/
protected void doKNNQuery(O object, KNNHeap<D> knnList) {
- final Heap<GenericDistanceSearchCandidate<D>> pq = new Heap<GenericDistanceSearchCandidate<D>>(Math.min(knnList.getK() * 2, 20));
+ final Heap<GenericDistanceSearchCandidate<D>> pq = new Heap<GenericDistanceSearchCandidate<D>>(Math.min(knnList.getK() << 1, 20));
// push root
pq.add(new GenericDistanceSearchCandidate<D>(distanceFunction.getDistanceFactory().nullDistance(), tree.getRootID()));
@@ -120,7 +121,7 @@ public class GenericRStarTreeKNNQuery<O extends SpatialComparable, D extends Dis
}
}
- private D expandNode(O object, KNNHeap<D> knnList, final Heap<GenericDistanceSearchCandidate<D>> pq, D maxDist, final Integer nodeID) {
+ private D expandNode(O object, KNNHeap<D> knnList, final Heap<GenericDistanceSearchCandidate<D>> pq, D maxDist, final int nodeID) {
AbstractRStarTreeNode<?, ?> node = tree.getNode(nodeID);
// data node
if(node.isLeaf()) {
@@ -192,7 +193,7 @@ public class GenericRStarTreeKNNQuery<O extends SpatialComparable, D extends Dis
if(minDist.compareTo(knn_q_maxDist) <= 0) {
SpatialEntry entry = distEntry.getEntry();
- AbstractRStarTreeNode<?, ?> child = tree.getNode(((DirectoryEntry) entry).getPageID());
+ AbstractRStarTreeNode<?, ?> child = tree.getNode(((DirectoryEntry) entry).getPageID().intValue());
batchNN(child, knnLists);
break;
}
@@ -201,12 +202,6 @@ public class GenericRStarTreeKNNQuery<O extends SpatialComparable, D extends Dis
}
}
- @Override
- public void getKNNForBulkHeaps(Map<DBID, KNNHeap<D>> heaps) {
- AbstractRStarTreeNode<?, ?> root = tree.getRoot();
- batchNN(root, heaps);
- }
-
/**
* Sorts the entries of the specified node according to their minimum distance
* to the specified objects.
@@ -238,7 +233,7 @@ public class GenericRStarTreeKNNQuery<O extends SpatialComparable, D extends Dis
throw new IllegalArgumentException("At least one enumeration has to be requested!");
}
- final KNNHeap<D> knnList = new KNNHeap<D>(k, distanceFunction.getDistanceFactory().infiniteDistance());
+ final KNNHeap<D> knnList = KNNUtil.newHeap(distanceFunction, k);
doKNNQuery(obj, knnList);
return knnList.toKNNList();
}
@@ -256,14 +251,14 @@ public class GenericRStarTreeKNNQuery<O extends SpatialComparable, D extends Dis
// While this works, it seems to be slow at least for large sets!
final Map<DBID, KNNHeap<D>> knnLists = new HashMap<DBID, KNNHeap<D>>(ids.size());
for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
- knnLists.put(iter.getDBID(), new KNNHeap<D>(k, distanceFunction.getDistanceFactory().infiniteDistance()));
+ knnLists.put(DBIDUtil.deref(iter), KNNUtil.newHeap(distanceFunction, k));
}
batchNN(tree.getRoot(), knnLists);
List<KNNResult<D>> result = new ArrayList<KNNResult<D>>();
for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
- result.add(knnLists.get(iter.getDBID()).toKNNList());
+ result.add(knnLists.get(DBIDUtil.deref(iter)).toKNNList());
}
return result;
}
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/query/GenericRStarTreeRangeQuery.java b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/query/GenericRStarTreeRangeQuery.java
index 3b312ed7..a5232b30 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/query/GenericRStarTreeRangeQuery.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/query/GenericRStarTreeRangeQuery.java
@@ -23,16 +23,13 @@ package de.lmu.ifi.dbs.elki.index.tree.spatial.rstarvariants.query;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.util.Collections;
-
import de.lmu.ifi.dbs.elki.data.spatial.SpatialComparable;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
-import de.lmu.ifi.dbs.elki.database.query.DistanceDBIDResult;
-import de.lmu.ifi.dbs.elki.database.query.GenericDistanceDBIDList;
-import de.lmu.ifi.dbs.elki.database.query.GenericDistanceResultPair;
import de.lmu.ifi.dbs.elki.database.query.distance.SpatialDistanceQuery;
import de.lmu.ifi.dbs.elki.database.query.range.AbstractDistanceRangeQuery;
import de.lmu.ifi.dbs.elki.distance.distancefunction.SpatialPrimitiveDistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.GenericDistanceDBIDList;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
import de.lmu.ifi.dbs.elki.index.tree.DirectoryEntry;
import de.lmu.ifi.dbs.elki.index.tree.LeafEntry;
@@ -89,7 +86,7 @@ public class GenericRStarTreeRangeQuery<O extends SpatialComparable, D extends D
* @param epsilon Query range
* @return Objects contained in query range.
*/
- protected GenericDistanceDBIDList<D> doRangeQuery(O object, D epsilon) {
+ protected DistanceDBIDResult<D> doRangeQuery(O object, D epsilon) {
final GenericDistanceDBIDList<D> result = new GenericDistanceDBIDList<D>();
final Heap<GenericDistanceSearchCandidate<D>> pq = new Heap<GenericDistanceSearchCandidate<D>>();
@@ -103,7 +100,7 @@ public class GenericRStarTreeRangeQuery<O extends SpatialComparable, D extends D
break;
}
- AbstractRStarTreeNode<?, ?> node = tree.getNode(pqNode.nodeID);
+ AbstractRStarTreeNode<?, ?> node = tree.getNode(pqNode.nodeID.intValue());
final int numEntries = node.getNumEntries();
for(int i = 0; i < numEntries; i++) {
@@ -111,7 +108,7 @@ public class GenericRStarTreeRangeQuery<O extends SpatialComparable, D extends D
if(distance.compareTo(epsilon) <= 0) {
if(node.isLeaf()) {
LeafEntry entry = (LeafEntry) node.getEntry(i);
- result.add(new GenericDistanceResultPair<D>(distance, entry.getDBID()));
+ result.add(distance, entry.getDBID());
}
else {
DirectoryEntry entry = (DirectoryEntry) node.getEntry(i);
@@ -122,7 +119,7 @@ public class GenericRStarTreeRangeQuery<O extends SpatialComparable, D extends D
}
// sort the result according to the distances
- Collections.sort(result);
+ result.sort();
return result;
}
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/rstar/RStarTree.java b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/rstar/RStarTree.java
index 53b32c6b..d63d77cb 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/rstar/RStarTree.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/rstar/RStarTree.java
@@ -48,7 +48,7 @@ public class RStarTree extends NonFlatRStarTree<RStarTreeNode, SpatialEntry> {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(RStarTree.class);
+ private static final Logging LOG = Logging.getLogger(RStarTree.class);
/**
* Constructor.
@@ -91,6 +91,6 @@ public class RStarTree extends NonFlatRStarTree<RStarTreeNode, SpatialEntry> {
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/rstar/RStarTreeFactory.java b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/rstar/RStarTreeFactory.java
index 79aac0bd..da7c03fd 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/rstar/RStarTreeFactory.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/rstar/RStarTreeFactory.java
@@ -43,7 +43,7 @@ import de.lmu.ifi.dbs.elki.persistent.PageFile;
*
* @param <O> Object type
*/
-public class RStarTreeFactory<O extends NumberVector<O, ?>> extends AbstractRStarTreeFactory<O, RStarTreeNode, SpatialEntry, RStarTreeIndex<O>> {
+public class RStarTreeFactory<O extends NumberVector<?>> extends AbstractRStarTreeFactory<O, RStarTreeNode, SpatialEntry, RStarTreeIndex<O>> {
/**
* Constructor.
*
@@ -83,7 +83,7 @@ public class RStarTreeFactory<O extends NumberVector<O, ?>> extends AbstractRSta
*
* @apiviz.exclude
*/
- public static class Parameterizer<O extends NumberVector<O, ?>> extends AbstractRStarTreeFactory.Parameterizer<O> {
+ public static class Parameterizer<O extends NumberVector<?>> extends AbstractRStarTreeFactory.Parameterizer<O> {
@Override
protected RStarTreeFactory<O> makeInstance() {
return new RStarTreeFactory<O>(fileName, pageSize, cacheSize, bulkSplitter, insertionStrategy, nodeSplitter, overflowTreatment, minimumFill);
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/rstar/RStarTreeIndex.java b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/rstar/RStarTreeIndex.java
index ab136926..1946293f 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/rstar/RStarTreeIndex.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/rstar/RStarTreeIndex.java
@@ -27,8 +27,9 @@ import java.util.ArrayList;
import java.util.List;
import de.lmu.ifi.dbs.elki.data.NumberVector;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.database.query.distance.SpatialDistanceQuery;
@@ -52,11 +53,11 @@ import de.lmu.ifi.dbs.elki.persistent.PageFile;
*
* @param <O> Object type
*/
-public class RStarTreeIndex<O extends NumberVector<?, ?>> extends RStarTree implements RangeIndex<O>, KNNIndex<O> {
+public class RStarTreeIndex<O extends NumberVector<?>> extends RStarTree implements RangeIndex<O>, KNNIndex<O> {
/**
* The appropriate logger for this index.
*/
- private static final Logging logger = Logging.getLogger(RStarTreeIndex.class);
+ private static final Logging LOG = Logging.getLogger(RStarTreeIndex.class);
/**
* Relation
@@ -81,8 +82,8 @@ public class RStarTreeIndex<O extends NumberVector<?, ?>> extends RStarTree impl
* @param id Object id
* @return Spatial leaf entry
*/
- protected SpatialPointLeafEntry createNewLeafEntry(DBID id) {
- return new SpatialPointLeafEntry(id, relation.get(id));
+ protected SpatialPointLeafEntry createNewLeafEntry(DBIDRef id) {
+ return new SpatialPointLeafEntry(DBIDUtil.deref(id), relation.get(id));
}
/**
@@ -91,7 +92,7 @@ public class RStarTreeIndex<O extends NumberVector<?, ?>> extends RStarTree impl
* @param id the object id that was inserted
*/
@Override
- public void insert(DBID id) {
+ public void insert(DBIDRef id) {
insertLeaf(createNewLeafEntry(id));
}
@@ -111,13 +112,13 @@ public class RStarTreeIndex<O extends NumberVector<?, ?>> extends RStarTree impl
if(canBulkLoad()) {
List<SpatialEntry> leafs = new ArrayList<SpatialEntry>(ids.size());
for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
- leafs.add(createNewLeafEntry(iter.getDBID()));
+ leafs.add(createNewLeafEntry(iter));
}
bulkLoad(leafs);
}
else {
for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
- insert(iter.getDBID());
+ insert(DBIDUtil.deref(iter));
}
}
@@ -131,7 +132,7 @@ public class RStarTreeIndex<O extends NumberVector<?, ?>> extends RStarTree impl
* false otherwise
*/
@Override
- public boolean delete(DBID id) {
+ public boolean delete(DBIDRef id) {
// find the leaf node containing o
O obj = relation.get(id);
IndexTreePath<SpatialEntry> deletionPath = findPathToObject(getRootPath(), obj, id);
@@ -145,7 +146,7 @@ public class RStarTreeIndex<O extends NumberVector<?, ?>> extends RStarTree impl
@Override
public void deleteAll(DBIDs ids) {
for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
- delete(iter.getDBID());
+ delete(iter);
}
}
@@ -189,6 +190,6 @@ public class RStarTreeIndex<O extends NumberVector<?, ?>> extends RStarTree impl
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/bulk/MaxExtensionBulkSplit.java b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/bulk/MaxExtensionBulkSplit.java
index 90a8b622..2fd69531 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/bulk/MaxExtensionBulkSplit.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/bulk/MaxExtensionBulkSplit.java
@@ -45,7 +45,7 @@ public class MaxExtensionBulkSplit extends AbstractBulkSplit {
/**
* Logger.
*/
- private static final Logging logger = Logging.getLogger(MaxExtensionBulkSplit.class);
+ private static final Logging LOG = Logging.getLogger(MaxExtensionBulkSplit.class);
/**
* Static instance
@@ -74,12 +74,12 @@ public class MaxExtensionBulkSplit extends AbstractBulkSplit {
List<N> objects = new ArrayList<N>(spatialObjects);
while(objects.size() > 0) {
- StringBuffer msg = new StringBuffer();
+ StringBuilder msg = new StringBuilder();
// get the split axis and split point
int splitAxis = chooseMaximalExtendedSplitAxis(objects);
int splitPoint = chooseBulkSplitPoint(objects.size(), minEntries, maxEntries);
- if(logger.isDebugging()) {
+ if(LOG.isDebugging()) {
msg.append("\nsplitAxis ").append(splitAxis);
msg.append("\nsplitPoint ").append(splitPoint);
}
@@ -96,15 +96,15 @@ public class MaxExtensionBulkSplit extends AbstractBulkSplit {
partitions.add(partition1);
// copy array
- if(logger.isDebugging()) {
- msg.append("\ncurrent partition " + partition1);
+ if(LOG.isDebugging()) {
+ msg.append("\ncurrent partition ").append(partition1);
msg.append("\nremaining objects # ").append(objects.size());
- logger.debugFine(msg.toString());
+ LOG.debugFine(msg.toString());
}
}
- if(logger.isDebugging()) {
- logger.debugFine("partitions " + partitions);
+ if(LOG.isDebugging()) {
+ LOG.debugFine("partitions " + partitions);
}
return partitions;
}
@@ -125,17 +125,17 @@ public class MaxExtensionBulkSplit extends AbstractBulkSplit {
// compute min and max value in each dimension
for(SpatialComparable object : objects) {
- for(int d = 1; d <= dimension; d++) {
+ for(int d = 0; d < dimension; d++) {
double min, max;
min = object.getMin(d);
max = object.getMax(d);
- if(maxExtension[d - 1] < max) {
- maxExtension[d - 1] = max;
+ if(maxExtension[d] < max) {
+ maxExtension[d] = max;
}
- if(minExtension[d - 1] > min) {
- minExtension[d - 1] = min;
+ if(minExtension[d] > min) {
+ minExtension[d] = min;
}
}
}
@@ -143,8 +143,8 @@ public class MaxExtensionBulkSplit extends AbstractBulkSplit {
// set split axis to dim with maximal extension
int splitAxis = -1;
double max = 0;
- for(int d = 1; d <= dimension; d++) {
- double currentExtension = maxExtension[d - 1] - minExtension[d - 1];
+ for(int d = 0; d < dimension; d++) {
+ double currentExtension = maxExtension[d] - minExtension[d];
if(max < currentExtension) {
max = currentExtension;
splitAxis = d;
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/bulk/OneDimSortBulkSplit.java b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/bulk/OneDimSortBulkSplit.java
index b99ae01e..2209ddef 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/bulk/OneDimSortBulkSplit.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/bulk/OneDimSortBulkSplit.java
@@ -45,7 +45,7 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
@Reference(authors = "Roussopoulos, N. and Leifker, D.", title = "Direct spatial search on pictorial databases using packed R-trees", booktitle = "ACM SIGMOD Record 14-4", url = "http://dx.doi.org/10.1145/971699.318900")
public class OneDimSortBulkSplit extends AbstractBulkSplit {
/**
- * Static instance
+ * Static instance.
*/
public static final AbstractBulkSplit STATIC = new OneDimSortBulkSplit();
@@ -62,8 +62,8 @@ public class OneDimSortBulkSplit extends AbstractBulkSplit {
Collections.sort(spatialObjects, new Comparator<SpatialComparable>() {
@Override
public int compare(SpatialComparable o1, SpatialComparable o2) {
- double min1 = (o1.getMax(1) + o1.getMin(1)) / 2;
- double min2 = (o2.getMax(1) + o2.getMin(1)) / 2;
+ double min1 = (o1.getMax(0) + o1.getMin(0)) * .5;
+ double min2 = (o2.getMax(0) + o2.getMin(0)) * .5;
return Double.compare(min1, min2);
}
});
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/bulk/SortTileRecursiveBulkSplit.java b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/bulk/SortTileRecursiveBulkSplit.java
index 28e96da6..e24ef3ce 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/bulk/SortTileRecursiveBulkSplit.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/bulk/SortTileRecursiveBulkSplit.java
@@ -59,15 +59,15 @@ public class SortTileRecursiveBulkSplit extends AbstractBulkSplit {
*
* @param objs Object list
* @param start Subinterval start
- * @param end Subinteval end
+ * @param end Subinterval end
* @param depth Iteration depth (must be less than dimensionality!)
* @param dims Total number of dimensions
* @param maxEntries Maximum page size
* @param c Comparison helper
* @param ret Output list
+ * @param <T> data type
*/
protected <T extends SpatialComparable> void strPartition(List<T> objs, int start, int end, int depth, int dims, int maxEntries, Compare<T> c, List<List<T>> ret) {
- c.dim = depth + 1;
final int p = (int) Math.ceil((end - start) / (double) maxEntries);
final int s = (int) Math.ceil(Math.pow(p, 1.0 / (dims - depth)));
@@ -78,6 +78,7 @@ public class SortTileRecursiveBulkSplit extends AbstractBulkSplit {
int e2 = start + (int) (((i + 1) * len) / s);
// LoggingUtil.warning("STR " + dim + " s2:" + s2 + " e2:" + e2);
if(e2 < end) {
+ c.dim = depth;
QuickSelect.quickSelect(objs, c, s2, end, e2);
}
if(depth + 1 == dims) {
@@ -101,9 +102,9 @@ public class SortTileRecursiveBulkSplit extends AbstractBulkSplit {
*/
private static class Compare<T extends SpatialComparable> implements Comparator<T> {
/**
- * Current dimension
+ * Current dimension.
*/
- public int dim;
+ int dim;
@Override
public int compare(T o1, T o2) {
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/bulk/SpatialSortBulkSplit.java b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/bulk/SpatialSortBulkSplit.java
index 9c3a41a1..beb6e657 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/bulk/SpatialSortBulkSplit.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/bulk/SpatialSortBulkSplit.java
@@ -82,7 +82,7 @@ public class SpatialSortBulkSplit extends AbstractBulkSplit {
/**
* Option ID for spatial sorting
*/
- public static final OptionID SORTER_ID = OptionID.getOrCreateOptionID("rtree.bulk.spatial-sort", "Strategy for spatial sorting in bulk loading.");
+ public static final OptionID SORTER_ID = new OptionID("rtree.bulk.spatial-sort", "Strategy for spatial sorting in bulk loading.");
/**
* Sorting class
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/insert/ApproximativeLeastOverlapInsertionStrategy.java b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/insert/ApproximativeLeastOverlapInsertionStrategy.java
index a1dfbdb0..c39bd914 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/insert/ApproximativeLeastOverlapInsertionStrategy.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/insert/ApproximativeLeastOverlapInsertionStrategy.java
@@ -144,7 +144,7 @@ public class ApproximativeLeastOverlapInsertionStrategy extends LeastOverlapInse
/**
* Fast-insertion parameter. Optional.
*/
- public static OptionID INSERTION_CANDIDATES_ID = OptionID.getOrCreateOptionID("rtree.insertion-candidates", "defines how many children are tested for finding the child generating the least overlap when inserting an object.");
+ public static OptionID INSERTION_CANDIDATES_ID = new OptionID("rtree.insertion-candidates", "defines how many children are tested for finding the child generating the least overlap when inserting an object.");
/**
* The number of candidates to use
@@ -154,7 +154,8 @@ public class ApproximativeLeastOverlapInsertionStrategy extends LeastOverlapInse
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- IntParameter insertionCandidatesP = new IntParameter(INSERTION_CANDIDATES_ID, new GreaterConstraint(0), numCandidates);
+ IntParameter insertionCandidatesP = new IntParameter(INSERTION_CANDIDATES_ID, numCandidates);
+ insertionCandidatesP.addConstraint(new GreaterConstraint(0));
if(config.grab(insertionCandidatesP)) {
numCandidates = insertionCandidatesP.getValue();
}
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/insert/CombinedInsertionStrategy.java b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/insert/CombinedInsertionStrategy.java
index c90d99b2..63bbe9dc 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/insert/CombinedInsertionStrategy.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/insert/CombinedInsertionStrategy.java
@@ -88,12 +88,12 @@ public class CombinedInsertionStrategy implements InsertionStrategy {
/**
* Insertion strategy for directory nodes.
*/
- public static final OptionID DIR_STRATEGY_ID = OptionID.getOrCreateOptionID("rtree.insert-directory", "Insertion strategy for directory nodes.");
+ public static final OptionID DIR_STRATEGY_ID = new OptionID("rtree.insert-directory", "Insertion strategy for directory nodes.");
/**
* Insertion strategy for leaf nodes.
*/
- public static final OptionID LEAF_STRATEGY_ID = OptionID.getOrCreateOptionID("rtree.insert-leaf", "Insertion strategy for leaf nodes.");
+ public static final OptionID LEAF_STRATEGY_ID = new OptionID("rtree.insert-leaf", "Insertion strategy for leaf nodes.");
/**
* Strategy when inserting into directory nodes
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/overflow/LimitedReinsertOverflowTreatment.java b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/overflow/LimitedReinsertOverflowTreatment.java
index 2532f351..3c25cbbc 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/overflow/LimitedReinsertOverflowTreatment.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/overflow/LimitedReinsertOverflowTreatment.java
@@ -111,7 +111,7 @@ public class LimitedReinsertOverflowTreatment implements OverflowTreatment {
/**
* Fast-insertion parameter. Optional.
*/
- public static OptionID REINSERT_STRATEGY_ID = OptionID.getOrCreateOptionID("rtree.reinsertion-strategy", "The strategy to select candidates for reinsertion.");
+ public static OptionID REINSERT_STRATEGY_ID = new OptionID("rtree.reinsertion-strategy", "The strategy to select candidates for reinsertion.");
/**
* The actual reinsertion strategy
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/reinsert/AbstractPartialReinsert.java b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/reinsert/AbstractPartialReinsert.java
index e0277606..bb222dfe 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/reinsert/AbstractPartialReinsert.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/reinsert/AbstractPartialReinsert.java
@@ -27,7 +27,8 @@ import de.lmu.ifi.dbs.elki.distance.distancefunction.SpatialPrimitiveDoubleDista
import de.lmu.ifi.dbs.elki.distance.distancefunction.SquaredEuclideanDistanceFunction;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.IntervalConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.LessConstraint;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.DoubleParameter;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ObjectParameter;
@@ -68,16 +69,16 @@ public abstract class AbstractPartialReinsert implements ReinsertStrategy {
*
* @apiviz.exclude
*/
- public static abstract class Parameterizer extends AbstractParameterizer {
+ public abstract static class Parameterizer extends AbstractParameterizer {
/**
* Reinsertion share
*/
- public static OptionID REINSERT_AMOUNT_ID = OptionID.getOrCreateOptionID("rtree.reinsertion-amount", "The amount of entries to reinsert.");
+ public static OptionID REINSERT_AMOUNT_ID = new OptionID("rtree.reinsertion-amount", "The amount of entries to reinsert.");
/**
* Reinsertion share
*/
- public static OptionID REINSERT_DISTANCE_ID = OptionID.getOrCreateOptionID("rtree.reinsertion-distancce", "The distance function to compute reinsertion candidates by.");
+ public static OptionID REINSERT_DISTANCE_ID = new OptionID("rtree.reinsertion-distancce", "The distance function to compute reinsertion candidates by.");
/**
* The actual reinsertion strategy
@@ -92,12 +93,14 @@ public abstract class AbstractPartialReinsert implements ReinsertStrategy {
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- DoubleParameter reinsertAmountP = new DoubleParameter(REINSERT_AMOUNT_ID, new IntervalConstraint(0.0, IntervalConstraint.IntervalBoundary.OPEN, 0.5, IntervalConstraint.IntervalBoundary.OPEN), 0.3);
- if(config.grab(reinsertAmountP)) {
+ DoubleParameter reinsertAmountP = new DoubleParameter(REINSERT_AMOUNT_ID, 0.3);
+ reinsertAmountP.addConstraint(new GreaterConstraint(0.0));
+ reinsertAmountP.addConstraint(new LessConstraint(0.5));
+ if (config.grab(reinsertAmountP)) {
reinsertAmount = reinsertAmountP.getValue();
}
ObjectParameter<SpatialPrimitiveDoubleDistanceFunction<?>> distanceP = new ObjectParameter<SpatialPrimitiveDoubleDistanceFunction<?>>(REINSERT_DISTANCE_ID, SpatialPrimitiveDoubleDistanceFunction.class, SquaredEuclideanDistanceFunction.class);
- if(config.grab(distanceP)) {
+ if (config.grab(distanceP)) {
distanceFunction = distanceP.instantiateClass(config);
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/split/AngTanLinearSplit.java b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/split/AngTanLinearSplit.java
index e59fe10e..3e3d599c 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/split/AngTanLinearSplit.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/split/AngTanLinearSplit.java
@@ -56,7 +56,7 @@ public class AngTanLinearSplit implements SplitStrategy {
/**
* Logger class
*/
- private static final Logging logger = Logging.getLogger(AngTanLinearSplit.class);
+ private static final Logging LOG = Logging.getLogger(AngTanLinearSplit.class);
/**
* Static instance.
@@ -82,11 +82,11 @@ public class AngTanLinearSplit implements SplitStrategy {
}
for(int i = 0; i < num; i++) {
E e = getter.get(entries, i);
- for(int d = 1; d <= dim; d++) {
+ for(int d = 0; d < dim; d++) {
double low = e.getMin(d) - total.getMin(d);
double hig = total.getMax(d) - e.getMax(d);
if(low >= hig) {
- closer[d - 1].set(i);
+ closer[d].set(i);
}
}
}
@@ -105,7 +105,7 @@ public class AngTanLinearSplit implements SplitStrategy {
continue;
}
if(card < bestcard) {
- axis = d + 1;
+ axis = d;
bestcard = card;
bestset = cand;
bestover = Double.NaN;
@@ -117,16 +117,16 @@ public class AngTanLinearSplit implements SplitStrategy {
}
double overlap = computeOverlap(entries, getter, cand);
if(overlap < bestover) {
- axis = d + 1;
+ axis = d;
bestcard = card;
bestset = cand;
bestover = overlap;
}
else if(overlap == bestover) {
double bestlen = total.getMax(axis) - total.getMin(axis);
- double candlen = total.getMax(d + 1) - total.getMin(d + 1);
+ double candlen = total.getMax(d) - total.getMin(d);
if(candlen < bestlen) {
- axis = d + 1;
+ axis = d;
bestcard = card;
bestset = cand;
bestover = overlap;
@@ -135,8 +135,8 @@ public class AngTanLinearSplit implements SplitStrategy {
}
}
if(bestset == null) {
- logger.warning("No Ang-Tan-Split found. Probably all points are the same? Returning random split.");
- return Util.randomBitSet(num / 2, num, new Random());
+ LOG.warning("No Ang-Tan-Split found. Probably all points are the same? Returning random split.");
+ return Util.randomBitSet(num >> 1, num, new Random());
}
return bestset;
}
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/split/GreeneSplit.java b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/split/GreeneSplit.java
index 7401fbe5..99c15fd6 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/split/GreeneSplit.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/split/GreeneSplit.java
@@ -70,72 +70,83 @@ public class GreeneSplit implements SplitStrategy {
// Compute individual areas
double[] areas = new double[num];
- for(int e1 = 0; e1 < num - 1; e1++) {
+ for (int e1 = 0; e1 < num - 1; e1++) {
final E e1i = getter.get(entries, e1);
areas[e1] = SpatialUtil.volume(e1i);
}
// Compute area increase
- for(int e1 = 0; e1 < num - 1; e1++) {
+ for (int e1 = 0; e1 < num - 1; e1++) {
final E e1i = getter.get(entries, e1);
- for(int e2 = e1 + 1; e2 < num; e2++) {
+ for (int e2 = e1 + 1; e2 < num; e2++) {
final E e2i = getter.get(entries, e2);
final double areaJ = SpatialUtil.volumeUnion(e1i, e2i);
final double d = areaJ - areas[e1] - areas[e2];
- if(d > worst) {
+ if (d > worst) {
worst = d;
w1 = e1;
w2 = e2;
}
}
}
- // Data to keep
- // Initial mbrs and areas
- E m1 = getter.get(entries, w1);
- E m2 = getter.get(entries, w2);
+ if (worst > 0) {
+ // Data to keep
+ // Initial mbrs and areas
+ E m1 = getter.get(entries, w1);
+ E m2 = getter.get(entries, w2);
- double bestsep = Double.NEGATIVE_INFINITY;
- double bestsep2 = Double.NEGATIVE_INFINITY;
- for(int d = 1; d <= m1.getDimensionality(); d++) {
- final double s1 = m1.getMin(d) - m2.getMax(d);
- final double s2 = m2.getMin(d) - m1.getMax(d);
- final double sm = Math.max(s1, s2);
- final double no = Math.max(m1.getMax(d), m2.getMax(d)) - Math.min(m1.getMin(d), m2.getMin(d));
- final double sep = sm / no;
- if(sep > bestsep || (sep == bestsep && sm > bestsep2)) {
- bestsep = sep;
- bestsep2 = sm;
- axis = d;
+ double bestsep = Double.NEGATIVE_INFINITY;
+ double bestsep2 = Double.NEGATIVE_INFINITY;
+ for (int d = 0; d < m1.getDimensionality(); d++) {
+ final double s1 = m1.getMin(d) - m2.getMax(d);
+ final double s2 = m2.getMin(d) - m1.getMax(d);
+ final double sm = Math.max(s1, s2);
+ final double no = Math.max(m1.getMax(d), m2.getMax(d)) - Math.min(m1.getMin(d), m2.getMin(d));
+ final double sep = sm / no;
+ if (sep > bestsep || (sep == bestsep && sm > bestsep2)) {
+ bestsep = sep;
+ bestsep2 = sm;
+ axis = d;
+ }
+ }
+ } else {
+ // All objects are identical!
+ final BitSet assignment = new BitSet(num);
+ final int half = (num + 1) >> 1;
+ // Put the first half into second node
+ for (int i = 0; i < half; i++) {
+ assignment.set(i);
}
+ return assignment;
}
}
// Sort by minimum value
DoubleIntPair[] data = new DoubleIntPair[num];
- for(int i = 0; i < num; i++) {
+ for (int i = 0; i < num; i++) {
data[i] = new DoubleIntPair(getter.get(entries, i).getMin(axis), i);
}
Arrays.sort(data);
// Object assignment
final BitSet assignment = new BitSet(num);
- final int half = (num + 1) / 2;
+ final int half = (num + 1) >> 1;
// Put the first half into second node
- for(int i = 0; i < half; i++) {
+ for (int i = 0; i < half; i++) {
assignment.set(data[i].second);
}
// Tie handling
- if(num % 2 == 0) {
+ if (num % 2 == 0) {
// We need to compute the bounding boxes
ModifiableHyperBoundingBox mbr1 = new ModifiableHyperBoundingBox(getter.get(entries, data[0].second));
- for(int i = 1; i < half; i++) {
+ for (int i = 1; i < half; i++) {
mbr1.extend(getter.get(entries, data[i].second));
}
ModifiableHyperBoundingBox mbr2 = new ModifiableHyperBoundingBox(getter.get(entries, data[num - 1].second));
- for(int i = half + 1; i < num - 1; i++) {
+ for (int i = half + 1; i < num - 1; i++) {
mbr2.extend(getter.get(entries, data[i].second));
}
E e = getter.get(entries, data[half].second);
double inc1 = SpatialUtil.volumeUnion(mbr1, e) - SpatialUtil.volume(mbr1);
double inc2 = SpatialUtil.volumeUnion(mbr2, e) - SpatialUtil.volume(mbr2);
- if(inc1 < inc2) {
+ if (inc1 < inc2) {
assignment.set(data[half].second);
}
}
@@ -155,4 +166,4 @@ public class GreeneSplit implements SplitStrategy {
return GreeneSplit.STATIC;
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/split/RTreeLinearSplit.java b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/split/RTreeLinearSplit.java
index 296f6b3b..b4ff2364 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/split/RTreeLinearSplit.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/split/RTreeLinearSplit.java
@@ -67,7 +67,7 @@ public class RTreeLinearSplit implements SplitStrategy {
double bestsep = Double.NEGATIVE_INFINITY;
int w1 = -1, w2 = -1;
// LPS1: find extreme rectangles
- for(int d = 1; d <= dim; d++) {
+ for(int d = 0; d < dim; d++) {
// We need to find two candidates each, in case of el==eh!
double minlow = Double.POSITIVE_INFINITY;
double maxlow = Double.NEGATIVE_INFINITY, maxlow2 = Double.NEGATIVE_INFINITY;
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/split/TopologicalSplitter.java b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/split/TopologicalSplitter.java
index 1789ab22..ab32ba19 100644
--- a/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/split/TopologicalSplitter.java
+++ b/src/de/lmu/ifi/dbs/elki/index/tree/spatial/rstarvariants/strategies/split/TopologicalSplitter.java
@@ -105,22 +105,25 @@ public class TopologicalSplitter implements SplitStrategy {
private A entries;
/**
- * The getter class for the entries
+ * The getter class for the entries.
*/
private ArrayAdapter<E, A> getter;
/**
- * List size
+ * List size.
*/
private int size;
/**
- * Dimensionality
+ * Dimensionality.
*/
private int dimensionality;
/**
* Constructor.
+ *
+ * @param entries Entires to split
+ * @param getter Array adapter for entries
*/
public Split(A entries, ArrayAdapter<E, A> getter) {
this.entries = entries;
@@ -140,7 +143,7 @@ public class TopologicalSplitter implements SplitStrategy {
double minSurface = Double.MAX_VALUE;
int splitAxis = -1;
- for(int d = 1; d <= dimensionality; d++) {
+ for(int d = 0; d < dimensionality; d++) {
double sumOfAllMargins = 0;
fillAndSort(d);
@@ -180,7 +183,7 @@ public class TopologicalSplitter implements SplitStrategy {
}
/**
- * Init the arrays we use
+ * Init the arrays we use.
*/
protected void initMinMaxArrays() {
maxSorting = new DoubleIntPair[size];
@@ -227,7 +230,7 @@ public class TopologicalSplitter implements SplitStrategy {
// is best for the split axis
bestSorting = null;
- assert (size - 2 * minEntries > 0) : "Cannot split underfull nodes.";
+ assert (size - 2 * minEntries >= 0) : "Cannot split nodes (" + size + " < 2*" + minEntries + ")";
// test the sorting with respect to the minimal values
{
ModifiableHyperBoundingBox mbr1 = mbr(minSorting, 0, minEntries - 1);
@@ -267,10 +270,22 @@ public class TopologicalSplitter implements SplitStrategy {
assert (splitPoint < size) : "No split found? Volume outside of double precision?";
}
+ /**
+ * Get an entry.
+ *
+ * @param off Offset
+ * @return Entry
+ */
private E get(int off) {
return getter.get(entries, off);
}
+ /**
+ * Get an entry.
+ *
+ * @param pair Entry pair
+ * @return Entry
+ */
private E get(DoubleIntPair pair) {
return getter.get(entries, pair.second);
}
@@ -306,4 +321,4 @@ public class TopologicalSplitter implements SplitStrategy {
return TopologicalSplitter.STATIC;
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/index/vafile/DAFile.java b/src/de/lmu/ifi/dbs/elki/index/vafile/DAFile.java
index f99f5918..089397dd 100644
--- a/src/de/lmu/ifi/dbs/elki/index/vafile/DAFile.java
+++ b/src/de/lmu/ifi/dbs/elki/index/vafile/DAFile.java
@@ -26,9 +26,9 @@ package de.lmu.ifi.dbs.elki.index.vafile;
import java.util.Arrays;
import de.lmu.ifi.dbs.elki.data.NumberVector;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.persistent.ByteArrayUtil;
import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
/**
@@ -50,21 +50,23 @@ import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
@Reference(authors = "Hans-Peter Kriegel, Peer Kröger, Matthias Schubert, Ziyue Zhu", title = "Efficient Query Processing in Arbitrary Subspaces Using Vector Approximations", booktitle = "Proc. 18th Int. Conf. on Scientific and Statistical Database Management (SSDBM 06), Wien, Austria, 2006", url = "http://dx.doi.org/10.1109/SSDBM.2006.23")
public class DAFile {
/**
- * Dimension of this approximation file
+ * Dimension of this approximation file.
*/
- final private int dimension;
+ private final int dimension;
/**
- * Splitting grid
+ * Splitting grid.
*/
- final private double[] splitPositions;
+ private final double[] splitPositions;
/**
* Constructor.
*
+ * @param relation Relation to index
* @param dimension Dimension of this file
+ * @param partitions Number of partitions
*/
- public DAFile(Relation<? extends NumberVector<?, ?>> relation, int dimension, int partitions) {
+ public DAFile(Relation<? extends NumberVector<?>> relation, int dimension, int partitions) {
final int size = relation.size();
this.dimension = dimension;
this.splitPositions = new double[partitions + 1];
@@ -72,8 +74,7 @@ public class DAFile {
double[] tempdata = new double[size];
int j = 0;
for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
- tempdata[j] = relation.get(id).doubleValue(dimension + 1);
+ tempdata[j] = relation.get(iditer).doubleValue(dimension);
j += 1;
}
Arrays.sort(tempdata);
@@ -87,6 +88,8 @@ public class DAFile {
}
/**
+ * Return the split positions.
+ *
* @return the split positions
*/
public double[] getSplitPositions() {
@@ -94,6 +97,8 @@ public class DAFile {
}
/**
+ * Return the dimension we indexed.
+ *
* @return the dimension
*/
public int getDimension() {
@@ -106,6 +111,6 @@ public class DAFile {
* @return IO costs
*/
public int getIOCosts() {
- return splitPositions.length * 8 + 4;
+ return splitPositions.length * ByteArrayUtil.SIZE_DOUBLE + 4;
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/index/vafile/PartialVAFile.java b/src/de/lmu/ifi/dbs/elki/index/vafile/PartialVAFile.java
index 848597d6..37fbe994 100644
--- a/src/de/lmu/ifi/dbs/elki/index/vafile/PartialVAFile.java
+++ b/src/de/lmu/ifi/dbs/elki/index/vafile/PartialVAFile.java
@@ -37,19 +37,20 @@ import de.lmu.ifi.dbs.elki.data.type.TypeInformation;
import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.query.DatabaseQuery;
-import de.lmu.ifi.dbs.elki.database.query.DistanceDBIDResult;
-import de.lmu.ifi.dbs.elki.database.query.DoubleDistanceResultPair;
-import de.lmu.ifi.dbs.elki.database.query.GenericDistanceDBIDList;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.database.query.knn.KNNQuery;
-import de.lmu.ifi.dbs.elki.database.query.knn.KNNResult;
import de.lmu.ifi.dbs.elki.database.query.range.RangeQuery;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
import de.lmu.ifi.dbs.elki.distance.distancefunction.LPNormDistanceFunction;
import de.lmu.ifi.dbs.elki.distance.distancefunction.subspace.SubspaceLPNormDistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DoubleDistanceDBIDList;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DoubleDistanceKNNHeap;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DoubleDistanceKNNList;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
import de.lmu.ifi.dbs.elki.index.AbstractRefiningIndex;
@@ -60,11 +61,8 @@ import de.lmu.ifi.dbs.elki.index.tree.TreeIndexFactory;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.math.MathUtil;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
-import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.Heap;
-import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.KNNHeap;
-import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.KNNList;
-import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.TopBoundedHeap;
+import de.lmu.ifi.dbs.elki.persistent.ByteArrayUtil;
+import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.DoubleMaxHeap;
import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
@@ -94,36 +92,38 @@ import de.lmu.ifi.dbs.elki.utilities.pairs.DoubleObjPair;
* @apiviz.uses PartialVACandidate
* @apiviz.has PartialVAFileRangeQuery
* @apiviz.has PartialVAFileKNNQuery
+ *
+ * @param <V> Vector type
*/
@Reference(authors = "Hans-Peter Kriegel, Peer Kröger, Matthias Schubert, Ziyue Zhu", title = "Efficient Query Processing in Arbitrary Subspaces Using Vector Approximations", booktitle = "Proc. 18th Int. Conf. on Scientific and Statistical Database Management (SSDBM 06), Wien, Austria, 2006", url = "http://dx.doi.org/10.1109/SSDBM.2006.23")
-public class PartialVAFile<V extends NumberVector<?, ?>> extends AbstractRefiningIndex<V> implements KNNIndex<V>, RangeIndex<V> {
+public class PartialVAFile<V extends NumberVector<?>> extends AbstractRefiningIndex<V> implements KNNIndex<V>, RangeIndex<V> {
/**
- * Class logger
+ * Class logger.
*/
- private static final Logging logger = Logging.getLogger(PartialVAFile.class);
+ private static final Logging LOG = Logging.getLogger(PartialVAFile.class);
/**
- * Partial VA files
+ * Partial VA files.
*/
List<DAFile> daFiles;
/**
- * Number of partitions
+ * Number of partitions.
*/
private final int partitions;
/**
- * Page size
+ * Page size.
*/
private final int pageSize;
/**
- * Splitting grid
+ * Splitting grid.
*/
private double[][] splitPartitions;
/**
- * Statistics
+ * Statistics.
*/
protected Statistics stats;
@@ -156,7 +156,7 @@ public class PartialVAFile<V extends NumberVector<?, ?>> extends AbstractRefinin
throw new IllegalArgumentException("Number of partitions must be a power of 2!");
}
- final int dimensions = DatabaseUtil.dimensionality(relation);
+ final int dimensions = RelationUtil.dimensionality(relation);
splitPartitions = new double[dimensions][];
daFiles = new ArrayList<DAFile>(dimensions);
@@ -168,7 +168,7 @@ public class PartialVAFile<V extends NumberVector<?, ?>> extends AbstractRefinin
vectorApprox = new ArrayList<VectorApproximation>();
for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
- DBID id = iter.getDBID();
+ DBID id = DBIDUtil.deref(iter);
V dv = relation.get(id);
VectorApproximation va = calculateFullApproximation(id, dv);
vectorApprox.add(va);
@@ -191,8 +191,8 @@ public class PartialVAFile<V extends NumberVector<?, ?>> extends AbstractRefinin
* @param relation Relation with full dimensionality
* @return Bit set with all bits set.
*/
- protected static BitSet fakeSubspace(Relation<? extends NumberVector<?, ?>> relation) {
- int dim = DatabaseUtil.dimensionality(relation);
+ protected static BitSet fakeSubspace(Relation<? extends NumberVector<?>> relation) {
+ int dim = RelationUtil.dimensionality(relation);
BitSet bits = new BitSet();
for(int i = 0; i < dim; i++) {
bits.set(i);
@@ -211,20 +211,20 @@ public class PartialVAFile<V extends NumberVector<?, ?>> extends AbstractRefinin
int approximation[] = new int[dv.getDimensionality()];
for(int d = 0; d < splitPartitions.length; d++) {
double[] split = daFiles.get(d).getSplitPositions();
- final double val = dv.doubleValue(d + 1);
+ final double val = dv.doubleValue(d);
final int lastBorderIndex = split.length - 1;
// Value is below data grid
if(val < split[0]) {
approximation[d] = 0;
if(id != null) {
- logger.warning("Vector outside of VAFile grid!");
+ LOG.warning("Vector outside of VAFile grid!");
}
} // Value is above data grid
else if(val > split[lastBorderIndex]) {
approximation[d] = lastBorderIndex - 1;
if(id != null) {
- logger.warning("Vector outside of VAFile grid!");
+ LOG.warning("Vector outside of VAFile grid!");
}
} // normal case
else {
@@ -294,7 +294,7 @@ public class PartialVAFile<V extends NumberVector<?, ?>> extends AbstractRefinin
* @param query Query vector
* @param epsilon Epsilon radius
*/
- protected static void calculateSelectivityCoeffs(List<DoubleObjPair<DAFile>> daFiles, NumberVector<?, ?> query, double epsilon) {
+ protected static void calculateSelectivityCoeffs(List<DoubleObjPair<DAFile>> daFiles, NumberVector<?> query, double epsilon) {
final int dimensions = query.getDimensionality();
double[] lowerVals = new double[dimensions];
double[] upperVals = new double[dimensions];
@@ -302,8 +302,9 @@ public class PartialVAFile<V extends NumberVector<?, ?>> extends AbstractRefinin
VectorApproximation queryApprox = calculatePartialApproximation(null, query, daFiles);
for(int i = 0; i < dimensions; i++) {
- lowerVals[i] = query.doubleValue(i + 1) - epsilon;
- upperVals[i] = query.doubleValue(i + 1) + epsilon;
+ final double val = query.doubleValue(i);
+ lowerVals[i] = val - epsilon;
+ upperVals[i] = val + epsilon;
}
Vector lowerEpsilon = new Vector(lowerVals);
@@ -319,17 +320,17 @@ public class PartialVAFile<V extends NumberVector<?, ?>> extends AbstractRefinin
}
/**
- * Calculate partial vector approximation
+ * Calculate partial vector approximation.
*
* @param id Object ID
* @param dv Object vector
* @param daFiles List of approximations to use
* @return Vector approximation
*/
- protected static VectorApproximation calculatePartialApproximation(DBID id, NumberVector<?, ?> dv, List<DoubleObjPair<DAFile>> daFiles) {
+ protected static VectorApproximation calculatePartialApproximation(DBID id, NumberVector<?> dv, List<DoubleObjPair<DAFile>> daFiles) {
int[] approximation = new int[dv.getDimensionality()];
for(int i = 0; i < daFiles.size(); i++) {
- double val = dv.doubleValue(i + 1);
+ double val = dv.doubleValue(i);
double[] borders = daFiles.get(i).second.getSplitPositions();
assert borders != null : "borders are null";
int lastBorderIndex = borders.length - 1;
@@ -353,7 +354,7 @@ public class PartialVAFile<V extends NumberVector<?, ?>> extends AbstractRefinin
}
/**
- * Class for tracking Partial VA file statistics
+ * Class for tracking Partial VA file statistics.
*
* TODO: refactor into a common statistics API
*
@@ -387,7 +388,7 @@ public class PartialVAFile<V extends NumberVector<?, ?>> extends AbstractRefinin
protected double minDistP = 0.0;
/**
- * The actual approximation
+ * The actual approximation.
*/
final private VectorApproximation approx;
@@ -429,12 +430,12 @@ public class PartialVAFile<V extends NumberVector<?, ?>> extends AbstractRefinin
*/
public class PartialVAFileRangeQuery extends AbstractRefiningIndex<V>.AbstractRangeQuery<DoubleDistance> {
/**
- * Lp-Norm p
+ * Lp-Norm p.
*/
private double p;
/**
- * Subspace
+ * Subspace.
*/
private BitSet subspace;
@@ -452,7 +453,7 @@ public class PartialVAFile<V extends NumberVector<?, ?>> extends AbstractRefinin
}
@Override
- public DistanceDBIDResult<DoubleDistance> getRangeForObject(V query, DoubleDistance range) {
+ public DoubleDistanceDBIDList getRangeForObject(V query, DoubleDistance range) {
stats.issuedQueries++;
long t = System.nanoTime();
@@ -480,7 +481,7 @@ public class PartialVAFile<V extends NumberVector<?, ?>> extends AbstractRefinin
// create candidate list (all objects) and prune candidates w.r.t.
// mindist (i.e. remove them from the list)
// important: this structure contains the maxDist values for refinement!
- DistanceDBIDResult<DoubleDistance> result = new GenericDistanceDBIDList<DoubleDistance>();
+ DoubleDistanceDBIDList result = new DoubleDistanceDBIDList();
int candidates = 0;
for(VectorApproximation va : vectorApprox) {
DBID id = va.getId();
@@ -503,26 +504,26 @@ public class PartialVAFile<V extends NumberVector<?, ?>> extends AbstractRefinin
// candidate cannot be dropped
// TODO: actually: no refinement needed - need API that allows
// reporting maxdists only.
- result.add(new DoubleDistanceResultPair(refine(id, query).doubleValue(), id));
+ result.add(refine(id, query).doubleValue(), id);
}
else { // refine candidate - true refinement
DoubleDistance dis = refine(id, query);
stats.refinements += 1;
if(dis.doubleValue() <= range.doubleValue()) {
- result.add(new DoubleDistanceResultPair(dis.doubleValue(), id));
+ result.add(dis.doubleValue(), id);
}
}
}
}
- Collections.sort(result);
+ result.sort();
stats.scannedBytes += relation.size() * VectorApproximation.byteOnDisk(subspace.cardinality(), partitions);
stats.queryTime += System.nanoTime() - t;
- if(logger.isDebuggingFine()) {
- logger.fine("query = " + query);
- logger.fine("database: " + relation.size() + ", candidates: " + candidates + ", results: " + result.size());
+ if(LOG.isDebuggingFine()) {
+ LOG.fine("query = " + query);
+ LOG.fine("database: " + relation.size() + ", candidates: " + candidates + ", results: " + result.size());
}
return result;
@@ -537,12 +538,12 @@ public class PartialVAFile<V extends NumberVector<?, ?>> extends AbstractRefinin
*/
public class PartialVAFileKNNQuery extends AbstractRefiningIndex<V>.AbstractKNNQuery<DoubleDistance> {
/**
- * Lp-Norm p
+ * Lp-Norm p.
*/
private double p;
/**
- * Subspace
+ * Subspace.
*/
private BitSet subspace;
@@ -560,7 +561,7 @@ public class PartialVAFile<V extends NumberVector<?, ?>> extends AbstractRefinin
}
@Override
- public KNNResult<DoubleDistance> getKNNForObject(V query, int k) {
+ public DoubleDistanceKNNList getKNNForObject(V query, int k) {
stats.issuedQueries++;
long t = System.nanoTime();
@@ -574,14 +575,14 @@ public class PartialVAFile<V extends NumberVector<?, ?>> extends AbstractRefinin
final int currentSubspaceDims = subspace.cardinality();
int reducedDims = (2 * currentSubspaceDims) / 3;
reducedDims = Math.max(1, reducedDims);
- if(logger.isDebuggingFine()) {
- logger.fine("subspaceDims=" + currentSubspaceDims + ", reducedDims=" + reducedDims);
+ if(LOG.isDebuggingFine()) {
+ LOG.fine("subspaceDims=" + currentSubspaceDims + ", reducedDims=" + reducedDims);
}
// filter 1
LinkedList<PartialVACandidate> candidates1 = filter1(k, reducedDims, daFiles, queryApprox, currentSubspaceDims, dist);
- if(logger.isDebuggingFine()) {
- logger.fine("candidate set after filter 1: " + candidates1.size());
+ if(LOG.isDebuggingFine()) {
+ LOG.fine("candidate set after filter 1: " + candidates1.size());
}
// filters 2+
@@ -596,15 +597,15 @@ public class PartialVAFile<V extends NumberVector<?, ?>> extends AbstractRefinin
// continue filtering until I/O costs of refining candidates < I/O
// costs of loading new DA files
while(candidates2 == null || (getIOCosts(candidates2.size(), currentSubspaceDims) >= getIOCosts(daFiles.get(0), currentSubspaceDims - addition)) && addition < currentSubspaceDims) {
- if(candidates2 != null && logger.isDebuggingFine()) {
- logger.fine("filter " + filterStep + ": refining costs " + getIOCosts(candidates2.size(), currentSubspaceDims) + " (" + candidates2.size() + "/" + currentSubspaceDims + "), DA file costs " + getIOCosts(daFiles.get(0), currentSubspaceDims - addition) + " (dim " + (addition + 1) + " of " + currentSubspaceDims + ")");
+ if(candidates2 != null && LOG.isDebuggingFine()) {
+ LOG.fine("filter " + filterStep + ": refining costs " + getIOCosts(candidates2.size(), currentSubspaceDims) + " (" + candidates2.size() + "/" + currentSubspaceDims + "), DA file costs " + getIOCosts(daFiles.get(0), currentSubspaceDims - addition) + " (dim " + (addition + 1) + " of " + currentSubspaceDims + ")");
}
if(candidates2 != null) {
candidates1 = candidates2;
}
candidates2 = new LinkedList<PartialVACandidate>();
- Heap<Double> kMinMaxDists = new TopBoundedHeap<Double>(k, Collections.reverseOrder());
+ DoubleMaxHeap kMinMaxDists = new DoubleMaxHeap(k+1);
for(PartialVACandidate va : candidates1) {
int dimension = daFiles.get(addition).getDimension();
int objectCell = va.getApproximation(dimension);
@@ -614,12 +615,12 @@ public class PartialVAFile<V extends NumberVector<?, ?>> extends AbstractRefinin
if(kMinMaxDists.size() < k || va.minDistP <= kMinMaxDists.peek()) {
candidates2.add(va);
- kMinMaxDists.add(va.maxDistP);
+ kMinMaxDists.add(va.maxDistP, k);
}
}
- if(logger.isDebuggingFine()) {
- logger.fine("candidate set after filter " + filterStep + ": " + candidates2.size());
+ if(LOG.isDebuggingFine()) {
+ LOG.fine("candidate set after filter " + filterStep + ": " + candidates2.size());
}
addition++;
@@ -633,7 +634,7 @@ public class PartialVAFile<V extends NumberVector<?, ?>> extends AbstractRefinin
ArrayList<PartialVACandidate> sortedCandidates = new ArrayList<PartialVACandidate>(candidates2);
// sort candidates by lower bound (minDist)
Collections.sort(sortedCandidates);
- KNNList<DoubleDistance> result = retrieveAccurateDistances(sortedCandidates, k, subspace, query);
+ DoubleDistanceKNNList result = retrieveAccurateDistances(sortedCandidates, k, subspace, query);
stats.queryTime += System.nanoTime() - t;
return result;
@@ -641,7 +642,7 @@ public class PartialVAFile<V extends NumberVector<?, ?>> extends AbstractRefinin
private LinkedList<PartialVACandidate> filter1(int k, int reducedDims, List<DAFile> daFiles, VectorApproximation queryApprox, int subspaceDims, VALPNormDistance dist) {
LinkedList<PartialVACandidate> candidates1 = new LinkedList<PartialVACandidate>();
- Heap<Double> minmaxdist = new TopBoundedHeap<Double>(k, Collections.reverseOrder());
+ DoubleMaxHeap minmaxdist = new DoubleMaxHeap(k+1);
for(VectorApproximation va : vectorApprox) {
PartialVACandidate pva = new PartialVACandidate(va);
@@ -656,7 +657,7 @@ public class PartialVAFile<V extends NumberVector<?, ?>> extends AbstractRefinin
}
if(minmaxdist.size() < k || pva.minDistP <= minmaxdist.peek()) {
candidates1.add(pva);
- minmaxdist.add(pva.maxDistP);
+ minmaxdist.add(pva.maxDistP, k);
}
}
// Drop candidates that don't satisfy the latest minmaxdist
@@ -680,7 +681,7 @@ public class PartialVAFile<V extends NumberVector<?, ?>> extends AbstractRefinin
* @return the cost value (in bytes)
*/
private int getIOCosts(int size, int subspaceDims) {
- return size * (subspaceDims * 8 + 4);
+ return size * (subspaceDims * ByteArrayUtil.SIZE_DOUBLE + 4);
}
/**
@@ -711,16 +712,16 @@ public class PartialVAFile<V extends NumberVector<?, ?>> extends AbstractRefinin
return result;
}
- protected KNNList<DoubleDistance> retrieveAccurateDistances(List<PartialVACandidate> sortedCandidates, int k, BitSet subspace, V query) {
- KNNHeap<DoubleDistance> result = new KNNHeap<DoubleDistance>(k, DoubleDistance.FACTORY.infiniteDistance());
+ protected DoubleDistanceKNNList retrieveAccurateDistances(List<PartialVACandidate> sortedCandidates, int k, BitSet subspace, V query) {
+ DoubleDistanceKNNHeap result = new DoubleDistanceKNNHeap(k);
for(PartialVACandidate va : sortedCandidates) {
- double stopdist = result.getKNNDistance().doubleValue();
+ double stopdist = result.doubleKNNDistance();
DBID currentID = va.getId();
if(result.size() < k || va.minDistP < stopdist) {
DoubleDistance dist = refine(currentID, query);
stats.refinements += 1;
if(dist.doubleValue() < stopdist) {
- result.add(new DoubleDistanceResultPair(dist.doubleValue(), currentID));
+ result.add(dist.doubleValue(), currentID);
}
}
}
@@ -747,16 +748,16 @@ public class PartialVAFile<V extends NumberVector<?, ?>> extends AbstractRefinin
}
/**
- * Index factory class
+ * Index factory class.
*
* @author Erich Schubert
*
- * @apiviz.stereotype «factory»
+ * @apiviz.stereotype factory
* @apiviz.has PartialVAFile
*
* @param <V> Vector type
*/
- public static class Factory<V extends NumberVector<?, ?>> implements IndexFactory<V, PartialVAFile<V>> {
+ public static class Factory<V extends NumberVector<?>> implements IndexFactory<V, PartialVAFile<V>> {
/**
* Number of partitions to use in each dimension.
*
@@ -764,15 +765,15 @@ public class PartialVAFile<V extends NumberVector<?, ?>> extends AbstractRefinin
* -vafile.partitions 8
* </pre>
*/
- public static final OptionID PARTITIONS_ID = OptionID.getOrCreateOptionID("vafile.partitions", "Number of partitions to use in each dimension.");
+ public static final OptionID PARTITIONS_ID = new OptionID("vafile.partitions", "Number of partitions to use in each dimension.");
/**
- * Page size
+ * Page size.
*/
int pagesize = 1;
/**
- * Number of partitions
+ * Number of partitions.
*/
int numpart = 2;
@@ -799,7 +800,7 @@ public class PartialVAFile<V extends NumberVector<?, ?>> extends AbstractRefinin
}
/**
- * Parameterization class
+ * Parameterization class.
*
* @author Erich Schubert
*
@@ -807,23 +808,25 @@ public class PartialVAFile<V extends NumberVector<?, ?>> extends AbstractRefinin
*/
public static class Parameterizer extends AbstractParameterizer {
/**
- * Page size
+ * Page size.
*/
int pagesize = 1;
/**
- * Number of partitions
+ * Number of partitions.
*/
int numpart = 2;
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- IntParameter pagesizeP = new IntParameter(TreeIndexFactory.PAGE_SIZE_ID, new GreaterConstraint(0), 1024);
+ IntParameter pagesizeP = new IntParameter(TreeIndexFactory.PAGE_SIZE_ID, 1024);
+ pagesizeP.addConstraint(new GreaterConstraint(0));
if(config.grab(pagesizeP)) {
pagesize = pagesizeP.getValue();
}
- IntParameter partitionsP = new IntParameter(Factory.PARTITIONS_ID, new GreaterConstraint(2));
+ IntParameter partitionsP = new IntParameter(Factory.PARTITIONS_ID);
+ partitionsP.addConstraint(new GreaterConstraint(2));
if(config.grab(partitionsP)) {
numpart = partitionsP.getValue();
}
@@ -831,7 +834,7 @@ public class PartialVAFile<V extends NumberVector<?, ?>> extends AbstractRefinin
@Override
protected Factory<?> makeInstance() {
- return new Factory<NumberVector<?, ?>>(pagesize, numpart);
+ return new Factory<NumberVector<?>>(pagesize, numpart);
}
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/index/vafile/VAFile.java b/src/de/lmu/ifi/dbs/elki/index/vafile/VAFile.java
index 1d4f8f6d..09b91be7 100644
--- a/src/de/lmu/ifi/dbs/elki/index/vafile/VAFile.java
+++ b/src/de/lmu/ifi/dbs/elki/index/vafile/VAFile.java
@@ -33,18 +33,19 @@ import de.lmu.ifi.dbs.elki.data.type.TypeInformation;
import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.query.DatabaseQuery;
-import de.lmu.ifi.dbs.elki.database.query.DistanceDBIDResult;
-import de.lmu.ifi.dbs.elki.database.query.DoubleDistanceResultPair;
-import de.lmu.ifi.dbs.elki.database.query.GenericDistanceDBIDList;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.database.query.knn.KNNQuery;
-import de.lmu.ifi.dbs.elki.database.query.knn.KNNResult;
import de.lmu.ifi.dbs.elki.database.query.range.RangeQuery;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
import de.lmu.ifi.dbs.elki.distance.distancefunction.LPNormDistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DoubleDistanceDBIDList;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DoubleDistanceKNNHeap;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DoubleDistanceKNNList;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
import de.lmu.ifi.dbs.elki.index.AbstractRefiningIndex;
@@ -53,10 +54,7 @@ import de.lmu.ifi.dbs.elki.index.KNNIndex;
import de.lmu.ifi.dbs.elki.index.RangeIndex;
import de.lmu.ifi.dbs.elki.index.tree.TreeIndexFactory;
import de.lmu.ifi.dbs.elki.logging.Logging;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
-import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.Heap;
-import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.KNNHeap;
-import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.TopBoundedHeap;
+import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.DoubleMaxHeap;
import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
import de.lmu.ifi.dbs.elki.utilities.documentation.Title;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
@@ -85,17 +83,19 @@ import de.lmu.ifi.dbs.elki.utilities.pairs.DoubleObjPair;
* @apiviz.has VAFileRangeQuery
* @apiviz.has VAFileKNNQuery
* @apiviz.uses VALPNormDistance
+ *
+ * @param <V> Vector type
*/
@Title("An approximation based data structure for similarity search")
@Reference(authors = "Weber, R. and Blott, S.", title = "An approximation based data structure for similarity search", booktitle = "Report TR1997b, ETH Zentrum, Zurich, Switzerland", url = "http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.40.480&rep=rep1&type=pdf")
-public class VAFile<V extends NumberVector<?, ?>> extends AbstractRefiningIndex<V> implements KNNIndex<V>, RangeIndex<V> {
+public class VAFile<V extends NumberVector<?>> extends AbstractRefiningIndex<V> implements KNNIndex<V>, RangeIndex<V> {
/**
- * Logging class
+ * Logging class.
*/
- private static final Logging log = Logging.getLogger(VAFile.class);
+ private static final Logging LOG = Logging.getLogger(VAFile.class);
/**
- * Approximation index
+ * Approximation index.
*/
private List<VectorApproximation> vectorApprox;
@@ -105,12 +105,12 @@ public class VAFile<V extends NumberVector<?, ?>> extends AbstractRefiningIndex<
private int partitions;
/**
- * Quantile grid we use
+ * Quantile grid we use.
*/
private double[][] splitPositions;
/**
- * Page size, for estimating the VA file size
+ * Page size, for estimating the VA file size.
*/
int pageSize;
@@ -138,7 +138,7 @@ public class VAFile<V extends NumberVector<?, ?>> extends AbstractRefiningIndex<
protected void initialize(Relation<V> relation, DBIDs ids) {
setPartitions(relation);
for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
- DBID id = iter.getDBID();
+ DBID id = DBIDUtil.deref(iter);
vectorApprox.add(calculateApproximation(id, relation.get(id)));
}
}
@@ -154,7 +154,7 @@ public class VAFile<V extends NumberVector<?, ?>> extends AbstractRefiningIndex<
throw new IllegalArgumentException("Number of partitions must be a power of 2!");
}
- final int dimensions = DatabaseUtil.dimensionality(relation);
+ final int dimensions = RelationUtil.dimensionality(relation);
final int size = relation.size();
splitPositions = new double[dimensions][partitions + 1];
@@ -162,8 +162,7 @@ public class VAFile<V extends NumberVector<?, ?>> extends AbstractRefiningIndex<
double[] tempdata = new double[size];
int j = 0;
for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
- tempdata[j] = relation.get(id).doubleValue(d + 1);
+ tempdata[j] = relation.get(iditer).doubleValue(d);
j += 1;
}
Arrays.sort(tempdata);
@@ -187,20 +186,20 @@ public class VAFile<V extends NumberVector<?, ?>> extends AbstractRefiningIndex<
public VectorApproximation calculateApproximation(DBID id, V dv) {
int approximation[] = new int[dv.getDimensionality()];
for(int d = 0; d < splitPositions.length; d++) {
- final double val = dv.doubleValue(d + 1);
+ final double val = dv.doubleValue(d);
final int lastBorderIndex = splitPositions[d].length - 1;
// Value is below data grid
if(val < splitPositions[d][0]) {
approximation[d] = 0;
if(id != null) {
- log.warning("Vector outside of VAFile grid!");
+ LOG.warning("Vector outside of VAFile grid!");
}
} // Value is above data grid
else if(val > splitPositions[d][lastBorderIndex]) {
approximation[d] = lastBorderIndex - 1;
if(id != null) {
- log.warning("Vector outside of VAFile grid!");
+ LOG.warning("Vector outside of VAFile grid!");
}
} // normal case
else {
@@ -318,7 +317,7 @@ public class VAFile<V extends NumberVector<?, ?>> extends AbstractRefiningIndex<
}
@Override
- public DistanceDBIDResult<DoubleDistance> getRangeForObject(V query, DoubleDistance range) {
+ public DoubleDistanceDBIDList getRangeForObject(V query, DoubleDistance range) {
final double eps = range.doubleValue();
// generate query approximation and lookup table
VectorApproximation queryApprox = calculateApproximation(null, query);
@@ -329,7 +328,7 @@ public class VAFile<V extends NumberVector<?, ?>> extends AbstractRefiningIndex<
// Count a VA file scan
scans += 1;
- GenericDistanceDBIDList<DoubleDistance> result = new GenericDistanceDBIDList<DoubleDistance>();
+ DoubleDistanceDBIDList result = new DoubleDistanceDBIDList();
// Approximation step
for(int i = 0; i < vectorApprox.size(); i++) {
VectorApproximation va = vectorApprox.get(i);
@@ -345,10 +344,10 @@ public class VAFile<V extends NumberVector<?, ?>> extends AbstractRefiningIndex<
// refine the next element
final double dist = refine(va.id, query).doubleValue();
if(dist <= eps) {
- result.add(new DoubleDistanceResultPair(dist, va.id));
+ result.add(dist, va.id);
}
}
- Collections.sort(result);
+ result.sort();
return result;
}
}
@@ -376,15 +375,15 @@ public class VAFile<V extends NumberVector<?, ?>> extends AbstractRefiningIndex<
}
@Override
- public KNNResult<DoubleDistance> getKNNForObject(V query, int k) {
+ public DoubleDistanceKNNList getKNNForObject(V query, int k) {
// generate query approximation and lookup table
VectorApproximation queryApprox = calculateApproximation(null, query);
// Approximative distance function
VALPNormDistance vadist = new VALPNormDistance(p, splitPositions, query, queryApprox);
- // Heap for the kth smallest maximum distance
- Heap<Double> minMaxHeap = new TopBoundedHeap<Double>(k, Collections.reverseOrder());
+ // Heap for the kth smallest maximum distance (yes, we need a max heap!)
+ DoubleMaxHeap minMaxHeap = new DoubleMaxHeap(k+1);
double minMaxDist = Double.POSITIVE_INFINITY;
// Candidates with minDist <= kth maxDist
ArrayList<DoubleObjPair<DBID>> candidates = new ArrayList<DoubleObjPair<DBID>>(vectorApprox.size());
@@ -405,7 +404,7 @@ public class VAFile<V extends NumberVector<?, ?>> extends AbstractRefiningIndex<
candidates.add(new DoubleObjPair<DBID>(minDist, va.id));
// Update candidate pruning heap
- minMaxHeap.add(maxDist);
+ minMaxHeap.add(maxDist, k);
if(minMaxHeap.size() >= k) {
minMaxDist = minMaxHeap.peek();
}
@@ -414,14 +413,14 @@ public class VAFile<V extends NumberVector<?, ?>> extends AbstractRefiningIndex<
Collections.sort(candidates);
// refinement step
- KNNHeap<DoubleDistance> result = new KNNHeap<DoubleDistance>(k);
+ DoubleDistanceKNNHeap result = new DoubleDistanceKNNHeap(k);
// log.fine("candidates size " + candidates.size());
// retrieve accurate distances
for(DoubleObjPair<DBID> va : candidates) {
// Stop when we are sure to have all elements
if(result.size() >= k) {
- double kDist = result.getKNNDistance().doubleValue();
+ double kDist = result.doubleKNNDistance();
if(va.first > kDist) {
break;
}
@@ -429,11 +428,11 @@ public class VAFile<V extends NumberVector<?, ?>> extends AbstractRefiningIndex<
// refine the next element
final double dist = refine(va.second, query).doubleValue();
- result.add(new DoubleDistanceResultPair(dist, va.second));
+ result.add(dist, va.second);
}
- if(log.isDebuggingFinest()) {
- log.finest("query = (" + query + ")");
- log.finest("database: " + vectorApprox.size() + ", candidates: " + candidates.size() + ", results: " + result.size());
+ if(LOG.isDebuggingFinest()) {
+ LOG.finest("query = (" + query + ")");
+ LOG.finest("database: " + vectorApprox.size() + ", candidates: " + candidates.size() + ", results: " + result.size());
}
return result.toKNNList();
@@ -441,16 +440,16 @@ public class VAFile<V extends NumberVector<?, ?>> extends AbstractRefiningIndex<
}
/**
- * Index factory class
+ * Index factory class.
*
* @author Erich Schubert
*
- * @apiviz.stereotype «factory»
+ * @apiviz.stereotype factory
* @apiviz.has VAFile
*
* @param <V> Vector type
*/
- public static class Factory<V extends NumberVector<?, ?>> implements IndexFactory<V, VAFile<V>> {
+ public static class Factory<V extends NumberVector<?>> implements IndexFactory<V, VAFile<V>> {
/**
* Number of partitions to use in each dimension.
*
@@ -458,15 +457,15 @@ public class VAFile<V extends NumberVector<?, ?>> extends AbstractRefiningIndex<
* -vafile.partitions 8
* </pre>
*/
- public static final OptionID PARTITIONS_ID = OptionID.getOrCreateOptionID("vafile.partitions", "Number of partitions to use in each dimension.");
+ public static final OptionID PARTITIONS_ID = new OptionID("vafile.partitions", "Number of partitions to use in each dimension.");
/**
- * Page size
+ * Page size.
*/
int pagesize = 1;
/**
- * Number of partitions
+ * Number of partitions.
*/
int numpart = 2;
@@ -493,7 +492,7 @@ public class VAFile<V extends NumberVector<?, ?>> extends AbstractRefiningIndex<
}
/**
- * Parameterization class
+ * Parameterization class.
*
* @author Erich Schubert
*
@@ -501,23 +500,25 @@ public class VAFile<V extends NumberVector<?, ?>> extends AbstractRefiningIndex<
*/
public static class Parameterizer extends AbstractParameterizer {
/**
- * Page size
+ * Page size.
*/
int pagesize = 1;
/**
- * Number of partitions
+ * Number of partitions.
*/
int numpart = 2;
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- IntParameter pagesizeP = new IntParameter(TreeIndexFactory.PAGE_SIZE_ID, new GreaterConstraint(0), 1024);
+ IntParameter pagesizeP = new IntParameter(TreeIndexFactory.PAGE_SIZE_ID, 1024);
+ pagesizeP.addConstraint(new GreaterConstraint(0));
if(config.grab(pagesizeP)) {
pagesize = pagesizeP.getValue();
}
- IntParameter partitionsP = new IntParameter(Factory.PARTITIONS_ID, new GreaterConstraint(2));
+ IntParameter partitionsP = new IntParameter(Factory.PARTITIONS_ID);
+ partitionsP.addConstraint(new GreaterConstraint(2));
if(config.grab(partitionsP)) {
numpart = partitionsP.getValue();
}
@@ -525,7 +526,7 @@ public class VAFile<V extends NumberVector<?, ?>> extends AbstractRefiningIndex<
@Override
protected Factory<?> makeInstance() {
- return new Factory<NumberVector<?, ?>>(pagesize, numpart);
+ return new Factory<NumberVector<?>>(pagesize, numpart);
}
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/index/vafile/VALPNormDistance.java b/src/de/lmu/ifi/dbs/elki/index/vafile/VALPNormDistance.java
index 77815c97..2d9469e7 100644
--- a/src/de/lmu/ifi/dbs/elki/index/vafile/VALPNormDistance.java
+++ b/src/de/lmu/ifi/dbs/elki/index/vafile/VALPNormDistance.java
@@ -26,23 +26,23 @@ package de.lmu.ifi.dbs.elki.index.vafile;
import de.lmu.ifi.dbs.elki.data.NumberVector;
/**
- * Lp-Norm distance function for partially computed objects
+ * Lp-Norm distance function for partially computed objects.
*
* @author Erich Schubert
*/
public class VALPNormDistance {
/**
- * Value of 1/p for lP norm
+ * Value of 1/p for lP norm.
*/
private final double onebyp;
/**
- * Lookup table for grid cells
+ * Lookup table for grid cells.
*/
private double[][] lookup;
/**
- * Approximation of the query vector
+ * Approximation of the query vector.
*/
private VectorApproximation queryApprox;
@@ -52,9 +52,9 @@ public class VALPNormDistance {
* @param p Value of p
* @param splitPositions Split positions
* @param query Query vector
- * @param queryApprox
+ * @param queryApprox Query approximation
*/
- public VALPNormDistance(double p, double[][] splitPositions, NumberVector<?, ?> query, VectorApproximation queryApprox) {
+ public VALPNormDistance(double p, double[][] splitPositions, NumberVector<?> query, VectorApproximation queryApprox) {
super();
this.onebyp = 1.0 / p;
this.queryApprox = queryApprox;
@@ -62,7 +62,7 @@ public class VALPNormDistance {
}
/**
- * Get the minimum distance contribution of a single dimension
+ * Get the minimum distance contribution of a single dimension.
*
* @param dimension Dimension
* @param vp Vector position
@@ -82,7 +82,7 @@ public class VALPNormDistance {
}
/**
- * Get the minimum distance to approximated vector vec
+ * Get the minimum distance to approximated vector vec.
*
* @param vec Vector approximation
* @return Minimum distance
@@ -98,7 +98,7 @@ public class VALPNormDistance {
}
/**
- * Get the maximum distance contribution of a single dimension
+ * Get the maximum distance contribution of a single dimension.
*
* @param dimension Dimension
* @param vp Vector position
@@ -149,18 +149,18 @@ public class VALPNormDistance {
}
/**
- * Initialize the lookup table
+ * Initialize the lookup table.
*
* @param splitPositions Split positions
* @param query Query vector
* @param p p
*/
- private void initializeLookupTable(double[][] splitPositions, NumberVector<?, ?> query, double p) {
+ private void initializeLookupTable(double[][] splitPositions, NumberVector<?> query, double p) {
final int dimensions = splitPositions.length;
final int bordercount = splitPositions[0].length;
lookup = new double[dimensions][bordercount];
for(int d = 0; d < dimensions; d++) {
- final double val = query.doubleValue(d + 1);
+ final double val = query.doubleValue(d);
for(int i = 0; i < bordercount; i++) {
lookup[d][i] = Math.pow(splitPositions[d][i] - val, p);
}
diff --git a/src/de/lmu/ifi/dbs/elki/index/vafile/VectorApproximation.java b/src/de/lmu/ifi/dbs/elki/index/vafile/VectorApproximation.java
index 4b170eb8..f679cf16 100644
--- a/src/de/lmu/ifi/dbs/elki/index/vafile/VectorApproximation.java
+++ b/src/de/lmu/ifi/dbs/elki/index/vafile/VectorApproximation.java
@@ -26,6 +26,7 @@ package de.lmu.ifi.dbs.elki.index.vafile;
import java.util.Arrays;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.persistent.ByteArrayUtil;
/**
* Object in a VA approximation.
@@ -46,7 +47,7 @@ public class VectorApproximation {
/**
* Constructor.
- *
+ *
* @param id Object represented (may be <code>null</code> for query objects)
* @param approximation Approximation
*/
@@ -95,9 +96,10 @@ public class VectorApproximation {
* @param numberOfPartitions the number of relevant partitions
* @return the cost values (in bytes)
*/
- //nicht gleich in bytes umwandeln, sonst rundungsfehler erst nachdem *anzahl objekte
+ // nicht gleich in bytes umwandeln, sonst rundungsfehler erst nachdem *anzahl
+ // objekte
public static int byteOnDisk(int numberOfDimensions, int numberOfPartitions) {
- //(partition*dimension+id) alles in Bit 32bit für 4 byte id
- return (int) (Math.ceil(numberOfDimensions * ((Math.log(numberOfPartitions) / Math.log(2)))+32) /8);
+ // (partition*dimension+id) alles in Bit 32bit für 4 byte id
+ return (int) (Math.ceil(numberOfDimensions * ((Math.log(numberOfPartitions) / Math.log(2))) + 32) / ByteArrayUtil.SIZE_DOUBLE);
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/logging/CLISmartHandler.java b/src/de/lmu/ifi/dbs/elki/logging/CLISmartHandler.java
index f51e6e7e..b4e6cc09 100644
--- a/src/de/lmu/ifi/dbs/elki/logging/CLISmartHandler.java
+++ b/src/de/lmu/ifi/dbs/elki/logging/CLISmartHandler.java
@@ -149,7 +149,7 @@ public class CLISmartHandler extends Handler {
Collection<Progress> completed = ptrack.removeCompleted();
Collection<Progress> progresses = ptrack.getProgresses();
- StringBuffer buf = new StringBuffer();
+ StringBuilder buf = new StringBuilder();
if(completed.size() > 0) {
buf.append(OutputStreamLogger.CARRIAGE_RETURN);
for(Progress prog : completed) {
@@ -166,7 +166,7 @@ public class CLISmartHandler extends Handler {
first = false;
}
else {
- buf.append(" ");
+ buf.append(' ');
}
// TODO: use formatter, somehow?
prog.appendToBuffer(buf);
diff --git a/src/de/lmu/ifi/dbs/elki/logging/ELKILogRecord.java b/src/de/lmu/ifi/dbs/elki/logging/ELKILogRecord.java
index 98a25b01..560e1b0f 100644
--- a/src/de/lmu/ifi/dbs/elki/logging/ELKILogRecord.java
+++ b/src/de/lmu/ifi/dbs/elki/logging/ELKILogRecord.java
@@ -52,12 +52,12 @@ public class ELKILogRecord extends LogRecord {
/**
* Classes to ignore when finding the relevant caller.
*/
- public final static String[] IGNORE_CLASSES = { Logger.class.getCanonicalName(), Logging.class.getCanonicalName(), LoggingUtil.class.getCanonicalName(), ELKILogRecord.class.getCanonicalName(), AbstractParameterization.class.getCanonicalName(), AbstractApplication.class.getCanonicalName() };
+ public static final String[] IGNORE_CLASSES = { Logger.class.getCanonicalName(), Logging.class.getCanonicalName(), LoggingUtil.class.getCanonicalName(), ELKILogRecord.class.getCanonicalName(), AbstractParameterization.class.getCanonicalName(), AbstractApplication.class.getCanonicalName() };
/**
* Name of this class.
*/
- private final static String START_TRACE_AT = Logger.class.getCanonicalName();
+ private static final String START_TRACE_AT = Logger.class.getCanonicalName();
/**
* Constructor.
diff --git a/src/de/lmu/ifi/dbs/elki/logging/ErrorFormatter.java b/src/de/lmu/ifi/dbs/elki/logging/ErrorFormatter.java
index cd307275..827108aa 100644
--- a/src/de/lmu/ifi/dbs/elki/logging/ErrorFormatter.java
+++ b/src/de/lmu/ifi/dbs/elki/logging/ErrorFormatter.java
@@ -1,5 +1,28 @@
package de.lmu.ifi.dbs.elki.logging;
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
import java.util.logging.Formatter;
import java.util.logging.LogRecord;
@@ -17,7 +40,7 @@ public class ErrorFormatter extends Formatter {
*
* TODO: make configurable via logging.properties
*/
- public static String[] PRUNE = {//
+ public static final String[] PRUNE = {//
"de.lmu.ifi.dbs.elki.gui.minigui.MiniGUI", //
"de.lmu.ifi.dbs.elki.KDDTask", //
"java.awt.event.", //
@@ -40,15 +63,20 @@ public class ErrorFormatter extends Formatter {
@Override
public String format(LogRecord record) {
- if(record instanceof ProgressLogRecord) {
+ if (record instanceof ProgressLogRecord) {
return record.getMessage();
}
String msg = record.getMessage();
- StringBuffer buf = new StringBuffer(msg);
- if(!msg.endsWith(OutputStreamLogger.NEWLINE)) {
- buf.append(OutputStreamLogger.NEWLINE);
+ StringBuilder buf = new StringBuilder();
+ if (msg != null) {
+ buf.append(msg);
+ if (!msg.endsWith(OutputStreamLogger.NEWLINE)) {
+ buf.append(OutputStreamLogger.NEWLINE);
+ }
+ } else {
+ buf.append("null" + OutputStreamLogger.NEWLINE);
}
- if(record.getThrown() != null) {
+ if (record.getThrown() != null) {
appendCauses(buf, record.getThrown());
}
return buf.toString();
@@ -60,31 +88,31 @@ public class ErrorFormatter extends Formatter {
* @param buf Buffer to append to
* @param thrown Throwable to format.
*/
- private void appendCauses(StringBuffer buf, Throwable thrown) {
+ private void appendCauses(StringBuilder buf, Throwable thrown) {
buf.append(thrown.toString()).append(OutputStreamLogger.NEWLINE);
StackTraceElement[] stack = thrown.getStackTrace();
int end = stack.length - 1;
- prune: for(; end >= 0; end--) {
+ prune: for (; end >= 0; end--) {
String cn = stack[end].getClassName();
- for(String pat : PRUNE) {
- if(cn.startsWith(pat)) {
+ for (String pat : PRUNE) {
+ if (cn.startsWith(pat)) {
continue prune;
}
}
break;
}
- if(end <= 0) {
+ if (end <= 0) {
end = stack.length - 1;
}
- for(int i = 0; i <= end; i++) {
+ for (int i = 0; i <= end; i++) {
buf.append("\tat ").append(stack[i]).append(OutputStreamLogger.NEWLINE);
}
- if(end < stack.length - 1) {
+ if (end < stack.length - 1) {
buf.append("\tat [...]").append(OutputStreamLogger.NEWLINE);
}
- if(thrown.getCause() != null) {
+ if (thrown.getCause() != null) {
buf.append("Caused by: ");
appendCauses(buf, thrown.getCause());
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/logging/Logging.java b/src/de/lmu/ifi/dbs/elki/logging/Logging.java
index ea412905..807d4b6e 100644
--- a/src/de/lmu/ifi/dbs/elki/logging/Logging.java
+++ b/src/de/lmu/ifi/dbs/elki/logging/Logging.java
@@ -61,7 +61,7 @@ public class Logging {
private static HashMap<String, Logging> loggers = new HashMap<String, Logging>();
/**
- * Wrapped logger
+ * Wrapped logger of this instance - not static!
*/
private final Logger logger;
@@ -108,9 +108,9 @@ public class Logging {
public boolean isLoggable(Level lev) {
return logger.isLoggable(lev);
}
-
+
/**
- * Test whether to log 'verbose'.
+ * Test whether to log 'verbose' aka 'info'.
*
* @return true if verbose
*/
@@ -119,6 +119,15 @@ public class Logging {
}
/**
+ * Test whether to log 'info' aka 'verbose'.
+ *
+ * @return true if verbose
+ */
+ public boolean isInfo() {
+ return logger.isLoggable(Level.INFO);
+ }
+
+ /**
* Test whether to log 'debug' at 'FINE' level.
*
* This is the same as {@link #isDebuggingFine}
@@ -253,6 +262,29 @@ public class Logging {
}
/**
+ * Log a message at the 'info' ('verbose') level.
+ *
+ * You should check isVerbose() before building the message.
+ *
+ * @param message Informational log message.
+ * @param e Exception
+ */
+ public void info(CharSequence message, Throwable e) {
+ log(Level.INFO, message, e);
+ }
+
+ /**
+ * Log a message at the 'info' ('verbose') level.
+ *
+ * You should check isVerbose() before building the message.
+ *
+ * @param message Informational log message.
+ */
+ public void info(CharSequence message) {
+ log(Level.INFO, message);
+ }
+
+ /**
* Log a message at the 'fine' debugging level.
*
* You should check isDebugging() before building the message.
@@ -429,7 +461,8 @@ public class Logging {
* @param e Exception
*/
public void exception(Throwable e) {
- log(Level.SEVERE, e.getMessage(), e);
+ final String msg = e.getMessage();
+ log(Level.SEVERE, msg != null ? msg : "An exception occurred.", e);
}
/**
diff --git a/src/de/lmu/ifi/dbs/elki/logging/LoggingConfiguration.java b/src/de/lmu/ifi/dbs/elki/logging/LoggingConfiguration.java
index a5f507a7..b0e7936b 100644
--- a/src/de/lmu/ifi/dbs/elki/logging/LoggingConfiguration.java
+++ b/src/de/lmu/ifi/dbs/elki/logging/LoggingConfiguration.java
@@ -112,7 +112,7 @@ public final class LoggingConfiguration {
InputStream cfgdata2 = FileUtil.openSystemFile(cfgfile);
Properties cfgprop = new Properties();
cfgprop.load(cfgdata2);
- DEBUG = Boolean.valueOf(cfgprop.getProperty("debug"));
+ DEBUG = Boolean.parseBoolean(cfgprop.getProperty("debug"));
logger.info("Logging configuration read.");
}
@@ -150,10 +150,10 @@ public final class LoggingConfiguration {
}
else {
// increase to warning level if it was INFO.
- if(logger1.getLevel() != null || logger1.getLevel() == Level.INFO) {
+ if(logger1.getLevel() == null || Level.INFO.equals(logger1.getLevel())) {
logger1.setLevel(Level.WARNING);
}
- if(logger2.getLevel() != null || logger2.getLevel() == Level.INFO) {
+ if(logger2.getLevel() == null || Level.INFO.equals(logger2.getLevel())) {
logger2.setLevel(Level.WARNING);
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/logging/LoggingUtil.java b/src/de/lmu/ifi/dbs/elki/logging/LoggingUtil.java
index 808a648c..1a48fc7a 100644
--- a/src/de/lmu/ifi/dbs/elki/logging/LoggingUtil.java
+++ b/src/de/lmu/ifi/dbs/elki/logging/LoggingUtil.java
@@ -53,7 +53,7 @@ public final class LoggingUtil {
* @param message Message to log.
* @param e Exception to report.
*/
- public final static void logExpensive(Level level, String message, Throwable e) {
+ public static final void logExpensive(Level level, String message, Throwable e) {
String[] caller = inferCaller();
if(caller != null) {
Logger logger = Logger.getLogger(caller[0]);
@@ -74,7 +74,7 @@ public final class LoggingUtil {
* @param level Logging level
* @param message Message to log.
*/
- public final static void logExpensive(Level level, String message) {
+ public static final void logExpensive(Level level, String message) {
LogRecord rec = new ELKILogRecord(level, message);
String[] caller = inferCaller();
if(caller != null) {
@@ -93,7 +93,7 @@ public final class LoggingUtil {
*
* @param e Exception to log
*/
- public final static void exception(Throwable e) {
+ public static final void exception(Throwable e) {
logExpensive(Level.SEVERE, e.getMessage(), e);
}
@@ -103,7 +103,7 @@ public final class LoggingUtil {
* @param message Exception message, may be null (defaults to e.getMessage())
* @param e causing exception
*/
- public final static void exception(String message, Throwable e) {
+ public static final void exception(String message, Throwable e) {
if(message == null && e != null) {
message = e.getMessage();
}
@@ -115,7 +115,7 @@ public final class LoggingUtil {
*
* @param message Warning message.
*/
- public final static void warning(String message) {
+ public static final void warning(String message) {
logExpensive(Level.WARNING, message);
}
@@ -125,7 +125,7 @@ public final class LoggingUtil {
* @param message Warning message, may be null (defaults to e.getMessage())
* @param e causing exception
*/
- public final static void warning(String message, Throwable e) {
+ public static final void warning(String message, Throwable e) {
if(message == null && e != null) {
message = e.getMessage();
}
@@ -137,7 +137,7 @@ public final class LoggingUtil {
*
* @param message Warning message.
*/
- public final static void message(String message) {
+ public static final void message(String message) {
logExpensive(Level.INFO, message);
}
@@ -147,7 +147,7 @@ public final class LoggingUtil {
* @param message Warning message, may be null (defaults to e.getMessage())
* @param e causing exception
*/
- public final static void message(String message, Throwable e) {
+ public static final void message(String message, Throwable e) {
if(message == null && e != null) {
message = e.getMessage();
}
@@ -163,7 +163,7 @@ public final class LoggingUtil {
*
* @return calling class name and calling method name
*/
- private final static String[] inferCaller() {
+ private static final String[] inferCaller() {
StackTraceElement stack[] = (new Throwable()).getStackTrace();
int ix = 0;
while(ix < stack.length) {
diff --git a/src/de/lmu/ifi/dbs/elki/logging/progress/AbstractProgress.java b/src/de/lmu/ifi/dbs/elki/logging/progress/AbstractProgress.java
index 070dbdb9..1d196533 100644
--- a/src/de/lmu/ifi/dbs/elki/logging/progress/AbstractProgress.java
+++ b/src/de/lmu/ifi/dbs/elki/logging/progress/AbstractProgress.java
@@ -109,7 +109,7 @@ public abstract class AbstractProgress implements Progress {
* @return Buffer the data was serialized to.
*/
@Override
- public abstract StringBuffer appendToBuffer(StringBuffer buf);
+ public abstract StringBuilder appendToBuffer(StringBuilder buf);
/**
* Returns a String representation of the progress suitable as a message for
@@ -119,7 +119,7 @@ public abstract class AbstractProgress implements Progress {
*/
@Override
public String toString() {
- StringBuffer message = new StringBuffer();
+ StringBuilder message = new StringBuilder();
appendToBuffer(message);
return message.toString();
}
diff --git a/src/de/lmu/ifi/dbs/elki/logging/progress/FiniteProgress.java b/src/de/lmu/ifi/dbs/elki/logging/progress/FiniteProgress.java
index 6d70b560..1ddc02f7 100644
--- a/src/de/lmu/ifi/dbs/elki/logging/progress/FiniteProgress.java
+++ b/src/de/lmu/ifi/dbs/elki/logging/progress/FiniteProgress.java
@@ -82,10 +82,10 @@ public class FiniteProgress extends AbstractProgress {
*/
@Override
public void setProcessed(int processed) throws IllegalArgumentException {
- if(processed > total) {
+ if (processed > total) {
throw new IllegalArgumentException(processed + " exceeds total: " + total);
}
- if(processed < 0) {
+ if (processed < 0) {
throw new IllegalArgumentException("Negative number of processed: " + processed);
}
super.setProcessed(processed);
@@ -98,20 +98,20 @@ public class FiniteProgress extends AbstractProgress {
* @return Buffer the data was serialized to.
*/
@Override
- public StringBuffer appendToBuffer(StringBuffer buf) {
+ public StringBuilder appendToBuffer(StringBuilder buf) {
String processedString = Integer.toString(getProcessed());
int percentage = (int) (getProcessed() * 100.0 / total);
buf.append(getTask());
buf.append(": ");
- for(int i = 0; i < totalLength - processedString.length(); i++) {
+ for (int i = 0; i < totalLength - processedString.length(); i++) {
buf.append(' ');
}
buf.append(getProcessed());
buf.append(" [");
- if(percentage < 100) {
+ if (percentage < 100) {
buf.append(' ');
}
- if(percentage < 10) {
+ if (percentage < 10) {
buf.append(' ');
}
buf.append(percentage);
@@ -142,10 +142,10 @@ public class FiniteProgress extends AbstractProgress {
* @param logger Logger to report to.
*/
public void ensureCompleted(Logging logger) {
- if(!isComplete()) {
- logger.warning("Progress had not completed automatically as expected.", new Throwable());
+ if (!isComplete()) {
+ logger.warning("Progress had not completed automatically as expected: " + getProcessed() + "/" + total, new Throwable());
setProcessed(getTotal());
logger.progress(this);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/logging/progress/IndefiniteProgress.java b/src/de/lmu/ifi/dbs/elki/logging/progress/IndefiniteProgress.java
index 9a13947c..1dd61674 100644
--- a/src/de/lmu/ifi/dbs/elki/logging/progress/IndefiniteProgress.java
+++ b/src/de/lmu/ifi/dbs/elki/logging/progress/IndefiniteProgress.java
@@ -61,7 +61,7 @@ public class IndefiniteProgress extends AbstractProgress {
* Serialize 'indefinite' progress.
*/
@Override
- public StringBuffer appendToBuffer(StringBuffer buf) {
+ public StringBuilder appendToBuffer(StringBuilder buf) {
buf.append(getTask());
buf.append(": ");
buf.append(getProcessed());
diff --git a/src/de/lmu/ifi/dbs/elki/logging/progress/Progress.java b/src/de/lmu/ifi/dbs/elki/logging/progress/Progress.java
index 972f0c58..74125798 100644
--- a/src/de/lmu/ifi/dbs/elki/logging/progress/Progress.java
+++ b/src/de/lmu/ifi/dbs/elki/logging/progress/Progress.java
@@ -23,7 +23,6 @@ package de.lmu.ifi.dbs.elki.logging.progress;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-
/**
* Generic Progress logging interface.
*
@@ -36,14 +35,14 @@ public interface Progress {
* @param buf Buffer to serialize to
* @return Buffer the data was serialized to.
*/
- public StringBuffer appendToBuffer(StringBuffer buf);
+ StringBuilder appendToBuffer(StringBuilder buf);
/**
* Test whether a progress is complete (and thus doesn't need to be shown anymore)
*
* @return Whether the progress was completed.
*/
- public boolean isComplete();
+ boolean isComplete();
/**
* Returns a String representation of the progress suitable as a message for
@@ -52,5 +51,5 @@ public interface Progress {
* @see java.lang.Object#toString()
*/
@Override
- public String toString();
+ String toString();
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/logging/progress/ProgressTracker.java b/src/de/lmu/ifi/dbs/elki/logging/progress/ProgressTracker.java
index d39e3107..ad3f7c37 100644
--- a/src/de/lmu/ifi/dbs/elki/logging/progress/ProgressTracker.java
+++ b/src/de/lmu/ifi/dbs/elki/logging/progress/ProgressTracker.java
@@ -28,7 +28,6 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
-import java.util.Vector;
/**
* Class to keep track of "alive" progresses.
@@ -41,7 +40,7 @@ public class ProgressTracker {
/**
* Set of potentially active progresses.
*/
- private Vector<WeakReference<Progress>> progresses = new Vector<WeakReference<Progress>>();
+ private ArrayList<WeakReference<Progress>> progresses = new ArrayList<WeakReference<Progress>>();
/**
* Get a list of progresses tracked.
diff --git a/src/de/lmu/ifi/dbs/elki/logging/progress/StepProgress.java b/src/de/lmu/ifi/dbs/elki/logging/progress/StepProgress.java
index df6e3b13..1a0d3101 100644
--- a/src/de/lmu/ifi/dbs/elki/logging/progress/StepProgress.java
+++ b/src/de/lmu/ifi/dbs/elki/logging/progress/StepProgress.java
@@ -62,15 +62,15 @@ public class StepProgress extends FiniteProgress {
// No constructor with auto logging - call beginStep() first
@Override
- public StringBuffer appendToBuffer(StringBuffer buf) {
+ public StringBuilder appendToBuffer(StringBuilder buf) {
buf.append(super.getTask());
if (isComplete()) {
buf.append(": complete.");
} else {
- buf.append(" #").append(getProcessed()+1).append("/").append(getTotal());
+ buf.append(" #").append(getProcessed()+1).append('/').append(getTotal());
buf.append(": ").append(getStepTitle());
}
- buf.append("\n");
+ buf.append('\n');
return buf;
}
diff --git a/src/de/lmu/ifi/dbs/elki/math/DoubleMinMax.java b/src/de/lmu/ifi/dbs/elki/math/DoubleMinMax.java
index cedfb389..56814649 100644
--- a/src/de/lmu/ifi/dbs/elki/math/DoubleMinMax.java
+++ b/src/de/lmu/ifi/dbs/elki/math/DoubleMinMax.java
@@ -102,7 +102,7 @@ public class DoubleMinMax extends DoubleDoublePair {
*/
public void put(Collection<Double> data) {
for(Double value : data) {
- this.put(value);
+ this.put(value.doubleValue());
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/math/GeoUtil.java b/src/de/lmu/ifi/dbs/elki/math/GeoUtil.java
new file mode 100644
index 00000000..226d8112
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/math/GeoUtil.java
@@ -0,0 +1,670 @@
+package de.lmu.ifi.dbs.elki.math;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
+
+/**
+ * Class with utility functions for geographic computations.
+ *
+ * The majority of formulas are adapted from:
+ * <p>
+ * Ed Williams<br />
+ * Aviation Formulary<br />
+ * Online: http://williams.best.vwh.net/avform.htm
+ * </p>
+ *
+ * TODO: add ellipsoid version of Vinentry formula.
+ *
+ * @author Erich Schubert
+ * @author Niels Dörre
+ */
+@Reference(authors = "Ed Williams", title = "Aviation Formulary", booktitle = "", url = "http://williams.best.vwh.net/avform.htm")
+public final class GeoUtil {
+ /**
+ * Earth radius approximation in km.
+ */
+ public static final double EARTH_RADIUS = 6371.009; // km.
+
+ /**
+ * Radius of the WGS84 Ellipsoid in km.
+ */
+ public static final double WGS84_RADIUS = 6378.137; // km
+
+ /**
+ * Flattening of the WGS84 Ellipsoid.
+ */
+ public static final double WGS84_FLATTENING = 0.00335281066474748;
+
+ /**
+ * Eccentricity squared of the WGS84 Ellipsoid
+ */
+ public static final double WGS84_ECCENTRICITY_SQUARED = 2 * WGS84_FLATTENING - (WGS84_FLATTENING * WGS84_FLATTENING);
+
+ /**
+ * Dummy constructor. Do not instantiate.
+ */
+ private GeoUtil() {
+ // Use static methods. Do not intantiate
+ }
+
+ /**
+ * Compute the approximate on-earth-surface distance of two points using the
+ * Haversine formula
+ *
+ * Complexity: 5 trigonometric functions, 2 sqrt.
+ *
+ * Reference:
+ * <p>
+ * R. W. Sinnott,<br/>
+ * Virtues of the Haversine<br />
+ * Sky and telescope, 68-2, 1984
+ * </p>
+ *
+ * @param lat1 Latitude of first point in degree
+ * @param lon1 Longitude of first point in degree
+ * @param lat2 Latitude of second point in degree
+ * @param lon2 Longitude of second point in degree
+ * @return Distance in km (approximately)
+ */
+ @Reference(authors = "Sinnott, R. W.", title = "Virtues of the Haversine", booktitle = "Sky and telescope, 68-2, 1984")
+ public static double haversineFormulaDeg(double lat1, double lon1, double lat2, double lon2) {
+ // Convert to radians:
+ lat1 = MathUtil.deg2rad(lat1);
+ lat2 = MathUtil.deg2rad(lat2);
+ lon1 = MathUtil.deg2rad(lon1);
+ lon2 = MathUtil.deg2rad(lon2);
+ return haversineFormulaRad(lat1, lon1, lat2, lon2);
+ }
+
+ /**
+ * Compute the approximate on-earth-surface distance of two points using the
+ * Haversine formula
+ *
+ * Complexity: 5 trigonometric functions, 2 sqrt.
+ *
+ * Reference:
+ * <p>
+ * R. W. Sinnott,<br/>
+ * Virtues of the Haversine<br />
+ * Sky and telescope, 68-2, 1984
+ * </p>
+ *
+ * @param lat1 Latitude of first point in degree
+ * @param lon1 Longitude of first point in degree
+ * @param lat2 Latitude of second point in degree
+ * @param lon2 Longitude of second point in degree
+ * @return Distance in km (approximately)
+ */
+ @Reference(authors = "Sinnott, R. W.", title = "Virtues of the Haversine", booktitle = "Sky and telescope, 68-2, 1984")
+ public static double haversineFormulaRad(double lat1, double lon1, double lat2, double lon2) {
+ // Haversine formula, higher precision at < 1 meters but maybe issues at
+ // antipodal points.
+ final double slat = Math.sin((lat1 - lat2) * .5);
+ final double slon = Math.sin((lon1 - lon2) * .5);
+ final double a = slat * slat + slon * slon * Math.cos(lat1) * Math.cos(lat2);
+ final double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
+ return EARTH_RADIUS * c;
+ }
+
+ /**
+ * Compute the approximate on-earth-surface distance of two points.
+ *
+ * Uses Vincenty's Formula for the spherical case, which does not require
+ * iterations.
+ *
+ * Complexity: 7 trigonometric functions, 1 sqrt.
+ *
+ * Reference:
+ * <p>
+ * T. Vincenty<br />
+ * Direct and inverse solutions of geodesics on the ellipsoid with application
+ * of nested equations<br />
+ * Survey review 23 176, 1975
+ * </p>
+ *
+ * @param lat1 Latitude of first point in degree
+ * @param lon1 Longitude of first point in degree
+ * @param lat2 Latitude of second point in degree
+ * @param lon2 Longitude of second point in degree
+ * @return Distance in km (approximately)
+ */
+ @Reference(authors = "T. Vincenty", title = "Direct and inverse solutions of geodesics on the ellipsoid with application of nested equations", booktitle = "Survey review 23 176, 1975", url = "http://www.ngs.noaa.gov/PUBS_LIB/inverse.pdf")
+ public static double sphericalVincentyFormulaDeg(double lat1, double lon1, double lat2, double lon2) {
+ // Work in radians
+ lat1 = MathUtil.deg2rad(lat1);
+ lat2 = MathUtil.deg2rad(lat2);
+ lon1 = MathUtil.deg2rad(lon1);
+ lon2 = MathUtil.deg2rad(lon2);
+ return sphericalVincentyFormulaRad(lat1, lon1, lat2, lon2);
+ }
+
+ /**
+ * Compute the approximate on-earth-surface distance of two points.
+ *
+ * Uses Vincenty's Formula for the spherical case, which does not require
+ * iterations.
+ *
+ * Complexity: 7 trigonometric functions, 1 sqrt.
+ *
+ * Reference:
+ * <p>
+ * T. Vincenty<br />
+ * Direct and inverse solutions of geodesics on the ellipsoid with application
+ * of nested equations<br />
+ * Survey review 23 176, 1975
+ * </p>
+ *
+ * @param lat1 Latitude of first point in degree
+ * @param lon1 Longitude of first point in degree
+ * @param lat2 Latitude of second point in degree
+ * @param lon2 Longitude of second point in degree
+ * @return Distance in km (approximately)
+ */
+ @Reference(authors = "T. Vincenty", title = "Direct and inverse solutions of geodesics on the ellipsoid with application of nested equations", booktitle = "Survey review 23 176, 1975", url = "http://www.ngs.noaa.gov/PUBS_LIB/inverse.pdf")
+ public static double sphericalVincentyFormulaRad(double lat1, double lon1, double lat2, double lon2) {
+ // Delta
+ final double dlon = lon1 - lon2;
+
+ // Spherical special case of Vincenty's formula - no iterations needed
+ final double slat1 = Math.sin(lat1);
+ final double slat2 = Math.sin(lat2);
+ final double slond = Math.sin(dlon * .5);
+ final double clat1 = Math.cos(lat1);
+ final double clat2 = Math.cos(lat2);
+ final double clond = Math.cos(dlon * .5);
+ final double a = clat2 * slond;
+ final double b = (clat1 * slat2) - (slat1 * clat2 * clond);
+ final double d = Math.atan2(Math.sqrt(a * a + b * b), slat1 * slat2 + clat1 * clat2 * clond);
+ return EARTH_RADIUS * d;
+ }
+
+ /**
+ * Compute the cross-track distance.
+ *
+ * @param lat1 Latitude of starting point.
+ * @param lon1 Longitude of starting point.
+ * @param lat2 Latitude of destination point.
+ * @param lon2 Longitude of destination point.
+ * @param latQ Latitude of query point.
+ * @param lonQ Longitude of query point.
+ * @param dist1Q Distance from starting point to query point in km.
+ * @return Cross-track distance in km. May be negative - this gives the side.
+ */
+ public static double crossTrackDistanceDeg(double lat1, double lon1, double lat2, double lon2, double latQ, double lonQ, double dist1Q) {
+ // Convert to radians.
+ lat1 = MathUtil.deg2rad(lat1);
+ latQ = MathUtil.deg2rad(latQ);
+ lat2 = MathUtil.deg2rad(lat2);
+ lon1 = MathUtil.deg2rad(lon1);
+ lonQ = MathUtil.deg2rad(lonQ);
+ lon2 = MathUtil.deg2rad(lon2);
+ return crossTrackDistanceRad(lat1, lon1, lat2, lon2, latQ, lonQ, dist1Q);
+ }
+
+ /**
+ * Compute the cross-track distance.
+ *
+ * @param lat1 Latitude of starting point.
+ * @param lon1 Longitude of starting point.
+ * @param lat2 Latitude of destination point.
+ * @param lon2 Longitude of destination point.
+ * @param latQ Latitude of query point.
+ * @param lonQ Longitude of query point.
+ * @param dist1Q Distance from starting point to query point in km.
+ * @return Cross-track distance in km. May be negative - this gives the side.
+ */
+ public static double crossTrackDistanceRad(double lat1, double lon1, double lat2, double lon2, double latQ, double lonQ, double dist1Q) {
+ final double dlon12 = lon2 - lon1;
+ final double dlon1Q = lonQ - lon1;
+
+ // Compute trigonometric functions only once.
+ final double slat1 = Math.sin(lat1);
+ final double slatQ = Math.sin(latQ);
+ final double slat2 = Math.sin(lat2);
+ final double clat1 = Math.cos(lat1);
+ final double clatQ = Math.cos(latQ);
+ final double clat2 = Math.cos(lat2);
+
+ // Compute the course
+ final double crs12, crs1Q;
+ {
+ // y = sin(dlon) * cos(lat2)
+ double yE = Math.sin(dlon12) * clat2;
+ double yQ = Math.sin(dlon1Q) * clatQ;
+
+ // x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(dlon)
+ double xE = clat1 * slat2 - slat1 * clat2 * Math.cos(dlon12);
+ double xQ = clat1 * slatQ - slat1 * clatQ * Math.cos(dlon1Q);
+
+ crs12 = Math.atan2(yE, xE);
+ crs1Q = Math.atan2(yQ, xQ);
+ }
+
+ // Calculate cross-track distance
+ return EARTH_RADIUS * Math.asin(Math.sin(dist1Q / EARTH_RADIUS) * Math.sin(crs1Q - crs12));
+ }
+
+ /**
+ * Compute the cross-track distance.
+ *
+ * XTD = asin(sin(dist_1Q)*sin(crs_1Q-crs_12))
+ *
+ * @param lat1 Latitude of starting point.
+ * @param lon1 Longitude of starting point.
+ * @param lat2 Latitude of destination point.
+ * @param lon2 Longitude of destination point.
+ * @param latQ Latitude of query point.
+ * @param lonQ Longitude of query point.
+ * @return Cross-track distance in km. May be negative - this gives the side.
+ */
+ public static double crossTrackDistance(double lat1, double lon1, double lat2, double lon2, double latQ, double lonQ) {
+ // Convert to radians.
+ lat1 = MathUtil.deg2rad(lat1);
+ latQ = MathUtil.deg2rad(latQ);
+ lat2 = MathUtil.deg2rad(lat2);
+ lon1 = MathUtil.deg2rad(lon1);
+ lonQ = MathUtil.deg2rad(lonQ);
+ lon2 = MathUtil.deg2rad(lon2);
+ return crossTrackDistanceRad(lat1, lon1, lat2, lon2, latQ, lonQ);
+ }
+
+ /**
+ * Compute the cross-track distance.
+ *
+ * XTD = asin(sin(dist_SQ)*sin(crs_SQ-crs_SE))
+ *
+ * @param lat1 Latitude of starting point.
+ * @param lon1 Longitude of starting point.
+ * @param lat2 Latitude of destination point.
+ * @param lon2 Longitude of destination point.
+ * @param latQ Latitude of query point.
+ * @param lonQ Longitude of query point.
+ * @return Cross-track distance in km. May be negative - this gives the side.
+ */
+ public static double crossTrackDistanceRad(double lat1, double lon1, double lat2, double lon2, double latQ, double lonQ) {
+ final double dlon12 = lon2 - lon1;
+ final double dlon1Q = lonQ - lon1;
+
+ // Compute trigonometric functions only once.
+ final double clat1 = Math.cos(lat1);
+ final double clatQ = Math.cos(latQ);
+ final double clat2 = Math.cos(lat2);
+ final double slat1 = Math.sin(lat1);
+ final double slatQ = Math.sin(latQ);
+ final double slat2 = Math.sin(lat2);
+
+ // Haversine formula, higher precision at < 1 meters but maybe issues at
+ // antipodal points - we do not yet multiply with the radius!
+ double angDist1Q;
+ {
+ final double slat = Math.sin((latQ - lat1) * .5);
+ final double slon = Math.sin(dlon1Q * .5);
+ final double a = slat * slat + slon * slon * clat1 * clatQ;
+ angDist1Q = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
+ }
+
+ // Compute the course
+ final double crs12, crs1Q;
+ {
+ // y = sin(dlon) * cos(lat2)
+ double yE = Math.sin(dlon12) * clat2;
+ double yQ = Math.sin(dlon1Q) * clatQ;
+
+ // x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(dlon)
+ double xE = clat1 * slat2 - slat1 * clat2 * Math.cos(dlon12);
+ double xQ = clat1 * slatQ - slat1 * clatQ * Math.cos(dlon1Q);
+
+ crs12 = Math.atan2(yE, xE);
+ crs1Q = Math.atan2(yQ, xQ);
+ }
+
+ // Calculate cross-track distance
+ return EARTH_RADIUS * Math.asin(Math.sin(angDist1Q) * Math.sin(crs1Q - crs12));
+ }
+
+ /**
+ * The along track distance, is the distance from S to Q along the track S to
+ * E.
+ *
+ * ATD=acos(cos(dist_1Q)/cos(XTD))
+ *
+ * FIXME: can we get a proper sign into this?
+ *
+ * @param lat1 Latitude of starting point.
+ * @param lon1 Longitude of starting point.
+ * @param lat2 Latitude of destination point.
+ * @param lon2 Longitude of destination point.
+ * @param latQ Latitude of query point.
+ * @param lonQ Longitude of query point.
+ * @return Along-track distance in km. May be negative - this gives the side.
+ */
+ public static double alongTrackDistance(double lat1, double lon1, double lat2, double lon2, double latQ, double lonQ) {
+ double dist1Q = haversineFormulaDeg(lat1, lon1, latQ, lonQ);
+ double ctd = crossTrackDistanceDeg(lat1, lon1, lat2, lon2, latQ, lonQ, dist1Q);
+ return alongTrackDistance(lat1, lon1, lat2, lon2, latQ, lonQ, dist1Q, ctd);
+ }
+
+ /**
+ * The along track distance, is the distance from S to Q along the track S to
+ * E.
+ *
+ * ATD=acos(cos(dist_SQ)/cos(XTD))
+ *
+ * FIXME: can we get a proper sign into this?
+ *
+ * @param lat1 Latitude of starting point.
+ * @param lon1 Longitude of starting point.
+ * @param lat2 Latitude of destination point.
+ * @param lon2 Longitude of destination point.
+ * @param latQ Latitude of query point.
+ * @param lonQ Longitude of query point.
+ * @param dist1Q Distance S to Q
+ * @param ctd Cross-track-distance
+ * @return Along-track distance in km. May be negative - this gives the side.
+ */
+ public static double alongTrackDistance(double lat1, double lon1, double lat2, double lon2, double latQ, double lonQ, double dist1Q, double ctd) {
+ // TODO: optimize the sign computation!
+ int sign = Math.abs(bearing(lat1, lon1, lat2, lon2) - bearing(lat1, lon1, latQ, lonQ)) < MathUtil.HALFPI ? +1 : -1;
+ return sign * EARTH_RADIUS * Math.acos(Math.cos(dist1Q / EARTH_RADIUS) / Math.cos(ctd / EARTH_RADIUS));
+ // TODO: for short distances, use this instead?
+ // asin(sqrt( (sin(dist_1Q))^2 - (sin(XTD))^2 )/cos(XTD))
+ }
+
+ /**
+ * Point to rectangle minimum distance.
+ *
+ * Complexity:
+ * <ul>
+ * <li>Trivial cases (on longitude slice): no trigonometric functions.</li>
+ * <li>Cross-track case: 10+2 trig</li>
+ * <li>Corner case: 10+3 trig, 2 sqrt</li>
+ * </ul>
+ *
+ * @param plat Latitude of query point.
+ * @param plng Longitude of query point.
+ * @param rminlat Min latitude of rectangle.
+ * @param rminlng Min longitude of rectangle.
+ * @param rmaxlat Max latitude of rectangle.
+ * @param rmaxlng Max longitude of rectangle.
+ * @return Distance
+ */
+ public static double latlngMinDistDeg(double plat, double plng, double rminlat, double rminlng, double rmaxlat, double rmaxlng) {
+ // Convert to radians.
+ plat = MathUtil.deg2rad(plat);
+ plng = MathUtil.deg2rad(plng);
+ rminlat = MathUtil.deg2rad(rminlat);
+ rminlng = MathUtil.deg2rad(rminlng);
+ rmaxlat = MathUtil.deg2rad(rmaxlat);
+ rmaxlng = MathUtil.deg2rad(rmaxlng);
+
+ return latlngMinDistRad(plat, plng, rminlat, rminlng, rmaxlat, rmaxlng);
+ }
+
+ /**
+ * Point to rectangle minimum distance.
+ *
+ * Complexity:
+ * <ul>
+ * <li>Trivial cases (on longitude slice): no trigonometric functions.</li>
+ * <li>Cross-track case: 10+2 trig</li>
+ * <li>Corner case: 10+3 trig, 2 sqrt</li>
+ * </ul>
+ *
+ * @param plat Latitude of query point.
+ * @param plng Longitude of query point.
+ * @param rminlat Min latitude of rectangle.
+ * @param rminlng Min longitude of rectangle.
+ * @param rmaxlat Max latitude of rectangle.
+ * @param rmaxlng Max longitude of rectangle.
+ * @return Distance
+ */
+ public static double latlngMinDistRad(double plat, double plng, double rminlat, double rminlng, double rmaxlat, double rmaxlng) {
+ // FIXME: handle rectangles crossing the +-180 deg boundary correctly!
+
+ // Degenerate rectangles:
+ if ((rminlat >= rmaxlat) && (rminlng >= rmaxlng)) {
+ return haversineFormulaRad(rminlat, rminlng, plat, plng);
+ }
+
+ // The simplest case is when the query point is in the same "slice":
+ if (rminlng <= plng && plng <= rmaxlng) {
+ // Inside rectangle:
+ if (rminlat <= plat && plat <= rmaxlat) {
+ return 0;
+ }
+ // South:
+ if (plat < rminlat) {
+ return EARTH_RADIUS * (rminlat - plat);
+ } else {
+ // plat > rmaxlat
+ return EARTH_RADIUS * (plat - rmaxlat);
+ }
+ }
+
+ // Determine whether going east or west is shorter.
+ double lngE = rminlng - plng;
+ lngE += (lngE < 0) ? MathUtil.TWOPI : 0;
+ double lngW = rmaxlng - plng;
+ lngW -= (lngW > 0) ? MathUtil.TWOPI : 0;
+
+ // Compute sine and cosine values we will certainly need below:
+ final double slatQ = Math.sin(plat);
+ final double clatQ = Math.cos(plat);
+ final double slatN = Math.sin(rmaxlat);
+ final double clatN = Math.cos(rmaxlat);
+ final double slatS = Math.sin(rminlat);
+ final double clatS = Math.cos(rminlat);
+
+ // East, to min edge:
+ if (lngE <= -lngW) {
+ final double slngD = Math.sin(lngE);
+ final double clngD = Math.cos(lngE);
+
+ // Bearing to south
+ // Math.atan2(slngD * clatS, clatQ * slatS - slatQ * clatS * clngD);
+ // Bearing from south
+ final double bs = Math.atan2(slngD * clatQ, clatS * slatQ - slatS * clatQ * clngD);
+ // Bearing to north
+ // Math.atan2(slngD * clatN, clatQ * slatN - slatQ * clatN * clngD);
+ // Bearing from north
+ final double bn = Math.atan2(slngD * clatQ, clatN * slatQ - slatN * clatQ * clngD);
+ if (bs < MathUtil.HALFPI) {
+ if (bn > MathUtil.HALFPI) {
+ // Radians from south pole = abs(ATD)
+ final double radFromS = -MathUtil.HALFPI - plat;
+
+ // Cross-track-distance to longitude line.
+ return EARTH_RADIUS * Math.asin(Math.sin(radFromS) * -slngD);
+ }
+ }
+ if (bs - MathUtil.HALFPI < MathUtil.HALFPI - bn) {
+ // Haversine to north corner.
+ final double slatN2 = Math.sin((plat - rmaxlat) * .5);
+ final double slon = Math.sin(lngE * .5);
+ final double aN = slatN2 * slatN2 + slon * slon * clatQ * clatN;
+ final double distN = 2 * Math.atan2(Math.sqrt(aN), Math.sqrt(1 - aN));
+ return EARTH_RADIUS * distN;
+ } else {
+ // Haversine to south corner.
+ final double slatS2 = Math.sin((plat - rminlat) * .5);
+ final double slon = Math.sin(lngE * .5);
+ final double aS = slatS2 * slatS2 + slon * slon * clatQ * clatS;
+ final double distS = 2 * Math.atan2(Math.sqrt(aS), Math.sqrt(1 - aS));
+ return EARTH_RADIUS * distS;
+ }
+ } else { // West, to max edge
+ final double slngD = Math.sin(lngW);
+ final double clngD = Math.cos(lngW);
+
+ // Bearing to south
+ // Math.atan2(slngD * clatS, clatQ * slatS - slatQ * clatS * clngD);
+ // Bearing from south
+ final double bs = Math.atan2(slngD * clatQ, clatS * slatQ - slatS * clatQ * clngD);
+ // Bearing to north
+ // Math.atan2(slngD * clatN, clatQ * slatN - slatQ * clatN * clngD);
+ // Bearing from north
+ final double bn = Math.atan2(slngD * clatQ, clatN * slatQ - slatN * clatQ * clngD);
+ if (bs > -MathUtil.HALFPI) {
+ if (bn < -MathUtil.HALFPI) {
+ // Radians from south = abs(ATD) = distance from pole
+ final double radFromS = -MathUtil.HALFPI - plat;
+ // Cross-track-distance to longitude line.
+ return EARTH_RADIUS * Math.asin(Math.sin(radFromS) * slngD);
+ }
+ }
+ if (-MathUtil.HALFPI - bs < bn + MathUtil.HALFPI) {
+ // Haversine to north corner.
+ final double slatN2 = Math.sin((plat - rmaxlat) * .5);
+ final double slon = Math.sin(lngW * .5);
+ final double aN = slatN2 * slatN2 + slon * slon * clatQ * clatN;
+ final double distN = 2 * Math.atan2(Math.sqrt(aN), Math.sqrt(1 - aN));
+ return EARTH_RADIUS * distN;
+ } else {
+ // Haversine to south corner.
+ final double slatS2 = Math.sin((plat - rminlat) * .5);
+ final double slon = Math.sin(lngW * .5);
+ final double aS = slatS2 * slatS2 + slon * slon * clatQ * clatS;
+ final double distS = 2 * Math.atan2(Math.sqrt(aS), Math.sqrt(1 - aS));
+ return EARTH_RADIUS * distS;
+ }
+ }
+ }
+
+ /**
+ * Compute the bearing from start to end.
+ *
+ * @param latS Start latitude, in degree
+ * @param lngS Start longitude, in degree
+ * @param latE End latitude, in degree
+ * @param lngE End longitude, in degree
+ * @return Bearing in radians
+ */
+ public static double bearing(double latS, double lngS, double latE, double lngE) {
+ latS = MathUtil.deg2rad(latS);
+ latE = MathUtil.deg2rad(latE);
+ lngS = MathUtil.deg2rad(lngS);
+ lngE = MathUtil.deg2rad(lngE);
+ final double slatS = Math.sin(latS);
+ final double clatS = Math.cos(latS);
+ final double slatE = Math.sin(latE);
+ final double clatE = Math.cos(latE);
+ return Math.atan2(-Math.sin(lngS - lngE) * clatE, clatS * slatE - slatS * clatE * Math.cos(lngS - lngE));
+ }
+
+ /**
+ * Map a latitude,longitude pair to 3D X-Y-Z coordinates, using athe WGS84
+ * ellipsoid.
+ *
+ * The coordinate system is chosen such that the earth rotates around the Z
+ * axis.
+ *
+ * @param lat Latitude in degree
+ * @param lng Longitude in degree
+ * @return Coordinate triple
+ */
+ public static double[] latLngDegToXZYWGS84(double lat, double lng) {
+ // Switch to radians:
+ lat = Math.toRadians(lat);
+ lng = Math.toRadians(lng);
+ // Sine and cosines:
+ final double clat = Math.cos(lat), slat = Math.sin(lat);
+ final double clng = Math.cos(lng), slng = Math.sin(lng);
+
+ // Eccentricity squared
+ final double v = WGS84_RADIUS / (Math.sqrt(1 - WGS84_ECCENTRICITY_SQUARED * slat * slat));
+
+ return new double[] { v * clat * clng, v * clat * slng, (1 - WGS84_ECCENTRICITY_SQUARED) * v * slat };
+ }
+
+ /**
+ * Convert Latitude-Longitude pair to X-Y-Z coordinates using a spherical
+ * approximation of the earth.
+ *
+ * The coordinate system is chosen such that the earth rotates around the Z
+ * axis.
+ *
+ * @param lat Latitude in degree
+ * @param lng Longitude in degree
+ * @return Coordinate triple
+ */
+ public static double[] latLngDegToXZY(double lat, double lng) {
+ // Map to radians.
+ lat = MathUtil.rad2deg(lat);
+ lng = MathUtil.rad2deg(lng);
+ // Sine and cosines:
+ final double clat = Math.cos(lat), slat = Math.sin(lat);
+ final double clng = Math.cos(lng), slng = Math.sin(lng);
+ return new double[] { EARTH_RADIUS * clat * clng, EARTH_RADIUS * clat * slng, EARTH_RADIUS * slat };
+ }
+
+ /**
+ * Convert a 3D coordinate pair to the corresponding longitude.
+ *
+ * Only x and y are required - z gives the latitude.
+ *
+ * @param x X value
+ * @param y Y value
+ * @return Latitude
+ */
+ public static double xyzToLatDegWGS84(double x, double y, double z) {
+ final double p = Math.sqrt(x * x + y * y);
+ double lat = Math.atan2(z, p * (1 - WGS84_ECCENTRICITY_SQUARED));
+
+ // Iteratively improving the lat value
+ // TODO: instead of a fixed number of iterations, check for convergence.
+ for (int i = 0; i < 10; i++) {
+ final double slat = Math.sin(lat);
+ final double v = WGS84_RADIUS / (Math.sqrt(1 - WGS84_ECCENTRICITY_SQUARED * slat * slat));
+ lat = Math.atan2(z + WGS84_ECCENTRICITY_SQUARED * v * slat, p);
+ }
+
+ return MathUtil.rad2deg(lat);
+ }
+
+ /**
+ * Convert a 3D coordinate pair to the corresponding latitude.
+ *
+ * Only the z coordinate is required.
+ *
+ * @param z Z value
+ * @return Latitude
+ */
+ public static double xyzToLatDeg(double z) {
+ return MathUtil.rad2deg(Math.asin(z / EARTH_RADIUS));
+ }
+
+ /**
+ * Convert a 3D coordinate pair to the corresponding longitude.
+ *
+ * Only x and y are required - z gives the latitude.
+ *
+ * @param x X value
+ * @param y Y value
+ * @return Latitude
+ */
+ public static double xyzToLngDeg(double x, double y) {
+ return MathUtil.rad2deg(Math.atan2(y, x));
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/math/IntegerMinMax.java b/src/de/lmu/ifi/dbs/elki/math/IntegerMinMax.java
new file mode 100644
index 00000000..4c89240b
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/math/IntegerMinMax.java
@@ -0,0 +1,176 @@
+package de.lmu.ifi.dbs.elki.math;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.Collection;
+import de.lmu.ifi.dbs.elki.utilities.pairs.IntIntPair;
+
+/**
+ * Class to find the minimum and maximum int values in data.
+ *
+ * @author Erich Schubert
+ * @author Arthur Zimek
+ */
+public class IntegerMinMax extends IntIntPair {
+ /**
+ * Constructor without starting values.
+ *
+ * The minimum will be initialized to {@link Integer#MAX_VALUE}.
+ *
+ * The maximum will be initialized to {@link Integer#MIN_VALUE}.
+ *
+ * So that the first data added will replace both.
+ */
+ public IntegerMinMax() {
+ super(Integer.MAX_VALUE, Integer.MIN_VALUE);
+ }
+
+ /**
+ * Constructor with predefined minimum and maximum values.
+ *
+ * @param min Minimum value
+ * @param max Maximum value
+ */
+ public IntegerMinMax(int min, int max) {
+ super(min, max);
+ }
+
+ /**
+ * Process a single int value.
+ *
+ * If the new value is smaller than the current minimum, it will become the
+ * new minimum.
+ *
+ * If the new value is larger than the current maximum, it will become the new
+ * maximum.
+ *
+ * @param data New value
+ */
+ public void put(int data) {
+ this.first = Math.min(this.first, data);
+ this.second = Math.max(this.second, data);
+ }
+
+ /**
+ * Process a whole array of int values.
+ *
+ * If any of the values is smaller than the current minimum, it will become
+ * the new minimum.
+ *
+ * If any of the values is larger than the current maximum, it will become the
+ * new maximum.
+ *
+ * @param data Data to process
+ */
+ public void put(int[] data) {
+ for(int value : data) {
+ this.put(value);
+ }
+ }
+
+ /**
+ * Process a whole collection of Integer values.
+ *
+ * If any of the values is smaller than the current minimum, it will become
+ * the new minimum.
+ *
+ * If any of the values is larger than the current maximum, it will become the
+ * new maximum.
+ *
+ * @param data Data to process
+ */
+ public void put(Collection<Integer> data) {
+ for(Integer value : data) {
+ this.put(value.intValue());
+ }
+ }
+
+ /**
+ * Get the current minimum.
+ *
+ * @return current minimum.
+ */
+ public int getMin() {
+ return this.first;
+ }
+
+ /**
+ * Get the current maximum.
+ *
+ * @return current maximum.
+ */
+ public int getMax() {
+ return this.second;
+ }
+
+ /**
+ * Return the difference between minimum and maximum.
+ *
+ * @return Difference of current Minimum and Maximum.
+ */
+ public int getDiff() {
+ return this.getMax() - this.getMin();
+ }
+
+ /**
+ * Test whether the result is defined.
+ *
+ * @return true when at least one value has been added
+ */
+ public boolean isValid() {
+ return (first <= second);
+ }
+
+ /**
+ * Return minimum and maximum as array.
+ *
+ * @return Minimum, Maximum
+ */
+ public int[] asIntArray() {
+ return new int[] { this.getMin(), this.getMax() };
+ }
+
+ /**
+ * Generate a new array of initialized IntegerMinMax objects (with default
+ * constructor)
+ *
+ * @param size Array size
+ * @return initialized array
+ */
+ public static IntegerMinMax[] newArray(int size) {
+ IntegerMinMax ret[] = new IntegerMinMax[size];
+ for(int i = 0; i < size; i++) {
+ ret[i] = new IntegerMinMax();
+ }
+ return ret;
+ }
+
+ /**
+ * Reset statistics.
+ */
+ public void reset() {
+ first = Integer.MAX_VALUE;
+ second = Integer.MIN_VALUE;
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/math/MathUtil.java b/src/de/lmu/ifi/dbs/elki/math/MathUtil.java
index c44a0203..aaa19f0d 100644
--- a/src/de/lmu/ifi/dbs/elki/math/MathUtil.java
+++ b/src/de/lmu/ifi/dbs/elki/math/MathUtil.java
@@ -28,6 +28,7 @@ import java.util.Random;
import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Matrix;
+import de.lmu.ifi.dbs.elki.math.linearalgebra.VMath;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector;
/**
@@ -45,6 +46,11 @@ public final class MathUtil {
public static final double TWOPI = 2 * Math.PI;
/**
+ * Half the value of Pi.
+ */
+ public static final double HALFPI = 0.5 * Math.PI;
+
+ /**
* Square root of two times Pi.
*/
public static final double SQRTTWOPI = Math.sqrt(TWOPI);
@@ -55,17 +61,17 @@ public final class MathUtil {
public static final double SQRT2 = Math.sqrt(2);
/**
- * Square root of 5
+ * Square root of 5.
*/
public static final double SQRT5 = Math.sqrt(5);
/**
- * Square root of 0.5 == 1 / sqrt(2)
+ * Square root of 0.5 == 1 / sqrt(2).
*/
public static final double SQRTHALF = Math.sqrt(.5);
/**
- * Precomputed value of 1 / sqrt(pi)
+ * Precomputed value of 1 / sqrt(pi).
*/
public static final double ONE_BY_SQRTPI = 1 / Math.sqrt(Math.PI);
@@ -75,26 +81,36 @@ public final class MathUtil {
public static final double LOG2 = Math.log(2);
/**
- * Natural logarithm of 10
+ * Natural logarithm of 10.
*/
public static final double LOG10 = Math.log(10);
/**
- * Math.log(Math.PI)
+ * Math.log(Math.PI).
*/
public static final double LOGPI = Math.log(Math.PI);
/**
- * Math.log(Math.PI) / 2
+ * Math.log(Math.PI) / 2.
*/
public static final double LOGPIHALF = LOGPI / 2.;
/**
- * Math.log(Math.sqrt(2*Math.PI))
+ * Math.log(Math.sqrt(2*Math.PI)).
*/
public static final double LOGSQRTTWOPI = Math.log(SQRTTWOPI);
/**
+ * Constant for degrees to radians.
+ */
+ public static final double DEG2RAD = Math.PI / 180.0;
+
+ /**
+ * Constant for radians to degrees.
+ */
+ public static final double RAD2DEG = 180 / Math.PI;
+
+ /**
* Fake constructor for static class.
*/
private MathUtil() {
@@ -113,21 +129,19 @@ public final class MathUtil {
* @return {@code sqrt(a<sup>2</sup> + b<sup>2</sup>)}
*/
public static double fastHypot(double a, double b) {
- if(a < 0) {
+ if (a < 0) {
a = -a;
}
- if(b < 0) {
+ if (b < 0) {
b = -b;
}
- if(a > b) {
+ if (a > b) {
final double r = b / a;
return a * Math.sqrt(1 + r * r);
- }
- else if(b != 0) {
+ } else if (b != 0) {
final double r = a / b;
return b * Math.sqrt(1 + r * r);
- }
- else {
+ } else {
return 0.0;
}
}
@@ -145,17 +159,17 @@ public final class MathUtil {
* @return {@code sqrt(a<sup>2</sup> + b<sup>2</sup> + c<sup>2</sup>)}
*/
public static double fastHypot3(double a, double b, double c) {
- if(a < 0) {
+ if (a < 0) {
a = -a;
}
- if(b < 0) {
+ if (b < 0) {
b = -b;
}
- if(c < 0) {
+ if (c < 0) {
c = -c;
}
double m = (a > b) ? ((a > c) ? a : c) : ((b > c) ? b : c);
- if(m <= 0) {
+ if (m <= 0) {
return 0.0;
}
a = a / m;
@@ -165,7 +179,7 @@ public final class MathUtil {
}
/**
- * Compute the Mahalanobis distance using the given weight matrix
+ * Compute the Mahalanobis distance using the given weight matrix.
*
* @param weightMatrix Weight Matrix
* @param o1_minus_o2 Delta vector
@@ -174,7 +188,23 @@ public final class MathUtil {
public static double mahalanobisDistance(Matrix weightMatrix, Vector o1_minus_o2) {
double sqrDist = o1_minus_o2.transposeTimesTimes(weightMatrix, o1_minus_o2);
- if(sqrDist < 0 && Math.abs(sqrDist) < 0.000000001) {
+ if (sqrDist < 0 && Math.abs(sqrDist) < 0.000000001) {
+ sqrDist = Math.abs(sqrDist);
+ }
+ return Math.sqrt(sqrDist);
+ }
+
+ /**
+ * Compute the Mahalanobis distance using the given weight matrix.
+ *
+ * @param weightMatrix Weight Matrix
+ * @param o1_minus_o2 Delta vector
+ * @return Mahalanobis distance
+ */
+ public static double mahalanobisDistance(double[][] weightMatrix, double[] o1_minus_o2) {
+ double sqrDist = VMath.transposeTimesTimes(o1_minus_o2, weightMatrix, o1_minus_o2);
+
+ if (sqrDist < 0 && Math.abs(sqrDist) < 0.000000001) {
sqrDist = Math.abs(sqrDist);
}
return Math.sqrt(sqrDist);
@@ -190,18 +220,18 @@ public final class MathUtil {
* @param y second FeatureVector
* @return the Pearson product-moment correlation coefficient for x and y
*/
- public static double pearsonCorrelationCoefficient(NumberVector<?, ?> x, NumberVector<?, ?> y) {
+ public static double pearsonCorrelationCoefficient(NumberVector<?> x, NumberVector<?> y) {
final int xdim = x.getDimensionality();
final int ydim = y.getDimensionality();
- if(xdim != ydim) {
+ if (xdim != ydim) {
throw new IllegalArgumentException("Invalid arguments: feature vectors differ in dimensionality.");
}
- if(xdim <= 0) {
+ if (xdim <= 0) {
throw new IllegalArgumentException("Invalid arguments: dimensionality not positive.");
}
PearsonCorrelation pc = new PearsonCorrelation();
- for(int i = 0; i < xdim; i++) {
- pc.put(x.doubleValue(i + 1), y.doubleValue(i + 1), 1.0);
+ for (int i = 0; i < xdim; i++) {
+ pc.put(x.doubleValue(i), y.doubleValue(i), 1.0);
}
return pc.getCorrelation();
}
@@ -214,20 +244,21 @@ public final class MathUtil {
*
* @param x first FeatureVector
* @param y second FeatureVector
+ * @param weights Weights
* @return the Pearson product-moment correlation coefficient for x and y
*/
- public static double weightedPearsonCorrelationCoefficient(NumberVector<?, ?> x, NumberVector<?, ?> y, double[] weights) {
+ public static double weightedPearsonCorrelationCoefficient(NumberVector<?> x, NumberVector<?> y, double[] weights) {
final int xdim = x.getDimensionality();
final int ydim = y.getDimensionality();
- if(xdim != ydim) {
+ if (xdim != ydim) {
throw new IllegalArgumentException("Invalid arguments: feature vectors differ in dimensionality.");
}
- if(xdim != weights.length) {
+ if (xdim != weights.length) {
throw new IllegalArgumentException("Dimensionality doesn't agree to weights.");
}
PearsonCorrelation pc = new PearsonCorrelation();
- for(int i = 0; i < xdim; i++) {
- pc.put(x.doubleValue(i + 1), y.doubleValue(i + 1), weights[i]);
+ for (int i = 0; i < xdim; i++) {
+ pc.put(x.doubleValue(i), y.doubleValue(i), weights[i]);
}
return pc.getCorrelation();
}
@@ -240,20 +271,21 @@ public final class MathUtil {
*
* @param x first FeatureVector
* @param y second FeatureVector
+ * @param weights Weights
* @return the Pearson product-moment correlation coefficient for x and y
*/
- public static double weightedPearsonCorrelationCoefficient(NumberVector<?, ?> x, NumberVector<?, ?> y, NumberVector<?, ?> weights) {
+ public static double weightedPearsonCorrelationCoefficient(NumberVector<?> x, NumberVector<?> y, NumberVector<?> weights) {
final int xdim = x.getDimensionality();
final int ydim = y.getDimensionality();
- if(xdim != ydim) {
+ if (xdim != ydim) {
throw new IllegalArgumentException("Invalid arguments: feature vectors differ in dimensionality.");
}
- if(xdim != weights.getDimensionality()) {
+ if (xdim != weights.getDimensionality()) {
throw new IllegalArgumentException("Dimensionality doesn't agree to weights.");
}
PearsonCorrelation pc = new PearsonCorrelation();
- for(int i = 0; i < xdim; i++) {
- pc.put(x.doubleValue(i + 1), y.doubleValue(i + 1), weights.doubleValue(i + 1));
+ for (int i = 0; i < xdim; i++) {
+ pc.put(x.doubleValue(i), y.doubleValue(i), weights.doubleValue(i));
}
return pc.getCorrelation();
}
@@ -271,11 +303,11 @@ public final class MathUtil {
public static double pearsonCorrelationCoefficient(double[] x, double[] y) {
final int xdim = x.length;
final int ydim = y.length;
- if(xdim != ydim) {
+ if (xdim != ydim) {
throw new IllegalArgumentException("Invalid arguments: feature vectors differ in dimensionality.");
}
PearsonCorrelation pc = new PearsonCorrelation();
- for(int i = 0; i < xdim; i++) {
+ for (int i = 0; i < xdim; i++) {
pc.put(x[i], y[i], 1.0);
}
return pc.getCorrelation();
@@ -289,19 +321,20 @@ public final class MathUtil {
*
* @param x first FeatureVector
* @param y second FeatureVector
+ * @param weights Weights
* @return the Pearson product-moment correlation coefficient for x and y
*/
public static double weightedPearsonCorrelationCoefficient(double[] x, double[] y, double[] weights) {
final int xdim = x.length;
final int ydim = y.length;
- if(xdim != ydim) {
+ if (xdim != ydim) {
throw new IllegalArgumentException("Invalid arguments: feature vectors differ in dimensionality.");
}
- if(xdim != weights.length) {
+ if (xdim != weights.length) {
throw new IllegalArgumentException("Dimensionality doesn't agree to weights.");
}
PearsonCorrelation pc = new PearsonCorrelation();
- for(int i = 0; i < xdim; i++) {
+ for (int i = 0; i < xdim; i++) {
pc.put(x[i], y[i], weights[i]);
}
return pc.getCorrelation();
@@ -309,7 +342,7 @@ public final class MathUtil {
/**
* Compute the Factorial of n, often written as <code>c!</code> in
- * mathematics.</p>
+ * mathematics.
* <p>
* Use this method if for large values of <code>n</code>.
* </p>
@@ -320,7 +353,7 @@ public final class MathUtil {
*/
public static BigInteger factorial(BigInteger n) {
BigInteger nFac = BigInteger.valueOf(1);
- while(n.compareTo(BigInteger.valueOf(1)) > 0) {
+ while (n.compareTo(BigInteger.valueOf(1)) > 0) {
nFac = nFac.multiply(n);
n = n.subtract(BigInteger.valueOf(1));
}
@@ -336,7 +369,7 @@ public final class MathUtil {
*/
public static long factorial(int n) {
long nFac = 1;
- for(long i = n; i > 0; i--) {
+ for (long i = n; i > 0; i--) {
nFac *= i;
}
return nFac;
@@ -355,7 +388,7 @@ public final class MathUtil {
public static long binomialCoefficient(long n, long k) {
final long m = Math.max(k, n - k);
double temp = 1;
- for(long i = n, j = 1; i > m; i--, j++) {
+ for (long i = n, j = 1; i > m; i--, j++) {
temp = temp * i / j;
}
return (long) temp;
@@ -370,16 +403,14 @@ public final class MathUtil {
*/
public static double approximateFactorial(int n) {
double nFac = 1.0;
- for(int i = n; i > 0; i--) {
+ for (int i = n; i > 0; i--) {
nFac *= i;
}
return nFac;
}
/**
- * <p>
- * Binomial coefficent, also known as "n choose k")
- * </p>
+ * Binomial coefficent, also known as "n choose k").
*
* @param n Total number of samples. n &gt; 0
* @param k Number of elements to choose. <code>n &gt;= k</code>,
@@ -389,7 +420,7 @@ public final class MathUtil {
public static double approximateBinomialCoefficient(int n, int k) {
final int m = Math.max(k, n - k);
long temp = 1;
- for(int i = n, j = 1; i > m; i--, j++) {
+ for (int i = n, j = 1; i > m; i--, j++) {
temp = temp * i / j;
}
return temp;
@@ -402,11 +433,11 @@ public final class MathUtil {
* @return Sum
*/
public static long sumFirstIntegers(final long i) {
- return ((i - 1L) * i) / 2;
+ return ((i - 1L) * i) >> 1;
}
/**
- * Produce an array of random numbers in [0:1]
+ * Produce an array of random numbers in [0:1].
*
* @param len Length
* @return Array
@@ -416,7 +447,7 @@ public final class MathUtil {
}
/**
- * Produce an array of random numbers in [0:1]
+ * Produce an array of random numbers in [0:1].
*
* @param len Length
* @param r Random generator
@@ -424,62 +455,36 @@ public final class MathUtil {
*/
public static double[] randomDoubleArray(int len, Random r) {
final double[] ret = new double[len];
- for(int i = 0; i < len; i++) {
+ for (int i = 0; i < len; i++) {
ret[i] = r.nextDouble();
}
return ret;
}
/**
- * Convert Degree to Radians
+ * Convert Degree to Radians.
+ *
+ * This is essentially the same as {@link Math#toRadians}, but we keep it for
+ * now, it might be marginally faster, but certainly not slower.
*
* @param deg Degree value
* @return Radian value
*/
public static double deg2rad(double deg) {
- return deg * Math.PI / 180.0;
+ return deg * DEG2RAD;
}
/**
- * Radians to Degree
+ * Radians to Degree.
+ *
+ * This is essentially the same as {@link Math#toRadians}, but we keep it for
+ * now, it might be marginally faster, but certainly not slower.
*
* @param rad Radians value
* @return Degree value
*/
public static double rad2deg(double rad) {
- return rad * 180 / Math.PI;
- }
-
- /**
- * Compute the approximate on-earth-surface distance of two points.
- *
- * @param lat1 Latitude of first point in degree
- * @param lon1 Longitude of first point in degree
- * @param lat2 Latitude of second point in degree
- * @param lon2 Longitude of second point in degree
- * @return Distance in km (approximately)
- */
- public static double latlngDistance(double lat1, double lon1, double lat2, double lon2) {
- final double EARTH_RADIUS = 6371; // km.
- // Work in radians
- lat1 = MathUtil.deg2rad(lat1);
- lat2 = MathUtil.deg2rad(lat2);
- lon1 = MathUtil.deg2rad(lon1);
- lon2 = MathUtil.deg2rad(lon2);
- // Delta
- final double dlat = lat1 - lat2;
- final double dlon = lon1 - lon2;
-
- // Spherical Law of Cosines
- // NOTE: there seems to be a signedness issue in this code!
- // double dist = Math.sin(lat1) * Math.sin(lat2) + Math.cos(lat1) *
- // Math.cos(lat2) * Math.cos(dlon);
- // return EARTH_RADIUS * Math.atan(dist);
-
- // Alternative: Havestine formula, higher precision at < 1 meters:
- final double a = Math.sin(dlat / 2) * Math.sin(dlat / 2) + Math.sin(dlon / 2) * Math.sin(dlon / 2) * Math.cos(lat1) * Math.cos(lat2);
- final double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
- return EARTH_RADIUS * c;
+ return rad * RAD2DEG;
}
/**
@@ -505,7 +510,7 @@ public final class MathUtil {
// v1.transposeTimes(v2) / (v1.euclideanLength() * v2.euclideanLength());
// We can just compute all three in parallel.
double s = 0, e1 = 0, e2 = 0;
- for(int k = 0; k < v1.length; k++) {
+ for (int k = 0; k < v1.length; k++) {
final double r1 = v1[k];
final double r2 = v2[k];
s += r1 * r2;
@@ -541,7 +546,7 @@ public final class MathUtil {
// v1'.transposeTimes(v2') / (v1'.euclideanLength()*v2'.euclideanLength());
// We can just compute all three in parallel.
double s = 0, e1 = 0, e2 = 0;
- for(int k = 0; k < v1.length; k++) {
+ for (int k = 0; k < v1.length; k++) {
final double r1 = v1[k] - o[k];
final double r2 = v2[k] - o[k];
s += r1 * r2;
@@ -635,53 +640,49 @@ public final class MathUtil {
* @return Double value
*/
public static double floatToDoubleUpper(float f) {
- if(Float.isNaN(f)) {
+ if (Float.isNaN(f)) {
return Double.NaN;
}
- if(Float.isInfinite(f)) {
- if(f > 0) {
+ if (Float.isInfinite(f)) {
+ if (f > 0) {
return Double.POSITIVE_INFINITY;
- }
- else {
- return Double.longBitsToDouble(0xc7efffffffffffffl);
+ } else {
+ return Double.longBitsToDouble(0xc7efffffffffffffL);
}
}
long bits = Double.doubleToRawLongBits((double) f);
- if((bits & 0x8000000000000000l) == 0) { // Positive
- if(bits == 0l) {
- return Double.longBitsToDouble(0x3690000000000000l);
+ if ((bits & 0x8000000000000000L) == 0) { // Positive
+ if (bits == 0L) {
+ return Double.longBitsToDouble(0x3690000000000000L);
}
- if(f == Float.MIN_VALUE) {
+ if (f == Float.MIN_VALUE) {
// bits += 0x7_ffff_ffff_ffffl;
- return Double.longBitsToDouble(0x36a7ffffffffffffl);
+ return Double.longBitsToDouble(0x36a7ffffffffffffL);
}
- if(Float.MIN_NORMAL > f && f >= Double.MIN_NORMAL) {
+ if (Float.MIN_NORMAL > f && f >= Double.MIN_NORMAL) {
// The most tricky case:
// a denormalized float, but a normalized double
final long bits2 = Double.doubleToRawLongBits((double) Math.nextUp(f));
- bits = (bits >>> 1) + (bits2 >>> 1) - 1l;
- }
- else {
- bits += 0xfffffffl; // 28 extra bits
+ bits = (bits >>> 1) + (bits2 >>> 1) - 1L;
+ } else {
+ bits += 0xfffffffL; // 28 extra bits
}
return Double.longBitsToDouble(bits);
- }
- else {
- if(bits == 0x8000000000000000l) {
+ } else {
+ if (bits == 0x8000000000000000L) {
return -0.0d;
}
- if(f == -Float.MIN_VALUE) {
+ if (f == -Float.MIN_VALUE) {
// bits -= 0xf_ffff_ffff_ffffl;
- return Double.longBitsToDouble(0xb690000000000001l);
+ return Double.longBitsToDouble(0xb690000000000001L);
}
- if(-Float.MIN_NORMAL < f && f <= -Double.MIN_NORMAL) {
+ if (-Float.MIN_NORMAL < f && f <= -Double.MIN_NORMAL) {
// The most tricky case:
// a denormalized float, but a normalized double
final long bits2 = Double.doubleToRawLongBits((double) Math.nextUp(f));
- bits = (bits >>> 1) + (bits2 >>> 1) + 1l;
- }
- else {
- bits -= 0xfffffffl; // 28 extra bits
+ bits = (bits >>> 1) + (bits2 >>> 1) + 1L;
+ } else {
+ bits -= 0xfffffffL; // 28 extra bits
}
return Double.longBitsToDouble(bits);
}
@@ -697,55 +698,51 @@ public final class MathUtil {
* @return Double value
*/
public static double floatToDoubleLower(float f) {
- if(Float.isNaN(f)) {
+ if (Float.isNaN(f)) {
return Double.NaN;
}
- if(Float.isInfinite(f)) {
- if(f < 0) {
+ if (Float.isInfinite(f)) {
+ if (f < 0) {
return Double.NEGATIVE_INFINITY;
- }
- else {
- return Double.longBitsToDouble(0x47efffffffffffffl);
+ } else {
+ return Double.longBitsToDouble(0x47efffffffffffffL);
}
}
long bits = Double.doubleToRawLongBits((double) f);
- if((bits & 0x8000000000000000l) == 0) { // Positive
- if(bits == 0l) {
+ if ((bits & 0x8000000000000000L) == 0) { // Positive
+ if (bits == 0L) {
return +0.0d;
}
- if(f == Float.MIN_VALUE) {
+ if (f == Float.MIN_VALUE) {
// bits -= 0xf_ffff_ffff_ffffl;
- return Double.longBitsToDouble(0x3690000000000001l);
+ return Double.longBitsToDouble(0x3690000000000001L);
}
- if(Float.MIN_NORMAL > f /* && f >= Double.MIN_NORMAL */) {
+ if (Float.MIN_NORMAL > f /* && f >= Double.MIN_NORMAL */) {
// The most tricky case:
// a denormalized float, but a normalized double
final long bits2 = Double.doubleToRawLongBits((double) -Math.nextUp(-f));
- bits = (bits >>> 1) + (bits2 >>> 1) + 1l; // + (0xfff_ffffl << 18);
- }
- else {
- bits -= 0xfffffffl; // 28 extra bits
+ bits = (bits >>> 1) + (bits2 >>> 1) + 1L; // + (0xfff_ffffL << 18);
+ } else {
+ bits -= 0xfffffffL; // 28 extra bits
}
return Double.longBitsToDouble(bits);
- }
- else {
- if(bits == 0x8000000000000000l) {
- return Double.longBitsToDouble(0xb690000000000000l);
+ } else {
+ if (bits == 0x8000000000000000L) {
+ return Double.longBitsToDouble(0xb690000000000000L);
}
- if(f == -Float.MIN_VALUE) {
+ if (f == -Float.MIN_VALUE) {
// bits += 0x7_ffff_ffff_ffffl;
- return Double.longBitsToDouble(0xb6a7ffffffffffffl);
+ return Double.longBitsToDouble(0xb6a7ffffffffffffL);
}
- if(-Float.MIN_NORMAL < f /* && f <= -Double.MIN_NORMAL */) {
+ if (-Float.MIN_NORMAL < f /* && f <= -Double.MIN_NORMAL */) {
// The most tricky case:
// a denormalized float, but a normalized double
final long bits2 = Double.doubleToRawLongBits((double) -Math.nextUp(-f));
- bits = (bits >>> 1) + (bits2 >>> 1) - 1l;
- }
- else {
- bits += 0xfffffffl; // 28 extra bits
+ bits = (bits >>> 1) + (bits2 >>> 1) - 1L;
+ } else {
+ bits += 0xfffffffL; // 28 extra bits
}
return Double.longBitsToDouble(bits);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/math/MeanVarianceMinMax.java b/src/de/lmu/ifi/dbs/elki/math/MeanVarianceMinMax.java
index 23e53bb5..4ea76002 100644
--- a/src/de/lmu/ifi/dbs/elki/math/MeanVarianceMinMax.java
+++ b/src/de/lmu/ifi/dbs/elki/math/MeanVarianceMinMax.java
@@ -101,6 +101,15 @@ public class MeanVarianceMinMax extends MeanVariance {
public double getMax() {
return this.max;
}
+
+ /**
+ * Get the current minimum and maximum.
+ *
+ * @return current minimum and maximum
+ */
+ public DoubleMinMax getDoubleMinMax(){
+ return new DoubleMinMax(this.min,this.max);
+ }
/**
* Return the difference between minimum and maximum.
diff --git a/src/de/lmu/ifi/dbs/elki/math/Primes.java b/src/de/lmu/ifi/dbs/elki/math/Primes.java
new file mode 100644
index 00000000..e9ac0463
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/math/Primes.java
@@ -0,0 +1,148 @@
+package de.lmu.ifi.dbs.elki.math;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * Class for prime number handling.
+ *
+ * TODO: actually offer functions for testing primes and generating primes - as
+ * of now, we just offer a list of the first 1000 primes for use by other
+ * classes.
+ *
+ * @author Erich Schubert
+ */
+public final class Primes {
+ /**
+ * The first 1000 prime numbers
+ */
+ public static final int[] FIRST_PRIMES = new int[] { //
+ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, //
+ 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, //
+ 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, //
+ 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, //
+ 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, //
+ 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, //
+ 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, //
+ 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, //
+ 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, //
+ 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, //
+ 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, //
+ 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, //
+ 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, //
+ 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, //
+ 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, //
+ 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, //
+ 947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, //
+ 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, //
+ 1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, //
+ 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223, //
+ 1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291, //
+ 1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, //
+ 1381, 1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451, //
+ 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499, 1511, //
+ 1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583, //
+ 1597, 1601, 1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657, //
+ 1663, 1667, 1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733, //
+ 1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789, 1801, 1811, //
+ 1823, 1831, 1847, 1861, 1867, 1871, 1873, 1877, 1879, 1889, //
+ 1901, 1907, 1913, 1931, 1933, 1949, 1951, 1973, 1979, 1987, //
+ 1993, 1997, 1999, 2003, 2011, 2017, 2027, 2029, 2039, 2053, //
+ 2063, 2069, 2081, 2083, 2087, 2089, 2099, 2111, 2113, 2129, //
+ 2131, 2137, 2141, 2143, 2153, 2161, 2179, 2203, 2207, 2213, //
+ 2221, 2237, 2239, 2243, 2251, 2267, 2269, 2273, 2281, 2287, //
+ 2293, 2297, 2309, 2311, 2333, 2339, 2341, 2347, 2351, 2357, //
+ 2371, 2377, 2381, 2383, 2389, 2393, 2399, 2411, 2417, 2423, //
+ 2437, 2441, 2447, 2459, 2467, 2473, 2477, 2503, 2521, 2531, //
+ 2539, 2543, 2549, 2551, 2557, 2579, 2591, 2593, 2609, 2617, //
+ 2621, 2633, 2647, 2657, 2659, 2663, 2671, 2677, 2683, 2687, //
+ 2689, 2693, 2699, 2707, 2711, 2713, 2719, 2729, 2731, 2741, //
+ 2749, 2753, 2767, 2777, 2789, 2791, 2797, 2801, 2803, 2819, //
+ 2833, 2837, 2843, 2851, 2857, 2861, 2879, 2887, 2897, 2903, //
+ 2909, 2917, 2927, 2939, 2953, 2957, 2963, 2969, 2971, 2999, //
+ 3001, 3011, 3019, 3023, 3037, 3041, 3049, 3061, 3067, 3079, //
+ 3083, 3089, 3109, 3119, 3121, 3137, 3163, 3167, 3169, 3181, //
+ 3187, 3191, 3203, 3209, 3217, 3221, 3229, 3251, 3253, 3257, //
+ 3259, 3271, 3299, 3301, 3307, 3313, 3319, 3323, 3329, 3331, //
+ 3343, 3347, 3359, 3361, 3371, 3373, 3389, 3391, 3407, 3413, //
+ 3433, 3449, 3457, 3461, 3463, 3467, 3469, 3491, 3499, 3511, //
+ 3517, 3527, 3529, 3533, 3539, 3541, 3547, 3557, 3559, 3571, //
+ 3581, 3583, 3593, 3607, 3613, 3617, 3623, 3631, 3637, 3643, //
+ 3659, 3671, 3673, 3677, 3691, 3697, 3701, 3709, 3719, 3727, //
+ 3733, 3739, 3761, 3767, 3769, 3779, 3793, 3797, 3803, 3821, //
+ 3823, 3833, 3847, 3851, 3853, 3863, 3877, 3881, 3889, 3907, //
+ 3911, 3917, 3919, 3923, 3929, 3931, 3943, 3947, 3967, 3989, //
+ 4001, 4003, 4007, 4013, 4019, 4021, 4027, 4049, 4051, 4057, //
+ 4073, 4079, 4091, 4093, 4099, 4111, 4127, 4129, 4133, 4139, //
+ 4153, 4157, 4159, 4177, 4201, 4211, 4217, 4219, 4229, 4231, //
+ 4241, 4243, 4253, 4259, 4261, 4271, 4273, 4283, 4289, 4297, //
+ 4327, 4337, 4339, 4349, 4357, 4363, 4373, 4391, 4397, 4409, //
+ 4421, 4423, 4441, 4447, 4451, 4457, 4463, 4481, 4483, 4493, //
+ 4507, 4513, 4517, 4519, 4523, 4547, 4549, 4561, 4567, 4583, //
+ 4591, 4597, 4603, 4621, 4637, 4639, 4643, 4649, 4651, 4657, //
+ 4663, 4673, 4679, 4691, 4703, 4721, 4723, 4729, 4733, 4751, //
+ 4759, 4783, 4787, 4789, 4793, 4799, 4801, 4813, 4817, 4831, //
+ 4861, 4871, 4877, 4889, 4903, 4909, 4919, 4931, 4933, 4937, //
+ 4943, 4951, 4957, 4967, 4969, 4973, 4987, 4993, 4999, 5003, //
+ 5009, 5011, 5021, 5023, 5039, 5051, 5059, 5077, 5081, 5087, //
+ 5099, 5101, 5107, 5113, 5119, 5147, 5153, 5167, 5171, 5179, //
+ 5189, 5197, 5209, 5227, 5231, 5233, 5237, 5261, 5273, 5279, //
+ 5281, 5297, 5303, 5309, 5323, 5333, 5347, 5351, 5381, 5387, //
+ 5393, 5399, 5407, 5413, 5417, 5419, 5431, 5437, 5441, 5443, //
+ 5449, 5471, 5477, 5479, 5483, 5501, 5503, 5507, 5519, 5521, //
+ 5527, 5531, 5557, 5563, 5569, 5573, 5581, 5591, 5623, 5639, //
+ 5641, 5647, 5651, 5653, 5657, 5659, 5669, 5683, 5689, 5693, //
+ 5701, 5711, 5717, 5737, 5741, 5743, 5749, 5779, 5783, 5791, //
+ 5801, 5807, 5813, 5821, 5827, 5839, 5843, 5849, 5851, 5857, //
+ 5861, 5867, 5869, 5879, 5881, 5897, 5903, 5923, 5927, 5939, //
+ 5953, 5981, 5987, 6007, 6011, 6029, 6037, 6043, 6047, 6053, //
+ 6067, 6073, 6079, 6089, 6091, 6101, 6113, 6121, 6131, 6133, //
+ 6143, 6151, 6163, 6173, 6197, 6199, 6203, 6211, 6217, 6221, //
+ 6229, 6247, 6257, 6263, 6269, 6271, 6277, 6287, 6299, 6301, //
+ 6311, 6317, 6323, 6329, 6337, 6343, 6353, 6359, 6361, 6367, //
+ 6373, 6379, 6389, 6397, 6421, 6427, 6449, 6451, 6469, 6473, //
+ 6481, 6491, 6521, 6529, 6547, 6551, 6553, 6563, 6569, 6571, //
+ 6577, 6581, 6599, 6607, 6619, 6637, 6653, 6659, 6661, 6673, //
+ 6679, 6689, 6691, 6701, 6703, 6709, 6719, 6733, 6737, 6761, //
+ 6763, 6779, 6781, 6791, 6793, 6803, 6823, 6827, 6829, 6833, //
+ 6841, 6857, 6863, 6869, 6871, 6883, 6899, 6907, 6911, 6917, //
+ 6947, 6949, 6959, 6961, 6967, 6971, 6977, 6983, 6991, 6997, //
+ 7001, 7013, 7019, 7027, 7039, 7043, 7057, 7069, 7079, 7103, //
+ 7109, 7121, 7127, 7129, 7151, 7159, 7177, 7187, 7193, 7207, //
+ 7211, 7213, 7219, 7229, 7237, 7243, 7247, 7253, 7283, 7297, //
+ 7307, 7309, 7321, 7331, 7333, 7349, 7351, 7369, 7393, 7411, //
+ 7417, 7433, 7451, 7457, 7459, 7477, 7481, 7487, 7489, 7499, //
+ 7507, 7517, 7523, 7529, 7537, 7541, 7547, 7549, 7559, 7561, //
+ 7573, 7577, 7583, 7589, 7591, 7603, 7607, 7621, 7639, 7643, //
+ 7649, 7669, 7673, 7681, 7687, 7691, 7699, 7703, 7717, 7723, //
+ 7727, 7741, 7753, 7757, 7759, 7789, 7793, 7817, 7823, 7829, //
+ 7841, 7853, 7867, 7873, 7877, 7879, 7883, 7901, 7907, 7919 //
+ };
+
+ /**
+ * Pseudo constructor.
+ */
+ private Primes() {
+ // Do not instantiate.
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/math/SinCosTable.java b/src/de/lmu/ifi/dbs/elki/math/SinCosTable.java
new file mode 100644
index 00000000..16b89772
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/math/SinCosTable.java
@@ -0,0 +1,308 @@
+package de.lmu.ifi.dbs.elki.math;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * Class to precompute / cache Sinus and Cosinus values.
+ *
+ * Note that the functions use integer offsets, not radians.
+ *
+ * TODO: add an interpolation function.
+ *
+ * TODO: add caching
+ *
+ * @author Erich Schubert
+ */
+public abstract class SinCosTable {
+ /**
+ * Number of steps.
+ */
+ protected final int steps;
+
+ /**
+ * Constructor.
+ *
+ * @param steps Number of steps (ideally, {@code steps % 4 = 0}!)
+ */
+ private SinCosTable(final int steps) {
+ this.steps = steps;
+ }
+
+ /**
+ * Get Cosine by step value.
+ *
+ * @param step Step value
+ * @return Cosinus
+ */
+ public abstract double cos(int step);
+
+ /**
+ * Get Sinus by step value.
+ *
+ * @param step Step value
+ * @return Sinus
+ */
+ public abstract double sin(int step);
+
+ /**
+ * Table that can't exploit much symmetry, because the steps are not divisible
+ * by 2.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ private static class FullTable extends SinCosTable {
+ /**
+ * Data store
+ */
+ private final double[] costable;
+
+ /**
+ * Data store
+ */
+ private final double[] sintable;
+
+ /**
+ * Constructor for tables with
+ *
+ * @param steps
+ */
+ public FullTable(int steps) {
+ super(steps);
+ final double radstep = Math.toRadians(360. / steps);
+ this.costable = new double[steps];
+ this.sintable = new double[steps];
+ double ang = 0.;
+ for (int i = 0; i < steps; i++, ang += radstep) {
+ this.costable[i] = Math.cos(ang);
+ this.sintable[i] = Math.sin(ang);
+ }
+ }
+
+ /**
+ * Get Cosine by step value.
+ *
+ * @param step Step value
+ * @return Cosinus
+ */
+ @Override
+ public double cos(int step) {
+ step = Math.abs(step) % steps;
+ return costable[step];
+ }
+
+ /**
+ * Get Sinus by step value.
+ *
+ * @param step Step value
+ * @return Sinus
+ */
+ @Override
+ public double sin(int step) {
+ step = step % steps;
+ if (step < 0) {
+ step += steps;
+ }
+ return sintable[step];
+ }
+ }
+
+ /**
+ * Table that exploits just one symmetry, as the number of steps is divisible
+ * by two.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ private static class HalfTable extends SinCosTable {
+ /**
+ * Number of steps div 2
+ */
+ private final int halfsteps;
+
+ /**
+ * Data store
+ */
+ private final double[] costable;
+
+ /**
+ * Data store
+ */
+ private final double[] sintable;
+
+ /**
+ * Constructor for tables with
+ *
+ * @param steps
+ */
+ public HalfTable(int steps) {
+ super(steps);
+ this.halfsteps = steps >> 1;
+ final double radstep = Math.toRadians(360. / steps);
+ this.costable = new double[halfsteps + 1];
+ this.sintable = new double[halfsteps + 1];
+ double ang = 0.;
+ for (int i = 0; i < halfsteps + 1; i++, ang += radstep) {
+ this.costable[i] = Math.cos(ang);
+ this.sintable[i] = Math.sin(ang);
+ }
+ }
+
+ /**
+ * Get Cosine by step value.
+ *
+ * @param step Step value
+ * @return Cosinus
+ */
+ @Override
+ public double cos(int step) {
+ // Tabularizing cosine is a bit more straightforward than sine
+ // As we can just drop the sign here:
+ step = Math.abs(step) % steps;
+ if (step < costable.length) {
+ return costable[step];
+ }
+ // Symmetry at PI:
+ return costable[steps - step];
+ }
+
+ /**
+ * Get Sinus by step value.
+ *
+ * @param step Step value
+ * @return Sinus
+ */
+ @Override
+ public double sin(int step) {
+ step = step % steps;
+ if (step < 0) {
+ step += steps;
+ }
+ if (step < sintable.length) {
+ return sintable[step];
+ }
+ // Anti symmetry at PI:
+ return -sintable[steps - step];
+ }
+ }
+
+ /**
+ * Table that exploits both symmetries, as the number of steps is divisible by
+ * four.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ private static class QuarterTable extends SinCosTable {
+ /**
+ * Number of steps div 4
+ */
+ private final int quarsteps;
+
+ /**
+ * Number of steps div 2
+ */
+ private final int halfsteps;
+
+ /**
+ * Data store
+ */
+ private final double[] costable;
+
+ /**
+ * Constructor for tables with
+ *
+ * @param steps
+ */
+ public QuarterTable(int steps) {
+ super(steps);
+ this.halfsteps = steps >> 1;
+ this.quarsteps = steps >> 2;
+ final double radstep = Math.toRadians(360. / steps);
+ this.costable = new double[quarsteps + 1];
+ double ang = 0.;
+ for (int i = 0; i < quarsteps + 1; i++, ang += radstep) {
+ this.costable[i] = Math.cos(ang);
+ }
+ }
+
+ /**
+ * Get Cosine by step value.
+ *
+ * @param step Step value
+ * @return Cosinus
+ */
+ @Override
+ public double cos(int step) {
+ // Tabularizing cosine is a bit more straightforward than sine
+ // As we can just drop the sign here:
+ step = Math.abs(step) % steps;
+ if (step < costable.length) {
+ return costable[step];
+ }
+ // Symmetry at PI:
+ if (step > halfsteps) {
+ step = steps - step;
+ if (step < costable.length) {
+ return costable[step];
+ }
+ }
+ // Inverse symmetry at PI/2:
+ step = halfsteps - step;
+ return -costable[step];
+ }
+
+ /**
+ * Get Sinus by step value.
+ *
+ * @param step Step value
+ * @return Sinus
+ */
+ @Override
+ public double sin(int step) {
+ return -cos(step + quarsteps);
+ }
+ }
+
+ /**
+ * Make a table for the given number of steps.
+ *
+ * For step numbers divisible by 4, an optimized implementation will be used.
+ *
+ * @param steps Number of steps
+ * @return Table
+ */
+ public static SinCosTable make(int steps) {
+ if ((steps & 0x3) == 0) {
+ return new QuarterTable(steps);
+ }
+ if ((steps & 0x1) == 0) {
+ return new HalfTable(steps);
+ }
+ return new FullTable(steps);
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/math/dimensionsimilarity/CovarianceDimensionSimilarity.java b/src/de/lmu/ifi/dbs/elki/math/dimensionsimilarity/CovarianceDimensionSimilarity.java
new file mode 100644
index 00000000..ad59ebfd
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/math/dimensionsimilarity/CovarianceDimensionSimilarity.java
@@ -0,0 +1,82 @@
+package de.lmu.ifi.dbs.elki.math.dimensionsimilarity;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
+import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.math.linearalgebra.CovarianceMatrix;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
+
+/**
+ * Class to compute the dimension similarity based on covariances.
+ *
+ * @author Erich Schubert
+ */
+public class CovarianceDimensionSimilarity implements DimensionSimilarity<NumberVector<?>> {
+ /**
+ * Static instance
+ */
+ public static final CovarianceDimensionSimilarity STATIC = new CovarianceDimensionSimilarity();
+
+ /**
+ * Constructor. Use static instance.
+ */
+ protected CovarianceDimensionSimilarity() {
+ super();
+ }
+
+ @Override
+ public void computeDimensionSimilarites(Relation<? extends NumberVector<?>> relation, DBIDs subset, DimensionSimilarityMatrix matrix) {
+ final int dim = matrix.size();
+ // FIXME: Use only necessary dimensions!
+ CovarianceMatrix covmat = CovarianceMatrix.make(relation, subset);
+ double[][] mat = covmat.destroyToSampleMatrix().getArrayRef();
+ // Transform diagonal to 1 / stddev
+ for (int i = 0; i < mat.length; i++) {
+ mat[i][i] = 1. / Math.sqrt(mat[i][i]);
+ }
+ // Fill output matrix:
+ for (int x = 0; x < dim; x++) {
+ final int i = matrix.dim(x);
+ for (int y = x + 1; y < dim; y++) {
+ final int j = matrix.dim(y);
+ matrix.set(x, y, mat[i][j] * mat[i][i] * mat[j][j]);
+ }
+ }
+ }
+
+ /**
+ * Parameterization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public static class Parameterizer extends AbstractParameterizer {
+ @Override
+ protected CovarianceDimensionSimilarity makeInstance() {
+ return STATIC;
+ }
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/math/dimensionsimilarity/DimensionSimilarity.java b/src/de/lmu/ifi/dbs/elki/math/dimensionsimilarity/DimensionSimilarity.java
new file mode 100644
index 00000000..6b01b3f3
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/math/dimensionsimilarity/DimensionSimilarity.java
@@ -0,0 +1,46 @@
+package de.lmu.ifi.dbs.elki.math.dimensionsimilarity;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
+import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable;
+
+/**
+ * Interface for computing pairwise dimension similarities, used for arranging
+ * dimensions in parallel coordinate plots.
+ *
+ * @author Erich Schubert
+ *
+ * @param <V> Object type
+ */
+public interface DimensionSimilarity<V> extends Parameterizable {
+ /**
+ * Compute the dimension similarity matrix
+ *
+ * @param relation Relation
+ * @param subset DBID subset (for sampling / selection)
+ * @param matrix Matrix to fill
+ */
+ public void computeDimensionSimilarites(Relation<? extends V> relation, DBIDs subset, DimensionSimilarityMatrix matrix);
+}
diff --git a/src/de/lmu/ifi/dbs/elki/math/dimensionsimilarity/DimensionSimilarityMatrix.java b/src/de/lmu/ifi/dbs/elki/math/dimensionsimilarity/DimensionSimilarityMatrix.java
new file mode 100644
index 00000000..9f7a9707
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/math/dimensionsimilarity/DimensionSimilarityMatrix.java
@@ -0,0 +1,247 @@
+package de.lmu.ifi.dbs.elki.math.dimensionsimilarity;
+
+import de.lmu.ifi.dbs.elki.math.geometry.PrimsMinimumSpanningTree;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * Class representing a similarity matrix between dimensions.
+ *
+ * @author Erich Schubert
+ */
+public abstract class DimensionSimilarityMatrix {
+ /**
+ * Adapter class for running Prim's minimum spanning tree algorithm.
+ */
+ public static final PrimAdapter PRIM_ADAPTER = new PrimAdapter();
+
+ /**
+ * Flat, symmetric storage. We use a lower triangle matrix.
+ *
+ * Basic memory layout (X = undef, S = symmetric)
+ *
+ * <pre>
+ * X S S S S S
+ * 0 X S S S S
+ * 1 2 X S S S
+ * 3 4 5 X S S
+ * 6 7 8 9 X S
+ * 10 11 12 13 14 X
+ * </pre>
+ *
+ *
+ */
+ private final double[] sim;
+
+ /**
+ * Constructor.
+ *
+ * @param dims Number of dimensions to allocate.
+ */
+ protected DimensionSimilarityMatrix(int dims) {
+ super();
+ this.sim = new double[index(0, dims)];
+ }
+
+ /**
+ * Number of dimensions.
+ *
+ * @return Size of dimensions array.
+ */
+ public abstract int size();
+
+ /**
+ * Get the dimension at position idx.
+ *
+ * @param idx Position
+ * @return Dimension
+ */
+ public abstract int dim(int idx);
+
+ /**
+ * Set the value of the given matrix position.
+ *
+ * Note that {@code x == y} is invalid!
+ *
+ * @param x X index coordinate
+ * @param y Y index coordinate
+ * @param val Value
+ */
+ public void set(int x, int y, double val) {
+ sim[index(x, y)] = val;
+ }
+
+ /**
+ * Get the value of the given matrix position.
+ *
+ * Note that {@code x == y} is invalid!
+ *
+ * @param x X index coordinate
+ * @param y Y index coordinate
+ * @return Value
+ */
+ public double get(int x, int y) {
+ return sim[index(x, y)];
+ }
+
+ /**
+ * Indexing function for triangular matrix.
+ *
+ * @param x X coordinate
+ * @param y Y coordinate
+ * @return Array index
+ */
+ private int index(int x, int y) {
+ assert (x != y);
+ if (x > y) {
+ return index(y, x);
+ }
+ return ((y * (y - 1)) >> 1) + x;
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer buf = new StringBuffer();
+ final int d = size();
+ for (int x = 1; x < d; x++) {
+ for (int y = 0; y < x; y++) {
+ if (y > 0) {
+ buf.append(' ');
+ }
+ buf.append(get(x, y));
+ }
+ buf.append('\n');
+ }
+ return buf.toString();
+ }
+
+ /**
+ * Complete matrix of pairwise dimension similarities.
+ *
+ * @author Erich Schubert
+ */
+ public static class FullDimensionSimilarityMatrix extends DimensionSimilarityMatrix {
+ /**
+ * Number of dimensions.
+ */
+ final int dims;
+
+ /**
+ * Constructor.
+ *
+ * @param dims Number of dimensions
+ */
+ public FullDimensionSimilarityMatrix(int dims) {
+ super(dims);
+ this.dims = dims;
+ }
+
+ @Override
+ public int size() {
+ return dims;
+ }
+
+ @Override
+ public int dim(int idx) {
+ return idx;
+ }
+ }
+
+ /**
+ * Partial matrix of pairwise dimension similarities.
+ *
+ * @author Erich Schubert
+ */
+ public static class PartialDimensionSimilarityMatrix extends DimensionSimilarityMatrix {
+ /**
+ * Enumeration of dimensions to use (so we could use a subset only!)
+ */
+ final int[] dims;
+
+ /**
+ * Constructor.
+ *
+ * @param dims Array of dimensions to process.
+ */
+ public PartialDimensionSimilarityMatrix(int[] dims) {
+ super(dims.length);
+ this.dims = dims;
+ }
+
+ @Override
+ public int size() {
+ return dims.length;
+ }
+
+ @Override
+ public int dim(int idx) {
+ return dims[idx];
+ }
+ }
+
+ /**
+ * Adapter class for running prim's algorithm.
+ *
+ * @author Erich Schubert
+ */
+ public static class PrimAdapter implements PrimsMinimumSpanningTree.Adapter<DimensionSimilarityMatrix> {
+ /**
+ * Constructor. Use static instance!
+ */
+ protected PrimAdapter() {
+ super();
+ }
+
+ @Override
+ public double distance(DimensionSimilarityMatrix data, int i, int j) {
+ return -Math.abs(data.get(i, j));
+ }
+
+ @Override
+ public int size(DimensionSimilarityMatrix data) {
+ return data.size();
+ }
+
+ }
+
+ /**
+ * Make a full dimension similarity matrix.
+ *
+ * @param dims Number of dimensions.
+ * @return Matrix
+ */
+ public static DimensionSimilarityMatrix make(int dims) {
+ return new FullDimensionSimilarityMatrix(dims);
+ }
+
+ /**
+ * Make a partial dimension similarity matrix.
+ *
+ * @param dims Array of relevant dimensions
+ * @return Matrix
+ */
+ public static DimensionSimilarityMatrix make(int[] dims) {
+ return new PartialDimensionSimilarityMatrix(dims);
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/math/dimensionsimilarity/HSMDimensionSimilarity.java b/src/de/lmu/ifi/dbs/elki/math/dimensionsimilarity/HSMDimensionSimilarity.java
new file mode 100644
index 00000000..b221866c
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/math/dimensionsimilarity/HSMDimensionSimilarity.java
@@ -0,0 +1,251 @@
+package de.lmu.ifi.dbs.elki.math.dimensionsimilarity;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
+import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.math.SinCosTable;
+import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
+import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
+import de.lmu.ifi.dbs.elki.utilities.pairs.Pair;
+
+/**
+ * FIXME: This needs serious TESTING before release. Large parts have been
+ * rewritten, but could not be tested at the time of rewriting.
+ *
+ * Compute the similarity of dimensions by using a hough transformation.
+ *
+ * Reference: <br>
+ * <p>
+ * A. Tatu, G. Albuquerque, M. Eisemann, P. Bak, H. Theisel, M. A. Magnor, and
+ * D. A. Keim.<br />
+ * Automated Analytical Methods to Support Visual Exploration of High-
+ * Dimensional Data. <br/>
+ * IEEEVisualization and Computer Graphics, 2011.
+ * </p>
+ *
+ * @author Erich Schubert
+ * @author Robert Rödler
+ */
+@Reference(authors = "A. Tatu, G. Albuquerque, M. Eisemann, P. Bak, H. Theisel, M. A. Magnor, and D. A. Keim.", title = "Automated Analytical Methods to Support Visual Exploration of High-Dimensional Data", booktitle = "IEEE Trans. Visualization and Computer Graphics, 2011", url = "http://dx.doi.org/10.1109/TVCG.2010.242")
+public class HSMDimensionSimilarity implements DimensionSimilarity<NumberVector<?>> {
+ /**
+ * Static instance.
+ */
+ public static final HSMDimensionSimilarity STATIC = new HSMDimensionSimilarity();
+
+ /**
+ * Angular resolution. Best if divisible by 4: smaller tables.
+ *
+ * The original publication used 50.
+ */
+ private final static int STEPS = 64;
+
+ /**
+ * Precompute sinus and cosinus
+ */
+ private final static SinCosTable table = SinCosTable.make(STEPS);
+
+ /**
+ * Constructor. Use static instance instead!
+ */
+ protected HSMDimensionSimilarity() {
+ super();
+ }
+
+ @Override
+ public void computeDimensionSimilarites(Relation<? extends NumberVector<?>> relation, DBIDs subset, DimensionSimilarityMatrix matrix) {
+ final int dim = matrix.size();
+ final int resolution = 500;
+ byte[][][][] pics = new byte[dim][dim][][]; // [resolution][resolution];
+
+ // Initialize / allocate "pictures":
+ for (int i = 0; i < dim - 1; i++) {
+ for (int j = i + 1; j < dim; j++) {
+ pics[i][j] = new byte[resolution][resolution];
+ }
+ }
+ // FIXME: Get/keep these statistics in the relation, or compute for the
+ // sample only.
+ double[] off = new double[dim], scale = new double[dim];
+ {
+ Pair<? extends NumberVector<?>, ? extends NumberVector<?>> mm = DatabaseUtil.computeMinMax(relation);
+ NumberVector<?> min = mm.first;
+ NumberVector<?> max = mm.second;
+ for (int d = 0; d < dim; d++) {
+ off[d] = min.doubleValue(matrix.dim(d));
+ final double m = max.doubleValue(matrix.dim(d));
+ scale[d] = (m > off[d]) ? 1. / (m - off[d]) : 1;
+ }
+ }
+ // Iterate dataset
+ for (DBIDIter id = subset.iter(); id.valid(); id.advance()) {
+ NumberVector<?> pvec = relation.get(id);
+ for (int i = 0; i < dim - 1; i++) {
+ for (int j = i + 1; j < dim; j++) {
+ double xi = (pvec.doubleValue(matrix.dim(i)) - off[i]) * scale[i];
+ double xj = (pvec.doubleValue(matrix.dim(j)) - off[j]) * scale[j];
+ drawLine(0, (int) (resolution * xi), resolution - 1, (int) (resolution * xj), pics[i][j]);
+ }
+ }
+ }
+
+ final double stepsq = (double) STEPS * (double) STEPS;
+ for (int x = 0; x < dim; x++) {
+ final int i = matrix.dim(x);
+ for (int y = x + 1; y < dim; y++) {
+ final int j = matrix.dim(y);
+ int[][] hough = houghTransformation(pics[i][j]);
+ pics[i][j] = null; // Release picture
+ // The original publication said "median", but judging from the text,
+ // meant "mean". Otherwise, always half of the cells are above the
+ // threshold, which doesn't match the explanation there.
+ double mean = sumMatrix(hough) / stepsq;
+ int abovemean = countAboveThreshold(hough, mean);
+
+ matrix.set(x, y, 1. - (abovemean / stepsq));
+ }
+ }
+ }
+
+ /**
+ * Compute the sum of a matix.
+ *
+ * @param mat Matrix
+ * @return Sum of all elements
+ */
+ private long sumMatrix(int[][] mat) {
+ long ret = 0;
+ for (int i = 0; i < mat[0].length; i++) {
+ for (int j = 0; j < mat.length; j++) {
+ ret += mat[i][j];
+ }
+ }
+ return ret;
+ }
+
+ /**
+ * Count the number of cells above the threshold.
+ *
+ * @param mat Matrix
+ * @param threshold Threshold
+ * @return Number of elements above the threshold.
+ */
+ private int countAboveThreshold(int[][] mat, double threshold) {
+ int ret = 0;
+ for (int i = 0; i < mat.length; i++) {
+ int[] row = mat[i];
+ for (int j = 0; j < row.length; j++) {
+ if (row[j] >= threshold) {
+ ret++;
+ }
+ }
+ }
+ return ret;
+ }
+
+ /**
+ * Perform a hough transformation on the binary image in "mat".
+ *
+ * @param mat Binary image
+ * @return Hough transformation of image.
+ */
+ private int[][] houghTransformation(byte[][] mat) {
+ final int xres = mat.length, yres = mat[0].length;
+ final double tscale = STEPS / Math.sqrt(xres * xres + yres * yres);
+ final int[][] ret = new int[STEPS][STEPS];
+
+ for (int x = 0; x < mat.length; x++) {
+ for (int y = 0; y < mat[0].length; y++) {
+ if (mat[x][y] > 0) {
+ for (int i = 0; i < STEPS; i++) {
+ final int d = (int) (tscale * (x * table.cos(i) + y * table.sin(i)));
+ if (d > 0 && d < STEPS) {
+ ret[d][i] += mat[x][y];
+ }
+ }
+ }
+ }
+ }
+
+ return ret;
+ }
+
+ /**
+ * Draw a line onto the array, using the classic Bresenham algorithm.
+ *
+ * @param x0 Start X
+ * @param y0 Start Y
+ * @param x1 End X
+ * @param y1 End Y
+ * @param pic Picture array
+ */
+ private static void drawLine(int x0, int y0, int x1, int y1, byte[][] pic) {
+ final int xres = pic.length, yres = pic[0].length;
+ // Ensure bounds
+ y0 = (y0 < 0) ? 0 : (y0 >= yres) ? (yres - 1) : y0;
+ y1 = (y1 < 0) ? 0 : (y1 >= yres) ? (yres - 1) : y1;
+ x0 = (x0 < 0) ? 0 : (x0 >= xres) ? (xres - 1) : x0;
+ x1 = (x1 < 0) ? 0 : (x1 >= xres) ? (xres - 1) : x1;
+ // Default slope
+ final int dx = +Math.abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
+ final int dy = -Math.abs(y1 - y0), sy = y0 < y1 ? 1 : -1;
+ // Error counter
+ int err = dx + dy;
+
+ for (;;) {
+ pic[x0][y0] = 1;
+ if (x0 == x1 && y0 == y1) {
+ break;
+ }
+
+ final int e2 = err << 1;
+ if (e2 > dy) {
+ err += dy;
+ x0 += sx;
+ }
+ if (e2 < dx) {
+ err += dx;
+ y0 += sy;
+ }
+ }
+ }
+
+ /**
+ * Parameterization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public static class Parameterizer extends AbstractParameterizer {
+ @Override
+ protected HSMDimensionSimilarity makeInstance() {
+ return STATIC;
+ }
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/math/dimensionsimilarity/HiCSDimensionSimilarity.java b/src/de/lmu/ifi/dbs/elki/math/dimensionsimilarity/HiCSDimensionSimilarity.java
new file mode 100644
index 00000000..468db679
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/math/dimensionsimilarity/HiCSDimensionSimilarity.java
@@ -0,0 +1,269 @@
+package de.lmu.ifi.dbs.elki.math.dimensionsimilarity;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.ArrayList;
+import java.util.Random;
+
+import de.lmu.ifi.dbs.elki.algorithm.outlier.meta.HiCS;
+import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.data.VectorUtil;
+import de.lmu.ifi.dbs.elki.data.VectorUtil.SortDBIDsBySingleDimension;
+import de.lmu.ifi.dbs.elki.database.ids.ArrayDBIDs;
+import de.lmu.ifi.dbs.elki.database.ids.ArrayModifiableDBIDs;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDArrayIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
+import de.lmu.ifi.dbs.elki.database.ids.HashSetModifiableDBIDs;
+import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.math.statistics.tests.GoodnessOfFitTest;
+import de.lmu.ifi.dbs.elki.math.statistics.tests.KolmogorovSmirnovTest;
+import de.lmu.ifi.dbs.elki.utilities.RandomFactory;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.DoubleParameter;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ObjectParameter;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.RandomParameter;
+
+/**
+ * Use the statistical tests as used by HiCS to arrange dimensions.
+ *
+ * <p>
+ * Based on:<br />
+ * Fabian Keller, Emmanuel Müller, and Klemens Böhm.<br />
+ * HiCS: High Contrast Subspaces for Density-Based Outlier Ranking. <br />
+ * In ICDE, pages 1037–1048, 2012.
+ * </p>
+ *
+ * @author Erich Schubert
+ * @author Robert Rödler
+ */
+public class HiCSDimensionSimilarity implements DimensionSimilarity<NumberVector<?>> {
+ /**
+ * Monte-Carlo iterations
+ */
+ private int m = 50;
+
+ /**
+ * Alpha threshold
+ */
+ private double alpha = 0.1;
+
+ /**
+ * Statistical test to use
+ */
+ private GoodnessOfFitTest statTest;
+
+ /**
+ * Random generator
+ */
+ private RandomFactory rnd;
+
+ /**
+ * Constructor.
+ *
+ * @param statTest Test function
+ * @param m Number of monte-carlo iterations
+ * @param alpha Alpha threshold
+ * @param rnd Random source
+ */
+ public HiCSDimensionSimilarity(GoodnessOfFitTest statTest, int m, double alpha, RandomFactory rnd) {
+ super();
+ this.statTest = statTest;
+ this.m = m;
+ this.alpha = alpha;
+ this.rnd = rnd;
+ }
+
+ @Override
+ public void computeDimensionSimilarites(Relation<? extends NumberVector<?>> relation, DBIDs subset, DimensionSimilarityMatrix matrix) {
+ final Random random = rnd.getRandom();
+ final int dim = matrix.size();
+
+ // FIXME: only compute indexes necessary.
+ ArrayList<ArrayDBIDs> subspaceIndex = buildOneDimIndexes(relation, subset, matrix);
+
+ // compute two-element sets of subspaces
+ for (int x = 0; x < dim; x++) {
+ final int i = matrix.dim(x);
+ for (int y = x + 1; y < dim; y++) {
+ final int j = matrix.dim(y);
+ matrix.set(x, y, calculateContrast(relation, subset, subspaceIndex.get(x), subspaceIndex.get(y), i, j, random));
+ }
+ }
+ }
+
+ /**
+ * Calculates "index structures" for every attribute, i.e. sorts a
+ * ModifiableArray of every DBID in the database for every dimension and
+ * stores them in a list
+ *
+ * @param relation Relation to index
+ * @param ids IDs to use
+ * @param matrix Matrix (for dimension subset)
+ * @return List of sorted objects
+ */
+ private ArrayList<ArrayDBIDs> buildOneDimIndexes(Relation<? extends NumberVector<?>> relation, DBIDs ids, DimensionSimilarityMatrix matrix) {
+ final int dim = matrix.size();
+ ArrayList<ArrayDBIDs> subspaceIndex = new ArrayList<ArrayDBIDs>(dim);
+
+ SortDBIDsBySingleDimension comp = new VectorUtil.SortDBIDsBySingleDimension(relation);
+ for (int i = 0; i < dim; i++) {
+ ArrayModifiableDBIDs amDBIDs = DBIDUtil.newArray(ids);
+ comp.setDimension(matrix.dim(i));
+ amDBIDs.sort(comp);
+ subspaceIndex.add(amDBIDs);
+ }
+
+ return subspaceIndex;
+ }
+
+ /**
+ * Calculates the actual contrast of a given subspace
+ *
+ * @param relation Data relation
+ * @param subset Subset to process
+ * @param subspaceIndex1 Index of first subspace
+ * @param subspaceIndex2 Index of second subspace
+ * @param dim1 First dimension
+ * @param dim2 Second dimension
+ * @param random Random generator
+ * @return Contrast
+ */
+ private double calculateContrast(Relation<? extends NumberVector<?>> relation, DBIDs subset, ArrayDBIDs subspaceIndex1, ArrayDBIDs subspaceIndex2, int dim1, int dim2, Random random) {
+ final double alpha1 = Math.pow(alpha, .5);
+ final int windowsize = (int) (relation.size() * alpha1);
+
+ // TODO: speed up by keeping marginal distributions prepared.
+ // Instead of doing the random switch, do half-half.
+ double deviationSum = 0.0;
+ for (int i = 0; i < m; i++) {
+ // Randomly switch dimensions
+ final int cdim1;
+ ArrayDBIDs cindex1, cindex2;
+ if (random.nextDouble() > .5) {
+ cdim1 = dim1;
+ cindex1 = subspaceIndex1;
+ cindex2 = subspaceIndex2;
+ } else {
+ cdim1 = dim2;
+ cindex1 = subspaceIndex2;
+ cindex2 = subspaceIndex1;
+ }
+ // Build the sample
+ DBIDArrayIter iter = cindex2.iter();
+ HashSetModifiableDBIDs conditionalSample = DBIDUtil.newHashSet();
+ iter.seek(random.nextInt(subset.size() - windowsize));
+ for (int k = 0; k < windowsize && iter.valid(); k++, iter.advance()) {
+ conditionalSample.add(iter);
+ }
+ // Project the data
+ double[] fullValues = new double[subset.size()];
+ double[] sampleValues = new double[conditionalSample.size()];
+ {
+ int l = 0, s = 0;
+ // Note: we use the sorted index sets.
+ for (DBIDIter id = cindex1.iter(); id.valid(); id.advance(), l++) {
+ final double val = relation.get(id).doubleValue(cdim1);
+ fullValues[l] = val;
+ if (conditionalSample.contains(id)) {
+ sampleValues[s] = val;
+ s++;
+ }
+ }
+ assert (s == conditionalSample.size());
+ }
+ double contrast = statTest.deviation(fullValues, sampleValues);
+ if (Double.isNaN(contrast)) {
+ i--;
+ continue;
+ }
+ deviationSum += contrast;
+ }
+ return deviationSum / m;
+ }
+
+ /**
+ * Parameterization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public static class Parameterizer extends AbstractParameterizer {
+ /**
+ * Statistical test to use
+ */
+ private GoodnessOfFitTest statTest;
+
+ /**
+ * Holds the value of {@link de.lmu.ifi.dbs.elki.algorithm.outlier.meta.HiCS.Parameterizer#M_ID}.
+ */
+ private int m = 50;
+
+ /**
+ * Holds the value of {@link de.lmu.ifi.dbs.elki.algorithm.outlier.meta.HiCS.Parameterizer#ALPHA_ID}.
+ */
+ private double alpha = 0.1;
+
+ /**
+ * Random generator.
+ */
+ private RandomFactory rnd;
+
+ @Override
+ protected void makeOptions(Parameterization config) {
+ super.makeOptions(config);
+ final IntParameter mP = new IntParameter(HiCS.Parameterizer.M_ID, 50);
+ mP.addConstraint(new GreaterConstraint(1));
+ if (config.grab(mP)) {
+ m = mP.intValue();
+ }
+
+ final DoubleParameter alphaP = new DoubleParameter(HiCS.Parameterizer.ALPHA_ID, 0.1);
+ alphaP.addConstraint(new GreaterConstraint(0));
+ if (config.grab(alphaP)) {
+ alpha = alphaP.doubleValue();
+ }
+
+ final ObjectParameter<GoodnessOfFitTest> testP = new ObjectParameter<GoodnessOfFitTest>(HiCS.Parameterizer.TEST_ID, GoodnessOfFitTest.class, KolmogorovSmirnovTest.class);
+ if (config.grab(testP)) {
+ statTest = testP.instantiateClass(config);
+ }
+
+ final RandomParameter rndP = new RandomParameter(HiCS.Parameterizer.SEED_ID);
+ if (config.grab(rndP)) {
+ rnd = rndP.getValue();
+ }
+ }
+
+ @Override
+ protected HiCSDimensionSimilarity makeInstance() {
+ return new HiCSDimensionSimilarity(statTest, m, alpha, rnd);
+ }
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/math/dimensionsimilarity/MCEDimensionSimilarity.java b/src/de/lmu/ifi/dbs/elki/math/dimensionsimilarity/MCEDimensionSimilarity.java
new file mode 100644
index 00000000..aecdf857
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/math/dimensionsimilarity/MCEDimensionSimilarity.java
@@ -0,0 +1,288 @@
+package de.lmu.ifi.dbs.elki.math.dimensionsimilarity;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.data.VectorUtil;
+import de.lmu.ifi.dbs.elki.data.VectorUtil.SortDBIDsBySingleDimension;
+import de.lmu.ifi.dbs.elki.database.ids.ArrayModifiableDBIDs;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDArrayIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
+import de.lmu.ifi.dbs.elki.database.ids.ModifiableDBIDs;
+import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.math.MathUtil;
+import de.lmu.ifi.dbs.elki.math.Mean;
+import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
+
+/**
+ * Compute dimension similarity by using a nested means discretization.
+ *
+ * Reference:
+ * <p>
+ * Diansheng Guo<br />
+ * Coordinating computational and visual approaches for interactive feature
+ * selection and multivariate clustering<br />
+ * Information Visualization, 2(4), 2003.
+ * </p>
+ *
+ * @author Erich Schubert
+ */
+@Reference(authors = "Diansheng Guo", title = "Coordinating computational and visual approaches for interactive feature selection and multivariate clustering", booktitle = "Information Visualization, 2(4)", url = "http://dx.doi.org/10.1057/palgrave.ivs.9500053")
+public class MCEDimensionSimilarity implements DimensionSimilarity<NumberVector<?>> {
+ /**
+ * Static instance.
+ */
+ public static final MCEDimensionSimilarity STATIC = new MCEDimensionSimilarity();
+
+ /**
+ * Desired size: 35 observations.
+ *
+ * While this could trivially be made parameterizable, it is a reasonable rule
+ * of thumb and not expected to have a major effect.
+ */
+ public static final int TARGET = 35;
+
+ /**
+ * Constructor. Use static instance instead!
+ */
+ protected MCEDimensionSimilarity() {
+ super();
+ }
+
+ @Override
+ public void computeDimensionSimilarites(Relation<? extends NumberVector<?>> relation, DBIDs subset, DimensionSimilarityMatrix matrix) {
+ final int dim = matrix.size();
+
+ // Find a number of bins as recommended by Cheng et al.
+ double p = Math.log(subset.size() / (double) TARGET) / MathUtil.LOG2;
+ // As we are in 2d, take the root (*.5) But let's use at least 1, too.
+ // Check: for 10000 this should give 4, for 150 it gives 1.
+ int power = Math.max(1, (int) Math.floor(p * .5));
+ int gridsize = 1 << power;
+ double loggrid = Math.log((double) gridsize);
+
+ ArrayList<ArrayList<DBIDs>> parts = buildPartitions(relation, subset, power, matrix);
+
+ // Partition sizes
+ int[][] psizes = new int[dim][gridsize];
+ for (int d = 0; d < dim; d++) {
+ final ArrayList<DBIDs> partsd = parts.get(d);
+ final int[] sizesp = psizes[d];
+ for (int i = 0; i < gridsize; i++) {
+ sizesp[i] = partsd.get(i).size();
+ }
+ }
+
+ int[][] res = new int[gridsize][gridsize];
+ for (int x = 0; x < dim; x++) {
+ ArrayList<DBIDs> partsi = parts.get(x);
+ for (int y = x + 1; y < dim; y++) {
+ ArrayList<DBIDs> partsj = parts.get(y);
+ // Fill the intersection matrix
+ intersectionMatrix(res, partsi, partsj, gridsize);
+ matrix.set(x, y, 1. - getMCEntropy(res, psizes[x], psizes[y], subset.size(), gridsize, loggrid));
+ }
+ }
+ }
+
+ /**
+ * Intersect the two 1d grid decompositions, to obtain a 2d matrix.
+ *
+ * @param res Output matrix to fill
+ * @param partsx Partitions in first component
+ * @param partsy Partitions in second component.
+ * @param gridsize Size of partition decomposition
+ */
+ private void intersectionMatrix(int[][] res, ArrayList<? extends DBIDs> partsx, ArrayList<? extends DBIDs> partsy, int gridsize) {
+ for (int x = 0; x < gridsize; x++) {
+ final DBIDs px = partsx.get(x);
+ final int[] rowx = res[x];
+ for (int y = 0; y < gridsize; y++) {
+ rowx[y] = DBIDUtil.intersectionSize(px, partsy.get(y));
+ }
+ }
+ }
+
+ /**
+ * Compute the MCE entropy value.
+ *
+ * @param mat Partition size matrix
+ * @param psizesx Partition sizes on X
+ * @param psizesy Partition sizes on Y
+ * @param size Data set size
+ * @param gridsize Size of grids
+ * @param loggrid Logarithm of grid sizes, for normalization
+ * @return MCE score.
+ */
+ private double getMCEntropy(int[][] mat, int[] psizesx, int[] psizesy, int size, int gridsize, double loggrid) {
+ // Margin entropies:
+ double[] mx = new double[gridsize];
+ double[] my = new double[gridsize];
+
+ for (int i = 0; i < gridsize; i++) {
+ // Note: indexes are a bit tricky here, because we compute both margin
+ // entropies at the same time!
+ final double sumx = (double) psizesx[i];
+ final double sumy = (double) psizesy[i];
+ for (int j = 0; j < gridsize; j++) {
+ double px = mat[i][j] / sumx;
+ double py = mat[j][i] / sumy;
+
+ if (px > 0.) {
+ mx[i] -= px * Math.log(px);
+ }
+ if (py > 0.) {
+ my[i] -= py * Math.log(py);
+ }
+ }
+ }
+
+ // Weighted sums of margin entropies.
+ double sumx = 0., sumy = 0.;
+ for (int i = 0; i < gridsize; i++) {
+ sumx += mx[i] * psizesx[i];
+ sumy += my[i] * psizesy[i];
+ }
+
+ double max = ((sumx > sumy) ? sumx : sumy);
+ return max / (size * loggrid);
+ }
+
+ /**
+ * Calculates "index structures" for every attribute, i.e. sorts a
+ * ModifiableArray of every DBID in the database for every dimension and
+ * stores them in a list.
+ *
+ * @param relation Relation to index
+ * @param ids IDs to use
+ * @param matrix Matrix for dimension information
+ * @return List of sorted objects
+ */
+ private ArrayList<ArrayList<DBIDs>> buildPartitions(Relation<? extends NumberVector<?>> relation, DBIDs ids, int depth, DimensionSimilarityMatrix matrix) {
+ final int dim = matrix.size();
+ ArrayList<ArrayList<DBIDs>> subspaceIndex = new ArrayList<ArrayList<DBIDs>>(dim);
+ SortDBIDsBySingleDimension comp = new VectorUtil.SortDBIDsBySingleDimension(relation);
+ double[] tmp = new double[ids.size()];
+ Mean mean = new Mean();
+
+ for (int i = 0; i < dim; i++) {
+ final int d = matrix.dim(i);
+ // Index for a single dimension:
+ ArrayList<DBIDs> idx = new ArrayList<DBIDs>(1 << depth);
+ // First, we need a copy of the DBIDs and sort it.
+ ArrayModifiableDBIDs sids = DBIDUtil.newArray(ids);
+ comp.setDimension(d);
+ sids.sort(comp);
+ // Now we build the temp array, and compute the first mean.
+ DBIDArrayIter it = sids.iter();
+ for (int j = 0; j < tmp.length; j++, it.advance()) {
+ assert (it.valid());
+ tmp[j] = relation.get(it).doubleValue(d);
+ }
+ divide(it, tmp, idx, 0, tmp.length, depth, mean);
+ assert (idx.size() == (1 << depth));
+ subspaceIndex.add(idx);
+ }
+
+ return subspaceIndex;
+ }
+
+ /**
+ * Recursive call to further subdivide the array.
+ *
+ * @param it Iterator (will be reset!)
+ * @param data 1D data, sorted
+ * @param idx Output index
+ * @param start Interval start
+ * @param end Interval end
+ * @param depth Depth
+ * @param mean Mean working variable (will be reset!)
+ */
+ private void divide(DBIDArrayIter it, double[] data, ArrayList<DBIDs> idx, int start, int end, int depth, Mean mean) {
+ final int count = end - start;
+ if (depth == 0) {
+ if (count > 0) {
+ ModifiableDBIDs out = DBIDUtil.newHashSet(count);
+ it.seek(start);
+ for (int i = count; i > 0; i--, it.advance()) {
+ out.add(it);
+ }
+ idx.add(out);
+ } else {
+ idx.add(DBIDUtil.EMPTYDBIDS);
+ }
+ return;
+ } else {
+ if (count > 0) {
+ mean.reset();
+ for (int i = start; i < end; i++) {
+ mean.put(data[i]);
+ }
+ final double m = mean.getMean();
+ int pos = Arrays.binarySearch(data, start, end, m);
+ if (pos >= 0) {
+ // Ties: try to choose the most central element.
+ int opt = (start + end) >> 1;
+ while (Double.compare(data[pos], m) == 0) {
+ if (pos < opt) {
+ pos++;
+ } else if (pos > opt) {
+ pos--;
+ } else {
+ break;
+ }
+ }
+ } else {
+ pos = (-pos - 1);
+ }
+ divide(it, data, idx, start, pos, depth - 1, mean);
+ divide(it, data, idx, pos, end, depth - 1, mean);
+ } else {
+ // Corner case, that should barely happen. But for ties, we currently
+ // Do not yet assure that it doesn't happen!
+ divide(it, data, idx, start, end, depth - 1, mean);
+ divide(it, data, idx, start, end, depth - 1, mean);
+ }
+ }
+ }
+
+ /**
+ * Parameterization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public static class Parameterizer extends AbstractParameterizer {
+ @Override
+ protected MCEDimensionSimilarity makeInstance() {
+ return STATIC;
+ }
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/math/dimensionsimilarity/SURFINGDimensionSimilarity.java b/src/de/lmu/ifi/dbs/elki/math/dimensionsimilarity/SURFINGDimensionSimilarity.java
new file mode 100644
index 00000000..551b4759
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/math/dimensionsimilarity/SURFINGDimensionSimilarity.java
@@ -0,0 +1,133 @@
+package de.lmu.ifi.dbs.elki.math.dimensionsimilarity;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.BitSet;
+
+import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.database.Database;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
+import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
+import de.lmu.ifi.dbs.elki.database.query.knn.KNNQuery;
+import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.distance.distancefunction.subspace.SubspaceEuclideanDistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
+import de.lmu.ifi.dbs.elki.math.Mean;
+import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
+
+/**
+ * Compute the similarity of dimensions using the SURFING score. The parameter k
+ * for the k nearest neighbors is currently hard-coded to 10% of the set size.
+ *
+ * Note that the complexity is roughly O(n n k) * O(d^2), so this is a rather
+ * slow method without index support, and with k at 10% of n, is actually cubic.
+ * So try to use an appropriate index!
+ *
+ * Reference:
+ * <p>
+ * Christian Baumgartner, Claudia Plant, Karin Kailing, Hans-Peter Kriegel, and
+ * Peer Kröger<br />
+ * Subspace Selection for Clustering High-Dimensional Data<br />
+ * In IEEE International Conference on Data Mining, 2004.
+ * </p>
+ *
+ * TODO: make the subspace distance function and k parameterizable.
+ *
+ * @author Robert Rödler
+ * @author Erich Schubert
+ *
+ * @apiviz.uses SubspaceEuclideanDistanceFunction
+ */
+@Reference(authors = "Christian Baumgartner, Claudia Plant, Karin Kailing, Hans-Peter Kriegel, and Peer Kröger", title = "Subspace Selection for Clustering High-Dimensional Data", booktitle = "IEEE International Conference on Data Mining, 2004", url = "http://dx.doi.org/10.1109/ICDM.2004.10112")
+public class SURFINGDimensionSimilarity implements DimensionSimilarity<NumberVector<?>> {
+ /**
+ * Static instance.
+ */
+ public static final SURFINGDimensionSimilarity STATIC = new SURFINGDimensionSimilarity();
+
+ /**
+ * Constructor. Use static instance instead!
+ */
+ protected SURFINGDimensionSimilarity() {
+ super();
+ }
+
+ @Override
+ public void computeDimensionSimilarites(Relation<? extends NumberVector<?>> relation, DBIDs subset, DimensionSimilarityMatrix matrix) {
+ final int dim = matrix.size();
+ final Database db = relation.getDatabase();
+ Mean kdistmean = new Mean();
+ final int k = Math.max(1, subset.size() / 10);
+
+ double[] knns = new double[subset.size()];
+
+ // TODO: optimize by using 1d indexes?
+ for (int x = 0; x < dim; x++) {
+ final int i = matrix.dim(x);
+ for (int y = x + 1; y < dim; y++) {
+ final int j = matrix.dim(y);
+ BitSet dims = new BitSet(dim);
+ dims.set(i);
+ dims.set(j);
+ DistanceQuery<? extends NumberVector<?>, DoubleDistance> dq = db.getDistanceQuery(relation, new SubspaceEuclideanDistanceFunction(dims));
+ KNNQuery<? extends NumberVector<?>, DoubleDistance> knnq = db.getKNNQuery(dq, k);
+
+ kdistmean.reset();
+ int knn = 0;
+ for (DBIDIter id1 = subset.iter(); id1.valid(); id1.advance(), knn++) {
+ final double kdist = knnq.getKNNForDBID(id1, k).getKNNDistance().doubleValue();
+ kdistmean.put(kdist);
+ knns[knn] = kdist;
+ }
+ double mean = kdistmean.getMean();
+ // Deviation from mean:
+ double diff = 0.;
+ int below = 0;
+ for (int l = 0; l < knns.length; l++) {
+ diff += Math.abs(mean - knns[l]);
+ if (knns[l] < mean) {
+ below++;
+ }
+ }
+ matrix.set(x, y, (below > 0) ? diff / (2. * mean * below) : 0);
+ }
+ }
+ }
+
+ /**
+ * Parameterization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public static class Parameterizer extends AbstractParameterizer {
+ @Override
+ protected SURFINGDimensionSimilarity makeInstance() {
+ return STATIC;
+ }
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/math/dimensionsimilarity/SlopeDimensionSimilarity.java b/src/de/lmu/ifi/dbs/elki/math/dimensionsimilarity/SlopeDimensionSimilarity.java
new file mode 100644
index 00000000..3c81da63
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/math/dimensionsimilarity/SlopeDimensionSimilarity.java
@@ -0,0 +1,142 @@
+package de.lmu.ifi.dbs.elki.math.dimensionsimilarity;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
+import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
+import de.lmu.ifi.dbs.elki.utilities.pairs.Pair;
+
+/**
+ * Arrange dimensions based on the entropy of the slope spectrum.
+ *
+ * @author Erich Schubert
+ * @author Robert Rödler
+ */
+public class SlopeDimensionSimilarity implements DimensionSimilarity<NumberVector<?>> {
+ /**
+ * Static instance.
+ */
+ public static final SlopeDimensionSimilarity STATIC = new SlopeDimensionSimilarity();
+
+ /**
+ * Full precision.
+ */
+ protected final static int PRECISION = 40;
+
+ /**
+ * Precision for entropy normalization.
+ */
+ protected final static double LOG_PRECISION = Math.log(PRECISION);
+
+ /**
+ * Scaling factor.
+ */
+ protected final static double RESCALE = PRECISION * .5;
+
+ /**
+ * Constructor. Use static instance instead!
+ */
+ protected SlopeDimensionSimilarity() {
+ super();
+ }
+
+ @Override
+ public void computeDimensionSimilarites(Relation<? extends NumberVector<?>> relation, DBIDs subset, DimensionSimilarityMatrix matrix) {
+ final int dim = matrix.size();
+ final int size = subset.size();
+
+ // FIXME: Get/keep these statistics in the relation, or compute for the
+ // sample only.
+ double[] off = new double[dim], scale = new double[dim];
+ {
+ Pair<? extends NumberVector<?>, ? extends NumberVector<?>> mm = DatabaseUtil.computeMinMax(relation);
+ NumberVector<?> min = mm.first;
+ NumberVector<?> max = mm.second;
+ for (int d = 0; d < dim; d++) {
+ off[d] = min.doubleValue(matrix.dim(d));
+ final double m = max.doubleValue(matrix.dim(d));
+ scale[d] = (m > off[d]) ? 1. / (m - off[d]) : 1;
+ }
+ }
+
+ // Collect angular histograms.
+ // Note, we only fill half of the matrix
+ int[][][] angles = new int[dim][dim][PRECISION];
+
+ // Scratch buffer
+ double[] vec = new double[dim];
+ for (DBIDIter id = subset.iter(); id.valid(); id.advance()) {
+ final NumberVector<?> obj = relation.get(id);
+ // Map values to 0..1
+ for (int d = 0; d < dim; d++) {
+ vec[d] = (obj.doubleValue(matrix.dim(d)) - off[d]) * scale[d];
+ }
+ for (int i = 0; i < dim - 1; i++) {
+ for (int j = i + 1; j < dim; j++) {
+ // This will be on a scale of 0 .. 2:
+ final double delta = vec[j] - vec[i] + 1;
+ int div = (int) Math.round(delta * RESCALE);
+ // TODO: do we really need this check?
+ div = (div < 0) ? 0 : (div >= PRECISION) ? PRECISION - 1 : div;
+ angles[i][j][div] += 1;
+ }
+ }
+ }
+
+ // Compute entropy in each combination:
+ for (int x = 0; x < dim; x++) {
+ for (int y = x + 1; y < dim; y++) {
+ double entropy = 0.;
+ int[] as = angles[x][y];
+ for (int l = 0; l < PRECISION; l++) {
+ if (as[l] > 0) {
+ final double p = as[l] / (double) size;
+ entropy += p * Math.log(p);
+ }
+ }
+ entropy /= LOG_PRECISION;
+
+ matrix.set(x, y, 1 + entropy);
+ }
+ }
+ }
+
+ /**
+ * Parameterization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public static class Parameterizer extends AbstractParameterizer {
+ @Override
+ protected SlopeDimensionSimilarity makeInstance() {
+ return STATIC;
+ }
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/math/dimensionsimilarity/SlopeInversionDimensionSimilarity.java b/src/de/lmu/ifi/dbs/elki/math/dimensionsimilarity/SlopeInversionDimensionSimilarity.java
new file mode 100644
index 00000000..aad58448
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/math/dimensionsimilarity/SlopeInversionDimensionSimilarity.java
@@ -0,0 +1,158 @@
+package de.lmu.ifi.dbs.elki.math.dimensionsimilarity;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
+import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
+import de.lmu.ifi.dbs.elki.utilities.pairs.Pair;
+
+/**
+ * Arrange dimensions based on the entropy of the slope spectrum. In contrast to
+ * {@link SlopeDimensionSimilarity}, we also take the option of inverting an
+ * axis into account.
+ *
+ * TODO: shouldn't this be normalized by the single-dimension entropies or so?
+ *
+ * @author Erich Schubert
+ * @author Robert Rödler
+ */
+public class SlopeInversionDimensionSimilarity extends SlopeDimensionSimilarity {
+ /**
+ * Static instance.
+ */
+ public static final SlopeInversionDimensionSimilarity STATIC = new SlopeInversionDimensionSimilarity();
+
+ /**
+ * Constructor. Use static instance instead!
+ */
+ protected SlopeInversionDimensionSimilarity() {
+ super();
+ }
+
+ @Override
+ public void computeDimensionSimilarites(Relation<? extends NumberVector<?>> relation, DBIDs subset, DimensionSimilarityMatrix matrix) {
+ final int dim = matrix.size();
+ final int size = subset.size();
+
+ // Collect angular histograms.
+ // Note, we only fill half of the matrix
+ int[][][] angles = new int[dim][dim][PRECISION];
+ int[][][] angleI = new int[dim][dim][PRECISION];
+
+ // FIXME: Get/keep these statistics in the relation, or compute for the
+ // sample only.
+ double[] off = new double[dim], scale = new double[dim];
+ {
+ Pair<? extends NumberVector<?>, ? extends NumberVector<?>> mm = DatabaseUtil.computeMinMax(relation);
+ NumberVector<?> min = mm.first;
+ NumberVector<?> max = mm.second;
+ for (int d = 0; d < dim; d++) {
+ off[d] = min.doubleValue(matrix.dim(d));
+ final double m = max.doubleValue(matrix.dim(d));
+ scale[d] = (m > off[d]) ? 1. / (m - off[d]) : 1;
+ }
+ }
+
+ // Scratch buffer
+ double[] vec = new double[dim];
+ for (DBIDIter id = subset.iter(); id.valid(); id.advance()) {
+ final NumberVector<?> obj = relation.get(id);
+ // Map values to 0..1
+ for (int d = 0; d < dim; d++) {
+ vec[d] = (obj.doubleValue(matrix.dim(d)) - off[d]) * scale[d];
+ }
+ for (int i = 0; i < dim - 1; i++) {
+ for (int j = i + 1; j < dim; j++) {
+ {
+ // This will be on a scale of 0 .. 2:
+ final double delta = vec[j] - vec[i] + 1;
+ int div = (int) Math.round(delta * RESCALE);
+ // TODO: do we really need this check?
+ div = (div < 0) ? 0 : (div >= PRECISION) ? PRECISION - 1 : div;
+ angles[i][j][div] += 1;
+ }
+ {
+ // This will be on a scale of 0 .. 2:
+ final double delta = vec[j] + vec[i];
+ int div = (int) Math.round(delta * RESCALE);
+ // TODO: do we really need this check?
+ div = (div < 0) ? 0 : (div >= PRECISION) ? PRECISION - 1 : div;
+ angleI[i][j][div] += 1;
+ }
+ }
+ }
+ }
+
+ // Compute entropy in each combination:
+ for (int x = 0; x < dim; x++) {
+ for (int y = x + 1; y < dim; y++) {
+ double entropy = 0., entropyI = 0;
+ {
+ int[] as = angles[x][y];
+ for (int l = 0; l < PRECISION; l++) {
+ if (as[l] > 0) {
+ final double p = as[l] / (double) size;
+ entropy += p * Math.log(p);
+ }
+ }
+ }
+ {
+ int[] as = angleI[x][y];
+ for (int l = 0; l < PRECISION; l++) {
+ if (as[l] > 0) {
+ final double p = as[l] / (double) size;
+ entropyI += p * Math.log(p);
+ }
+ }
+ }
+ if (entropy >= entropyI) {
+ entropy = 1 + entropy / LOG_PRECISION;
+ matrix.set(x, y, entropy);
+ } else {
+ entropyI = 1 + entropyI / LOG_PRECISION;
+ // Negative sign to indicate the axes might be inversely related
+ matrix.set(x, y, -entropyI);
+ }
+ }
+ }
+ }
+
+ /**
+ * Parameterization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public static class Parameterizer extends AbstractParameterizer {
+ @Override
+ protected SlopeInversionDimensionSimilarity makeInstance() {
+ return STATIC;
+ }
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/math/dimensionsimilarity/package-info.java b/src/de/lmu/ifi/dbs/elki/math/dimensionsimilarity/package-info.java
new file mode 100644
index 00000000..b6c27c57
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/math/dimensionsimilarity/package-info.java
@@ -0,0 +1,26 @@
+/**
+ * <p>Functions to compute the similarity of dimensions (or the interestingness of the combination).</p>
+ */
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package de.lmu.ifi.dbs.elki.math.dimensionsimilarity; \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/math/geometry/AlphaShape.java b/src/de/lmu/ifi/dbs/elki/math/geometry/AlphaShape.java
index 35858c67..1ab324c3 100644
--- a/src/de/lmu/ifi/dbs/elki/math/geometry/AlphaShape.java
+++ b/src/de/lmu/ifi/dbs/elki/math/geometry/AlphaShape.java
@@ -5,7 +5,7 @@ import java.util.BitSet;
import java.util.List;
import de.lmu.ifi.dbs.elki.data.spatial.Polygon;
-import de.lmu.ifi.dbs.elki.math.geometry.SweepHullDelaunay2D.Triangle;
+
import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector;
/*
@@ -53,7 +53,7 @@ public class AlphaShape {
/**
* Delaunay triangulation
*/
- private ArrayList<Triangle> delaunay = null;
+ private ArrayList<SweepHullDelaunay2D.Triangle> delaunay = null;
public AlphaShape(List<Vector> points, double alpha) {
this.alpha2 = alpha * alpha;
@@ -71,9 +71,9 @@ public class AlphaShape {
List<Vector> cur = new ArrayList<Vector>();
for(int i = 0 /* = used.nextClearBit(0) */; i < delaunay.size() && i >= 0; i = used.nextClearBit(i + 1)) {
- if(used.get(i) == false) {
+ if(!used.get(i)) {
used.set(i);
- Triangle tri = delaunay.get(i);
+ SweepHullDelaunay2D.Triangle tri = delaunay.get(i);
if(tri.r2 <= alpha2) {
// Check neighbors
processNeighbor(cur, used, i, tri.ab, tri.b);
@@ -96,7 +96,7 @@ public class AlphaShape {
return;
}
used.set(ab);
- final Triangle next = delaunay.get(ab);
+ final SweepHullDelaunay2D.Triangle next = delaunay.get(ab);
if(next.r2 < alpha2) {
// Continue where we left off...
if(next.ab == i) {
diff --git a/src/de/lmu/ifi/dbs/elki/math/geometry/PrimsMinimumSpanningTree.java b/src/de/lmu/ifi/dbs/elki/math/geometry/PrimsMinimumSpanningTree.java
new file mode 100644
index 00000000..d6675256
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/math/geometry/PrimsMinimumSpanningTree.java
@@ -0,0 +1,177 @@
+package de.lmu.ifi.dbs.elki.math.geometry;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.Arrays;
+import java.util.BitSet;
+
+import de.lmu.ifi.dbs.elki.math.linearalgebra.Matrix;
+import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
+
+/**
+ * Prim's algorithm for finding the minimum spanning tree.
+ *
+ * Implementation for <em>dense</em> graphs, represented as distance matrix.
+ *
+ * Reference:
+ * <p>
+ * R. C. Prim<br />
+ * Shortest connection networks and some generalizations<br />
+ * In: Bell System Technical Journal, 36 (1957), pp. 1389–140
+ * </p>
+ *
+ * @author Erich Schubert
+ */
+@Reference(authors = "R. C. Prim", title = "Shortest connection networks and some generalizations", booktitle = "Bell System Technical Journal, 36 (1957)")
+public class PrimsMinimumSpanningTree {
+ /**
+ * Adapter class for double[][] matrixes.
+ */
+ public static final Array2DAdapter ARRAY2D_ADAPTER = new Array2DAdapter();
+
+ /**
+ * Process a k x k distance matrix.
+ *
+ * @param mat Distance matrix
+ * @return list of node number pairs representing the edges
+ */
+ public static int[] processDense(double[][] mat) {
+ return processDense(mat, ARRAY2D_ADAPTER);
+ }
+
+ /**
+ * Process a k x k distance matrix.
+ *
+ * @param mat Distance matrix
+ * @return list of node number pairs representing the edges
+ */
+ public static int[] processDense(Matrix mat) {
+ return processDense(mat.getArrayRef(), ARRAY2D_ADAPTER);
+ }
+
+ /**
+ * Run Prim's algorithm on a dense graph.
+ *
+ * @param data Data set
+ * @param adapter Adapter instance
+ * @return list of node number pairs representing the edges
+ */
+ public static <T> int[] processDense(T data, Adapter<T> adapter) {
+ // Number of nodes
+ final int n = adapter.size(data);
+ // Output array storage
+ int[] mst = new int[(n - 1) << 1];
+ // Best distance for each node
+ double[] best = new double[n];
+ Arrays.fill(best, Double.POSITIVE_INFINITY);
+ // Best previous node
+ int[] src = new int[n];
+ // Nodes already handled
+ BitSet in = new BitSet(n);
+
+ // We always start at "random" node 0
+ // Note: we use this below in the "j" loop!
+ int current = 0;
+ in.set(current);
+ best[current] = 0;
+
+ // Search
+ for (int i = n - 2; i >= 0; i--) {
+ // Update best and src from current:
+ int newbesti = -1;
+ double newbestd = Double.POSITIVE_INFINITY;
+ // Note: we assume we started with 0, and can thus skip it
+ for (int j = in.nextClearBit(1); j < n && j > 0; j = in.nextClearBit(j + 1)) {
+ final double dist = adapter.distance(data, current, j);
+ if (dist < best[j]) {
+ best[j] = dist;
+ src[j] = current;
+ }
+ if (best[j] < newbestd) {
+ newbestd = best[j];
+ newbesti = j;
+ }
+ }
+ assert (newbesti >= 0);
+ // Flag
+ in.set(newbesti);
+ // Store edge
+ mst[i << 1] = newbesti;
+ mst[(i << 1) + 1] = src[newbesti];
+ // Continue
+ current = newbesti;
+ }
+ return mst;
+ }
+
+ /**
+ * Adapter interface to allow use with different data representations.
+ *
+ * @author Erich Schubert
+ *
+ * @param <T> Data reference
+ */
+ public interface Adapter<T> {
+ /**
+ * Get the distance of two objects
+ *
+ * @param data Data set
+ * @param i First index
+ * @param j Second index
+ * @return Distance of objects number i and number j.
+ */
+ public double distance(T data, int i, int j);
+
+ /**
+ * Get number of objects in dataset
+ *
+ * @return Size
+ */
+ public int size(T data);
+ }
+
+ /**
+ * Adapter for a simple 2d double matrix.
+ *
+ * @author Erich Schubert
+ */
+ public static class Array2DAdapter implements Adapter<double[][]> {
+ /**
+ * Constructor. Use static instance!
+ */
+ private Array2DAdapter() {
+ // Use static instance!
+ }
+
+ @Override
+ public double distance(double[][] data, int i, int j) {
+ return data[i][j];
+ }
+
+ @Override
+ public int size(double[][] data) {
+ return data.length;
+ }
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/math/geometry/SweepHullDelaunay2D.java b/src/de/lmu/ifi/dbs/elki/math/geometry/SweepHullDelaunay2D.java
index 4f78cb76..afb8db26 100644
--- a/src/de/lmu/ifi/dbs/elki/math/geometry/SweepHullDelaunay2D.java
+++ b/src/de/lmu/ifi/dbs/elki/math/geometry/SweepHullDelaunay2D.java
@@ -54,7 +54,7 @@ public class SweepHullDelaunay2D {
/**
* Class logger
*/
- private static final Logging logger = Logging.getLogger(SweepHullDelaunay2D.class);
+ private static final Logging LOG = Logging.getLogger(SweepHullDelaunay2D.class);
/**
* The current set of points.
@@ -152,7 +152,7 @@ public class SweepHullDelaunay2D {
besttri.copyFrom(testtri);
besti = i;
}
- else if(besttri.r2 * 4 < sort[i].first) {
+ else if(besttri.r2 * 4. < sort[i].first) {
// Stop early, points are too far away from seed.
break;
}
@@ -178,7 +178,7 @@ public class SweepHullDelaunay2D {
hull.add(new IntIntPair(besttri.b, 0));
hull.add(new IntIntPair(besttri.c, 0));
- if(logger.isDebuggingFinest()) {
+ if(LOG.isDebuggingFinest()) {
debugHull();
}
@@ -286,8 +286,8 @@ public class SweepHullDelaunay2D {
lasttri = tristart + newtris.size() - 1;
}
final int hullsize = hull.size();
- if(logger.isDebuggingFinest()) {
- logger.debugFinest("Size: " + hullsize + " start: " + hstart + " end: " + hend);
+ if(LOG.isDebuggingFinest()) {
+ LOG.debugFinest("Size: " + hullsize + " start: " + hstart + " end: " + hend);
}
if(hend < hullsize) {
ListIterator<IntIntPair> iter = hull.listIterator();
@@ -338,7 +338,7 @@ public class SweepHullDelaunay2D {
iter.remove();
}
}
- if(logger.isDebuggingFinest()) {
+ if(LOG.isDebuggingFinest()) {
debugHull();
}
if(!hullonly) {
@@ -410,11 +410,11 @@ public class SweepHullDelaunay2D {
* Debug helper
*/
void debugHull() {
- StringBuffer buf = new StringBuffer();
+ StringBuilder buf = new StringBuilder();
for(IntIntPair p : hull) {
- buf.append(p).append(" ");
+ buf.append(p).append(' ');
}
- logger.debugFinest(buf);
+ LOG.debugFinest(buf);
}
/**
diff --git a/src/de/lmu/ifi/dbs/elki/math/geometry/XYCurve.java b/src/de/lmu/ifi/dbs/elki/math/geometry/XYCurve.java
index 5d39de16..3a53f2a2 100644
--- a/src/de/lmu/ifi/dbs/elki/math/geometry/XYCurve.java
+++ b/src/de/lmu/ifi/dbs/elki/math/geometry/XYCurve.java
@@ -90,7 +90,7 @@ public class XYCurve implements Result, TextWriteable {
*/
public XYCurve(String labelx, String labely, int size) {
super();
- this.data = new TDoubleArrayList(size * 2);
+ this.data = new TDoubleArrayList(size << 1);
this.labelx = labelx;
this.labely = labely;
}
@@ -295,21 +295,21 @@ public class XYCurve implements Result, TextWriteable {
out.commentPrint(labely);
out.flush();
for(int pos = 0; pos < data.size(); pos+=2) {
- out.inlinePrint(data.get(pos));
- out.inlinePrint(data.get(pos + 1));
+ out.inlinePrint(Double.toString(data.get(pos)));
+ out.inlinePrint(Double.toString(data.get(pos + 1)));
out.flush();
}
}
@Override
public String toString() {
- StringBuffer buf = new StringBuffer();
+ StringBuilder buf = new StringBuilder();
buf.append("XYCurve[");
- buf.append(labelx).append(",").append(labely).append(":");
+ buf.append(labelx).append(',').append(labely).append(':');
for(int pos = 0; pos < data.size(); pos += 2) {
- buf.append(" ").append(data.get(pos)).append(",").append(data.get(pos + 1));
+ buf.append(' ').append(data.get(pos)).append(',').append(data.get(pos + 1));
}
- buf.append("]");
+ buf.append(']');
return buf.toString();
}
diff --git a/src/de/lmu/ifi/dbs/elki/math/histograms/AggregatingHistogram.java b/src/de/lmu/ifi/dbs/elki/math/histograms/AggregatingHistogram.java
deleted file mode 100644
index 87b93910..00000000
--- a/src/de/lmu/ifi/dbs/elki/math/histograms/AggregatingHistogram.java
+++ /dev/null
@@ -1,254 +0,0 @@
-package de.lmu.ifi.dbs.elki.math.histograms;
-
-/*
- This file is part of ELKI:
- Environment for Developing KDD-Applications Supported by Index-Structures
-
- Copyright (C) 2012
- Ludwig-Maximilians-Universität München
- Lehr- und Forschungseinheit für Datenbanksysteme
- ELKI Development Team
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-import de.lmu.ifi.dbs.elki.math.MeanVariance;
-import de.lmu.ifi.dbs.elki.utilities.pairs.DoubleDoublePair;
-import de.lmu.ifi.dbs.elki.utilities.pairs.IntIntPair;
-import de.lmu.ifi.dbs.elki.utilities.pairs.Pair;
-
-/**
- * Class for the typical case of an aggregating (e.g. counting, averaging)
- * Histogram.
- *
- * @author Erich Schubert
- *
- * @apiviz.composedOf de.lmu.ifi.dbs.elki.math.histograms.AggregatingHistogram.Adapter
- *
- * @param <T> Type of data in histogram
- * @param <D> Type of input data
- */
-public class AggregatingHistogram<T, D> extends ReplacingHistogram<T> {
- /**
- * The class we are using for putting data.
- */
- private Adapter<T, D> putter;
-
- /**
- * Adapter class for an AggregatingHistogram
- *
- * @author Erich Schubert
- *
- * @param <T> Histogram bin type
- * @param <D> Incoming data type
- */
- public static abstract class Adapter<T, D> extends ReplacingHistogram.Adapter<T> {
- /**
- * Update an existing histogram value with new data.
- *
- * @param existing Existing histogram data
- * @param data New value
- * @return Aggregated value
- */
- public abstract T aggregate(T existing, D data);
- }
-
- /**
- * Constructor with Adapter.
- *
- * @param bins Number of bins
- * @param min Minimum value
- * @param max Maximum value
- * @param adapter Adapter
- */
- public AggregatingHistogram(int bins, double min, double max, Adapter<T, D> adapter) {
- super(bins, min, max, adapter);
- this.putter = adapter;
- }
-
- /**
- * Add a value to the histogram using the aggregation adapter.
- *
- * @param coord Coordinate
- * @param value New value
- */
- public void aggregate(double coord, D value) {
- super.replace(coord, putter.aggregate(super.get(coord), value));
- }
-
- /**
- * Convenience constructor for {@link MeanVariance}-based Histograms. Uses a
- * constructor to initialize bins with new {@link MeanVariance} objects
- *
- * @param bins Number of bins
- * @param min Minimum coordinate
- * @param max Maximum coordinate
- * @return New histogram for {@link MeanVariance}.
- */
- public static AggregatingHistogram<MeanVariance, Double> MeanVarianceHistogram(int bins, double min, double max) {
- return new AggregatingHistogram<MeanVariance, Double>(bins, min, max, new Adapter<MeanVariance, Double>() {
- @Override
- public MeanVariance make() {
- return new MeanVariance();
- }
-
- @Override
- public MeanVariance aggregate(MeanVariance existing, Double data) {
- existing.put(data);
- return existing;
- }
- });
- }
-
- /**
- * Convenience constructor for Integer-based Histograms. Uses a constructor to
- * initialize bins with Integer(0). Aggregation is done by adding the values
- *
- * @param bins Number of bins
- * @param min Minimum coordinate
- * @param max Maximum coordinate
- * @return New histogram for Integers.
- */
- public static AggregatingHistogram<Integer, Integer> IntSumHistogram(int bins, double min, double max) {
- return new AggregatingHistogram<Integer, Integer>(bins, min, max, new Adapter<Integer, Integer>() {
- @Override
- public Integer make() {
- return new Integer(0);
- }
-
- @Override
- public Integer aggregate(Integer existing, Integer data) {
- return existing + data;
- }
- });
- }
-
- /**
- * Convenience constructor for Long-based Histograms. Uses a constructor to
- * initialize bins with Long(0L). Aggregation is done by adding the values
- *
- * @param bins Number of bins
- * @param min Minimum coordinate
- * @param max Maximum coordinate
- * @return New histogram for Integers.
- */
- public static AggregatingHistogram<Long, Long> LongSumHistogram(int bins, double min, double max) {
- return new AggregatingHistogram<Long, Long>(bins, min, max, new Adapter<Long, Long>() {
- @Override
- public Long make() {
- return new Long(0L);
- }
-
- @Override
- public Long aggregate(Long existing, Long data) {
- return existing + data;
- }
- });
- }
-
- /**
- * Convenience constructor for Double-based Histograms. Uses a constructor to
- * initialize bins with Double(0.0). Aggregation is done by adding the values
- *
- * @param bins Number of bins
- * @param min Minimum coordinate
- * @param max Maximum coordinate
- * @return New histogram for Double.
- */
- public static AggregatingHistogram<Double, Double> DoubleSumHistogram(int bins, double min, double max) {
- return new AggregatingHistogram<Double, Double>(bins, min, max, new Adapter<Double, Double>() {
- @Override
- public Double make() {
- return new Double(0.0);
- }
-
- @Override
- public Double aggregate(Double existing, Double data) {
- return existing + data;
- }
- });
- }
-
- /**
- * Histograms that work like two {@link #IntSumHistogram}, component wise.
- *
- * @param bins Number of bins.
- * @param min Minimum value
- * @param max Maximum value
- * @return Histogram object
- */
- public static AggregatingHistogram<IntIntPair, IntIntPair> IntSumIntSumHistogram(int bins, double min, double max) {
- return new AggregatingHistogram<IntIntPair, IntIntPair>(bins, min, max, new Adapter<IntIntPair, IntIntPair>() {
- @Override
- public IntIntPair make() {
- return new IntIntPair(0, 0);
- }
-
- @Override
- public IntIntPair aggregate(IntIntPair existing, IntIntPair data) {
- existing.first = existing.first + data.first;
- existing.second = existing.second + data.second;
- return existing;
- }
- });
- }
-
- /**
- * Histograms that work like two {@link #LongSumHistogram}, component wise.
- *
- * @param bins Number of bins.
- * @param min Minimum value
- * @param max Maximum value
- * @return Histogram object
- */
- public static AggregatingHistogram<Pair<Long, Long>, Pair<Long, Long>> LongSumLongSumHistogram(int bins, double min, double max) {
- return new AggregatingHistogram<Pair<Long, Long>, Pair<Long, Long>>(bins, min, max, new Adapter<Pair<Long, Long>, Pair<Long, Long>>() {
- @Override
- public Pair<Long, Long> make() {
- return new Pair<Long, Long>(0L, 0L);
- }
-
- @Override
- public Pair<Long, Long> aggregate(Pair<Long, Long> existing, Pair<Long, Long> data) {
- existing.setFirst(existing.getFirst() + data.getFirst());
- existing.setSecond(existing.getSecond() + data.getSecond());
- return existing;
- }
- });
- }
-
- /**
- * Histograms that work like two {@link #DoubleSumHistogram}, component wise.
- *
- * @param bins Number of bins.
- * @param min Minimum value
- * @param max Maximum value
- * @return Histogram object
- */
- public static AggregatingHistogram<DoubleDoublePair, DoubleDoublePair> DoubleSumDoubleSumHistogram(int bins, double min, double max) {
- return new AggregatingHistogram<DoubleDoublePair, DoubleDoublePair>(bins, min, max, new Adapter<DoubleDoublePair, DoubleDoublePair>() {
- @Override
- public DoubleDoublePair make() {
- return new DoubleDoublePair(0., 0.);
- }
-
- @Override
- public DoubleDoublePair aggregate(DoubleDoublePair existing, DoubleDoublePair data) {
- existing.first = existing.first + data.first;
- existing.second = existing.second + data.second;
- return existing;
- }
- });
- }
-} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/math/histograms/FlexiHistogram.java b/src/de/lmu/ifi/dbs/elki/math/histograms/FlexiHistogram.java
deleted file mode 100644
index 07d20dd2..00000000
--- a/src/de/lmu/ifi/dbs/elki/math/histograms/FlexiHistogram.java
+++ /dev/null
@@ -1,487 +0,0 @@
-package de.lmu.ifi.dbs.elki.math.histograms;
-
-/*
- This file is part of ELKI:
- Environment for Developing KDD-Applications Supported by Index-Structures
-
- Copyright (C) 2012
- Ludwig-Maximilians-Universität München
- Lehr- und Forschungseinheit für Datenbanksysteme
- ELKI Development Team
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-import java.util.ArrayList;
-import java.util.Iterator;
-
-import de.lmu.ifi.dbs.elki.math.MeanVariance;
-import de.lmu.ifi.dbs.elki.math.scales.LinearScale;
-import de.lmu.ifi.dbs.elki.utilities.pairs.DoubleDoublePair;
-import de.lmu.ifi.dbs.elki.utilities.pairs.DoubleObjPair;
-import de.lmu.ifi.dbs.elki.utilities.pairs.IntIntPair;
-import de.lmu.ifi.dbs.elki.utilities.pairs.Pair;
-
-/**
- * Histogram with flexible size, guaranteed to be in [bin, 2*bin[
- *
- * @author Erich Schubert
- *
- * @apiviz.composedOf de.lmu.ifi.dbs.elki.math.histograms.FlexiHistogram.Adapter
- *
- * @param <T> Type of data in histogram
- * @param <D> Type of input data
- */
-public class FlexiHistogram<T, D> extends AggregatingHistogram<T, D> {
- /**
- * Adapter class, extended "maker".
- */
- private Adapter<T, D> downsampler;
-
- /**
- * Cache for elements when not yet initialized.
- */
- private ArrayList<DoubleObjPair<D>> tempcache = null;
-
- /**
- * Destination (minimum) size of the structure. At most 2*destsize bins are
- * allowed.
- */
- private int destsize;
-
- /**
- * Adapter interface to specify bin creation, data caching and combination.
- *
- * @author Erich Schubert
- *
- * @param <T> Type of data in histogram
- * @param <D> Type of input data
- */
- public static abstract class Adapter<T, D> extends AggregatingHistogram.Adapter<T, D> {
- /**
- * Rule to combine two bins into one.
- *
- * first and second MAY be modified and returned.
- *
- * @param first First bin value
- * @param second Second bin value
- * @return combined bin value
- */
- public abstract T downsample(T first, T second);
-
- /**
- * Clone a data passed to the algorithm for computing the initial size.
- *
- * @param data Data to be cloned
- * @return cloned data
- */
- public abstract D cloneForCache(D data);
- }
-
- /**
- * Create a new histogram for an unknown data range.
- *
- * The generated histogram is guaranteed to have within {@code bins} and
- * {@code 2*bins} bins in length.
- *
- * @param bins Target number of bins
- * @param adapter Adapter for data types and combination rules.
- */
- public FlexiHistogram(int bins, Adapter<T, D> adapter) {
- super(bins, 0.0, 1.0, adapter);
- this.destsize = bins;
- this.downsampler = adapter;
- tempcache = new ArrayList<DoubleObjPair<D>>(this.destsize * 2);
- }
-
- private synchronized void materialize() {
- // already materialized?
- if(tempcache == null) {
- return;
- }
- // we can't really initialize, but since we have to, we'll just stick
- // to 0.0 and 1.0 as used in the constructor.
- if(tempcache.size() <= 0) {
- tempcache = null;
- return;
- }
- double min = Double.MAX_VALUE;
- double max = Double.MIN_VALUE;
- for(DoubleObjPair<D> pair : tempcache) {
- min = Math.min(min, pair.first);
- max = Math.max(max, pair.first);
- }
- // use the LinearScale magic to round to "likely suiteable" step sizes.
- LinearScale scale = new LinearScale(min, max);
- min = scale.getMin();
- max = scale.getMax();
- this.base = min;
- this.max = max;
- this.binsize = (max - min) / this.destsize;
- // initialize array
- this.data = new ArrayList<T>(this.destsize * 2);
- for(int i = 0; i < this.destsize; i++) {
- this.data.add(downsampler.make());
- }
- // re-insert data we have
- for(DoubleObjPair<D> pair : tempcache) {
- super.aggregate(pair.first, pair.second);
- }
- // delete cache, signal that we're initialized
- tempcache = null;
- }
-
- @Override
- public synchronized void replace(double coord, T d) {
- materialize();
- // super class put will already handle histogram resizing
- super.replace(coord, d);
- // but not resampling
- testResample();
- }
-
- private void testResample() {
- while(super.size >= 2 * this.destsize) {
- // Resampling.
- ArrayList<T> newdata = new ArrayList<T>(this.destsize * 2);
- for(int i = 0; i < super.size; i += 2) {
- if(i + 1 < super.size) {
- newdata.add(downsampler.downsample(super.data.get(i), super.data.get(i + 1)));
- }
- else {
- newdata.add(downsampler.downsample(super.data.get(i), super.make()));
- }
- }
- // recalculate histogram base.
- double base = super.base - super.offset * super.binsize;
- // update data
- super.data = newdata;
- // update sizes
- super.base = base;
- super.offset = 0;
- super.size = newdata.size();
- super.binsize = super.binsize * 2;
- super.max = super.base + super.binsize * super.size;
- }
- }
-
- @Override
- public T get(double coord) {
- materialize();
- return super.get(coord);
- }
-
- @Override
- public double getBinsize() {
- materialize();
- return super.getBinsize();
- }
-
- @Override
- public double getCoverMaximum() {
- materialize();
- return super.getCoverMaximum();
- }
-
- @Override
- public double getCoverMinimum() {
- materialize();
- return super.getCoverMinimum();
- }
-
- @Override
- public ArrayList<T> getData() {
- materialize();
- return super.getData();
- }
-
- @Override
- public int getNumBins() {
- materialize();
- return super.getNumBins();
- }
-
- @Override
- public double getBinMean(int bin) {
- materialize();
- return super.getBinMean(bin);
- }
-
- @Override
- public double getBinMin(int bin) {
- materialize();
- return super.getBinMin(bin);
- }
-
- @Override
- public double getBinMax(int bin) {
- materialize();
- return super.getBinMax(bin);
- }
-
- @Override
- public Iterator<DoubleObjPair<T>> iterator() {
- materialize();
- return super.iterator();
- }
-
- @Override
- public Iterator<DoubleObjPair<T>> reverseIterator() {
- materialize();
- return super.reverseIterator();
- }
-
- @Override
- public void aggregate(double coord, D value) {
- if(tempcache != null) {
- if(tempcache.size() < this.destsize * 2) {
- tempcache.add(new DoubleObjPair<D>(coord, downsampler.cloneForCache(value)));
- return;
- }
- else {
- materialize();
- // .. and continue below!
- }
- }
- // super class put will already handle histogram resizing
- super.aggregate(coord, value);
- // but not resampling
- testResample();
- }
-
- /**
- * Convenience constructor for Integer-based Histograms. Uses a constructor to
- * initialize bins with Integer(0)
- *
- * @param bins Number of bins
- * @return New histogram for Integer.
- */
- public static FlexiHistogram<Integer, Integer> IntSumHistogram(int bins) {
- return new FlexiHistogram<Integer, Integer>(bins, new Adapter<Integer, Integer>() {
- @Override
- public Integer make() {
- return new Integer(0);
- }
-
- @Override
- public Integer cloneForCache(Integer data) {
- // no need to clone, Integer are singletons
- return data;
- }
-
- @Override
- public Integer downsample(Integer first, Integer second) {
- return first + second;
- }
-
- @Override
- public Integer aggregate(Integer existing, Integer data) {
- return existing + data;
- }
- });
- }
-
- /**
- * Convenience constructor for Long-based Histograms. Uses a constructor to
- * initialize bins with Long(0)
- *
- * @param bins Number of bins
- * @return New histogram for Long.
- */
- public static FlexiHistogram<Long, Long> LongSumHistogram(int bins) {
- return new FlexiHistogram<Long, Long>(bins, new Adapter<Long, Long>() {
- @Override
- public Long make() {
- return new Long(0);
- }
-
- @Override
- public Long cloneForCache(Long data) {
- // no need to clone, Long are singletons
- return data;
- }
-
- @Override
- public Long downsample(Long first, Long second) {
- return first + second;
- }
-
- @Override
- public Long aggregate(Long existing, Long data) {
- return existing + data;
- }
- });
- }
-
- /**
- * Convenience constructor for Double-based Histograms. Uses a constructor to
- * initialize bins with Double(0), and downsampling is done by summation.
- *
- * @param bins Number of bins
- * @return New histogram for Doubles.
- */
- public static FlexiHistogram<Double, Double> DoubleSumHistogram(int bins) {
- return new FlexiHistogram<Double, Double>(bins, new Adapter<Double, Double>() {
- @Override
- public Double make() {
- return new Double(0.0);
- }
-
- @Override
- public Double cloneForCache(Double data) {
- // no need to clone, Doubles are singletons
- return data;
- }
-
- @Override
- public Double downsample(Double first, Double second) {
- return first + second;
- }
-
- @Override
- public Double aggregate(Double existing, Double data) {
- return existing + data;
- }
- });
- }
-
- /**
- * Convenience constructor for {@link MeanVariance}-based Histograms. Uses a
- * constructor to initialize bins with new {@link MeanVariance} objects
- *
- * @param bins Number of bins
- * @return New histogram for {@link MeanVariance}.
- */
- public static FlexiHistogram<MeanVariance, Double> MeanVarianceHistogram(int bins) {
- return new FlexiHistogram<MeanVariance, Double>(bins, new Adapter<MeanVariance, Double>() {
- @Override
- public MeanVariance make() {
- return new MeanVariance();
- }
-
- @Override
- public Double cloneForCache(Double data) {
- return data;
- }
-
- @Override
- public MeanVariance downsample(MeanVariance first, MeanVariance second) {
- first.put(second);
- return first;
- }
-
- @Override
- public MeanVariance aggregate(MeanVariance existing, Double data) {
- existing.put(data);
- return existing;
- }
- });
- }
-
- /**
- * Histograms that work like two {@link #IntSumHistogram}, component wise.
- *
- * @param bins Number of bins.
- * @return New Histogram object
- */
- public static FlexiHistogram<IntIntPair, IntIntPair> IntSumIntSumHistogram(int bins) {
- return new FlexiHistogram<IntIntPair, IntIntPair>(bins, new Adapter<IntIntPair, IntIntPair>() {
- @Override
- public IntIntPair make() {
- return new IntIntPair(0, 0);
- }
-
- @Override
- public IntIntPair cloneForCache(IntIntPair data) {
- return new IntIntPair(data.first, data.second);
- }
-
- @Override
- public IntIntPair downsample(IntIntPair first, IntIntPair second) {
- return new IntIntPair(first.first + second.first, first.second + second.second);
- }
-
- @Override
- public IntIntPair aggregate(IntIntPair existing, IntIntPair data) {
- existing.first = existing.first + data.first;
- existing.second = existing.second + data.second;
- return existing;
- }
- });
- }
-
- /**
- * Histograms that work like two {@link #LongSumHistogram}, component wise.
- *
- * @param bins Number of bins.
- * @return New Histogram object
- */
- public static FlexiHistogram<Pair<Long, Long>, Pair<Long, Long>> LongSumLongSumHistogram(int bins) {
- return new FlexiHistogram<Pair<Long, Long>, Pair<Long, Long>>(bins, new Adapter<Pair<Long, Long>, Pair<Long, Long>>() {
- @Override
- public Pair<Long, Long> make() {
- return new Pair<Long, Long>(0L, 0L);
- }
-
- @Override
- public Pair<Long, Long> cloneForCache(Pair<Long, Long> data) {
- return new Pair<Long, Long>(data.getFirst(), data.getSecond());
- }
-
- @Override
- public Pair<Long, Long> downsample(Pair<Long, Long> first, Pair<Long, Long> second) {
- return new Pair<Long, Long>(first.getFirst() + second.getFirst(), first.getSecond() + second.getSecond());
- }
-
- @Override
- public Pair<Long, Long> aggregate(Pair<Long, Long> existing, Pair<Long, Long> data) {
- existing.setFirst(existing.getFirst() + data.getFirst());
- existing.setSecond(existing.getSecond() + data.getSecond());
- return existing;
- }
- });
- }
-
- /**
- * Histograms that work like two {@link #DoubleSumHistogram}, component wise.
- *
- * @param bins Number of bins.
- * @return New Histogram object
- */
- public static FlexiHistogram<DoubleDoublePair, DoubleDoublePair> DoubleSumDoubleSumHistogram(int bins) {
- return new FlexiHistogram<DoubleDoublePair, DoubleDoublePair>(bins, new Adapter<DoubleDoublePair, DoubleDoublePair>() {
- @Override
- public DoubleDoublePair make() {
- return new DoubleDoublePair(0., 0.);
- }
-
- @Override
- public DoubleDoublePair cloneForCache(DoubleDoublePair data) {
- return new DoubleDoublePair(data.first, data.second);
- }
-
- @Override
- public DoubleDoublePair downsample(DoubleDoublePair first, DoubleDoublePair second) {
- return new DoubleDoublePair(first.first + second.first, first.second + second.second);
- }
-
- @Override
- public DoubleDoublePair aggregate(DoubleDoublePair existing, DoubleDoublePair data) {
- existing.first = existing.first + data.first;
- existing.second = existing.second + data.second;
- return existing;
- }
- });
- }
-}
diff --git a/src/de/lmu/ifi/dbs/elki/math/histograms/ReplacingHistogram.java b/src/de/lmu/ifi/dbs/elki/math/histograms/ReplacingHistogram.java
deleted file mode 100644
index 515fa016..00000000
--- a/src/de/lmu/ifi/dbs/elki/math/histograms/ReplacingHistogram.java
+++ /dev/null
@@ -1,452 +0,0 @@
-package de.lmu.ifi.dbs.elki.math.histograms;
-
-/*
- This file is part of ELKI:
- Environment for Developing KDD-Applications Supported by Index-Structures
-
- Copyright (C) 2012
- Ludwig-Maximilians-Universität München
- Lehr- und Forschungseinheit für Datenbanksysteme
- ELKI Development Team
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-import java.util.ArrayList;
-import java.util.Iterator;
-
-import de.lmu.ifi.dbs.elki.utilities.pairs.DoubleDoublePair;
-import de.lmu.ifi.dbs.elki.utilities.pairs.DoubleObjPair;
-import de.lmu.ifi.dbs.elki.utilities.pairs.IntIntPair;
-
-/**
- * Class to manage a simple Histogram.
- *
- * Note: the iterator returns pairs containing the coordinate and the bin value!
- *
- * @author Erich Schubert
- *
- * @apiviz.composedOf de.lmu.ifi.dbs.elki.math.histograms.ReplacingHistogram.Adapter
- *
- * @param <T> Histogram data type.
- */
-public class ReplacingHistogram<T> implements Iterable<DoubleObjPair<T>> {
- /**
- * Interface to plug in a data type T.
- *
- * @author Erich Schubert
- *
- * @param <T> Data type
- */
- public static abstract class Adapter<T> {
- /**
- * Construct a new T when needed.
- *
- * @return new T
- */
- public abstract T make();
- }
-
- /**
- * Array shift to account for negative indices.
- */
- protected int offset = 0;
-
- /**
- * Size of array storage.
- */
- protected int size;
-
- /**
- * Array 'base', i.e. the point of 0.0. Usually the minimum.
- */
- protected double base;
-
- /**
- * To avoid introducing an extra bucket for the maximum value.
- */
- protected double max;
-
- /**
- * Width of a bin.
- */
- protected double binsize;
-
- /**
- * Data storage
- */
- protected ArrayList<T> data;
-
- /**
- * Constructor for new elements
- */
- private Adapter<T> maker;
-
- /**
- * Histogram constructor
- *
- * @param bins Number of bins to use.
- * @param min Minimum Value
- * @param max Maximum Value
- * @param maker Constructor for new elements.
- */
- public ReplacingHistogram(int bins, double min, double max, Adapter<T> maker) {
- this.base = min;
- this.max = max;
- this.binsize = (max - min) / bins;
- this.size = bins;
- this.data = new ArrayList<T>(bins);
- this.maker = maker;
- for(int i = 0; i < bins; i++) {
- this.data.add(maker.make());
- }
- }
-
- /**
- * Histogram constructor without 'Constructor' to generate new elements. Empty
- * bins will be initialized with 'null'.
- *
- * @param bins Number of bins
- * @param min Minimum value
- * @param max Maximum value.
- */
- public ReplacingHistogram(int bins, double min, double max) {
- this(bins, min, max, null);
- }
-
- /**
- * Get the data at a given Coordinate.
- *
- * @param coord Coordinate.
- * @return data element there (which may be a new empty bin or null)
- */
- public T get(double coord) {
- int bin = getBinNr(coord);
- // compare with allocated area
- if(bin < 0) {
- T n = maker.make();
- return n;
- }
- if(bin >= size) {
- T n = maker.make();
- return n;
- }
- return data.get(bin);
- }
-
- /**
- * Put data at a given coordinate. Note: this replaces the contents, it
- * doesn't "add" or "count".
- *
- * @param coord Coordinate
- * @param d New Data
- */
- public void replace(double coord, T d) {
- int bin = getBinNr(coord);
- putBin(bin, d);
- }
-
- /**
- * Compute the bin number. Has a special case for rounding max down to the
- * last bin.
- *
- * @param coord Coordinate
- * @return bin number
- */
- protected int getBinNr(double coord) {
- if(Double.isInfinite(coord) || Double.isNaN(coord)) {
- throw new UnsupportedOperationException("Encountered non-finite value in Histogram: " + coord);
- }
- if(coord == max) {
- // System.err.println("Triggered special case: "+ (Math.floor((coord -
- // base) / binsize) + offset) + " vs. " + (size - 1));
- return size - 1;
- }
- return (int) Math.floor((coord - base) / binsize) + offset;
- }
-
- /**
- * Internal put function to handle the special cases of histogram resizing.
- *
- * @param bin bin number
- * @param d data to put
- */
- private void putBin(int bin, T d) {
- if(bin < 0) {
- // make sure to have enough space
- data.ensureCapacity(size - bin);
- // insert new data in front.
- data.add(0, d);
- // fill the gap. Note that bin < 0.
- for(int i = bin + 1; i < 0; i++) {
- data.add(1, maker.make());
- }
- // We still have bin < 0, thus (size - bin) > size!
- assert (data.size() == size - bin);
- offset = offset - bin;
- size = size - bin;
- // drop max value when resizing
- max = Double.MAX_VALUE;
- }
- else if(bin >= size) {
- this.data.ensureCapacity(bin + 1);
- while(data.size() < bin) {
- data.add(maker.make());
- }
- // add the new data.
- data.add(d);
- assert (data.size() == bin + 1);
- size = bin + 1;
- // drop max value when resizing
- max = Double.MAX_VALUE;
- }
- else {
- this.data.set(bin, d);
- }
- }
-
- /**
- * Get the number of bins actually in use.
- *
- * @return number of bins
- */
- public int getNumBins() {
- return size;
- }
-
- /**
- * Get the size (width) of a bin.
- *
- * @return bin size
- */
- public double getBinsize() {
- return binsize;
- }
-
- /**
- * Mean of bin
- *
- * @param bin Bin number
- * @return Mean
- */
- public double getBinMean(int bin) {
- return base + (bin + 0.5 - offset) * binsize;
- }
-
- /**
- * Minimum of bin
- *
- * @param bin Bin number
- * @return Lower bound
- */
- public double getBinMin(int bin) {
- return base + (bin - offset) * binsize;
- }
-
- /**
- * Maximum of bin
- *
- * @param bin Bin number
- * @return Upper bound
- */
- public double getBinMax(int bin) {
- return base + (bin + 1 - offset) * binsize;
- }
-
- /**
- * Get minimum (covered by bins, not data!)
- *
- * @return minimum
- */
- public double getCoverMinimum() {
- return base - offset * binsize;
- }
-
- /**
- * Get maximum (covered by bins, not data!)
- *
- * @return maximum
- */
- public double getCoverMaximum() {
- return base + (size - offset) * binsize;
- }
-
- /**
- * Get the raw data. Note that this does NOT include the coordinates.
- *
- * @return raw data array.
- */
- public ArrayList<T> getData() {
- return data;
- }
-
- /**
- * Make a new bin.
- *
- * @return new bin.
- */
- protected T make() {
- return maker.make();
- }
-
- /**
- * Iterator class to iterate over all bins.
- *
- * @author Erich Schubert
- *
- * @apiviz.exclude
- */
- protected class Iter implements Iterator<DoubleObjPair<T>> {
- /**
- * Current bin number
- */
- int bin = 0;
-
- @Override
- public boolean hasNext() {
- return bin < size;
- }
-
- @Override
- public DoubleObjPair<T> next() {
- DoubleObjPair<T> pair = new DoubleObjPair<T>(base + (bin + 0.5 - offset) * binsize, data.get(bin));
- bin++;
- return pair;
- }
-
- @Override
- public void remove() {
- throw new UnsupportedOperationException("Histogram iterators cannot be modified.");
- }
- }
-
- /**
- * Iterator class to iterate over all bins.
- *
- * @author Erich Schubert
- *
- * @apiviz.exclude
- */
- protected class RIter implements Iterator<DoubleObjPair<T>> {
- /**
- * Current bin number
- */
- int bin = size - 1;
-
- @Override
- public boolean hasNext() {
- return bin >= 0;
- }
-
- @Override
- public DoubleObjPair<T> next() {
- DoubleObjPair< T> pair = new DoubleObjPair<T>(base + (bin + 0.5 - offset) * binsize, data.get(bin));
- bin--;
- return pair;
- }
-
- @Override
- public void remove() {
- throw new UnsupportedOperationException("Histogram iterators cannot be modified.");
- }
- }
-
- /**
- * Get an iterator over all histogram bins.
- */
- @Override
- public Iterator<DoubleObjPair<T>> iterator() {
- return new Iter();
- }
-
- /**
- * Get an iterator over all histogram bins.
- */
- // TODO: is there some interface to implement.
- public Iterator<DoubleObjPair<T>> reverseIterator() {
- return new RIter();
- }
-
- /**
- * Convenience constructor for Integer-based Histograms. Uses a constructor to
- * initialize bins with Integer(0)
- *
- * @param bins Number of bins
- * @param min Minimum coordinate
- * @param max Maximum coordinate
- * @return New histogram for Integers.
- */
- public static ReplacingHistogram<Integer> IntHistogram(int bins, double min, double max) {
- return new ReplacingHistogram<Integer>(bins, min, max, new Adapter<Integer>() {
- @Override
- public Integer make() {
- return new Integer(0);
- }
- });
- }
-
- /**
- * Convenience constructor for Double-based Histograms. Uses a constructor to
- * initialize bins with Double(0)
- *
- * @param bins Number of bins
- * @param min Minimum coordinate
- * @param max Maximum coordinate
- * @return New histogram for Doubles.
- */
- public static ReplacingHistogram<Double> DoubleHistogram(int bins, double min, double max) {
- return new ReplacingHistogram<Double>(bins, min, max, new Adapter<Double>() {
- @Override
- public Double make() {
- return new Double(0.0);
- }
- });
- }
-
- /**
- * Convenience constructor for Histograms with pairs of Integers Uses a
- * constructor to initialize bins with Pair(Integer(0),Integer(0))
- *
- * @param bins Number of bins
- * @param min Minimum coordinate
- * @param max Maximum coordinate
- * @return New histogram for Integer pairs.
- */
- public static ReplacingHistogram<IntIntPair> IntIntHistogram(int bins, double min, double max) {
- return new ReplacingHistogram<IntIntPair>(bins, min, max, new Adapter<IntIntPair>() {
- @Override
- public IntIntPair make() {
- return new IntIntPair(0, 0);
- }
- });
- }
-
- /**
- * Convenience constructor for Histograms with pairs of Doubles Uses a
- * constructor to initialize bins with Pair(Double(0),Double(0))
- *
- * @param bins Number of bins
- * @param min Minimum coordinate
- * @param max Maximum coordinate
- * @return New histogram for Double pairs.
- */
- public static ReplacingHistogram<DoubleDoublePair> DoubleDoubleHistogram(int bins, double min, double max) {
- return new ReplacingHistogram<DoubleDoublePair>(bins, min, max, new Adapter<DoubleDoublePair>() {
- @Override
- public DoubleDoublePair make() {
- return new DoubleDoublePair(0.0, 0.0);
- }
- });
- }
-}
diff --git a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/AffineTransformation.java b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/AffineTransformation.java
index 5d185ec5..5a4f40fb 100644
--- a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/AffineTransformation.java
+++ b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/AffineTransformation.java
@@ -1,5 +1,7 @@
package de.lmu.ifi.dbs.elki.math.linearalgebra;
+import java.util.Arrays;
+
/*
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
@@ -23,7 +25,6 @@ package de.lmu.ifi.dbs.elki.math.linearalgebra;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-
/**
* Affine transformations implemented using homogeneous coordinates.
*
@@ -296,15 +297,25 @@ public class AffineTransformation {
*/
public Vector homogeneVector(Vector v) {
assert (v.getDimensionality() == dim);
- double[] dv = new double[dim + 1];
- for(int i = 0; i < dim; i++) {
- dv[i] = v.get(i);
- }
+ double[] dv = Arrays.copyOf(v.getArrayRef(), dim + 1);
dv[dim] = 1.0;
return new Vector(dv);
}
/**
+ * Transform an absolute vector into homogeneous coordinates.
+ *
+ * @param v initial vector
+ * @return vector of dim+1, with new column having the value 1.0
+ */
+ public double[] homogeneVector(double[] v) {
+ assert (v.length == dim);
+ double[] dv = Arrays.copyOf(v, dim + 1);
+ dv[dim] = 1.0;
+ return dv;
+ }
+
+ /**
* Transform a relative vector into homogeneous coordinates.
*
* @param v initial vector
@@ -313,15 +324,26 @@ public class AffineTransformation {
public Vector homogeneRelativeVector(Vector v) {
assert (v.getDimensionality() == dim);
// TODO: this only works properly when trans[dim][dim] == 1.0, right?
- double[] dv = new double[dim + 1];
- for(int i = 0; i < dim; i++) {
- dv[i] = v.get(i);
- }
+ double[] dv = Arrays.copyOf(v.getArrayRef(), dim + 1);
dv[dim] = 0.0;
return new Vector(dv);
}
/**
+ * Transform a relative vector into homogeneous coordinates.
+ *
+ * @param v initial vector
+ * @return vector of dim+1, with new column having the value 0.0
+ */
+ public double[] homogeneRelativeVector(double[] v) {
+ assert (v.length == dim);
+ // TODO: this only works properly when trans[dim][dim] == 1.0, right?
+ double[] dv = Arrays.copyOf(v, dim + 1);
+ dv[dim] = 0.0;
+ return dv;
+ }
+
+ /**
* Project an homogeneous vector back into the original space.
*
* @param v Matrix of 1 x dim+1 containing the homogeneous vector
@@ -345,11 +367,28 @@ public class AffineTransformation {
* @param v Matrix of 1 x dim+1 containing the homogeneous vector
* @return vector of dimension dim
*/
+ public double[] unhomogeneVector(double[] v) {
+ assert (v.length == dim + 1);
+ // TODO: this only works properly when trans[dim][dim] == 1.0, right?
+ double[] dv = new double[dim];
+ double scale = v[dim];
+ assert (Math.abs(scale) > 0.0);
+ for(int i = 0; i < dim; i++) {
+ dv[i] = v[i] / scale;
+ }
+ return dv;
+ }
+
+ /**
+ * Project an homogeneous vector back into the original space.
+ *
+ * @param v Matrix of 1 x dim+1 containing the homogeneous vector
+ * @return vector of dimension dim
+ */
public Vector unhomogeneRelativeVector(Vector v) {
assert (v.getDimensionality() == dim + 1);
double[] dv = new double[dim];
- double scale = v.get(dim);
- assert (Math.abs(scale) == 0.0);
+ assert (Math.abs(v.get(dim)) < Double.MIN_NORMAL);
for(int i = 0; i < dim; i++) {
dv[i] = v.get(i);
}
@@ -357,6 +396,20 @@ public class AffineTransformation {
}
/**
+ * Project an homogeneous vector back into the original space.
+ *
+ * @param v Matrix of 1 x dim+1 containing the homogeneous vector
+ * @return vector of dimension dim
+ */
+ public double[] unhomogeneRelativeVector(double[] v) {
+ assert (v.length == dim + 1);
+ double[] dv = new double[dim];
+ System.arraycopy(v, 0, dv, 0, dim);
+ assert (Math.abs(v[dim]) < Double.MIN_NORMAL);
+ return dv;
+ }
+
+ /**
* Apply the transformation onto a vector
*
* @param v vector of dimensionality dim
@@ -367,6 +420,16 @@ public class AffineTransformation {
}
/**
+ * Apply the transformation onto a vector
+ *
+ * @param v vector of dimensionality dim
+ * @return transformed vector of dimensionality dim
+ */
+ public double[] apply(double[] v) {
+ return unhomogeneVector(VMath.times(trans.elements, homogeneVector(v)));
+ }
+
+ /**
* Apply the inverse transformation onto a vector
*
* @param v vector of dimensionality dim
@@ -380,6 +443,19 @@ public class AffineTransformation {
}
/**
+ * Apply the inverse transformation onto a vector
+ *
+ * @param v vector of dimensionality dim
+ * @return transformed vector of dimensionality dim
+ */
+ public double[] applyInverse(double[] v) {
+ if(inv == null) {
+ updateInverse();
+ }
+ return unhomogeneVector(VMath.times(inv.elements, homogeneVector(v)));
+ }
+
+ /**
* Apply the transformation onto a vector
*
* @param v vector of dimensionality dim
@@ -390,6 +466,16 @@ public class AffineTransformation {
}
/**
+ * Apply the transformation onto a vector
+ *
+ * @param v vector of dimensionality dim
+ * @return transformed vector of dimensionality dim
+ */
+ public double[] applyRelative(double[] v) {
+ return unhomogeneRelativeVector(VMath.times(trans.elements, homogeneRelativeVector(v)));
+ }
+
+ /**
* Apply the inverse transformation onto a vector
*
* @param v vector of dimensionality dim
@@ -401,4 +487,16 @@ public class AffineTransformation {
}
return unhomogeneRelativeVector(inv.times(homogeneRelativeVector(v)));
}
+ /**
+ * Apply the inverse transformation onto a vector
+ *
+ * @param v vector of dimensionality dim
+ * @return transformed vector of dimensionality dim
+ */
+ public double[] applyRelativeInverse(double[] v) {
+ if(inv == null) {
+ updateInverse();
+ }
+ return unhomogeneRelativeVector(VMath.times(inv.elements, homogeneRelativeVector(v)));
+ }
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/Centroid.java b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/Centroid.java
index a9939a56..c46ae89a 100644
--- a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/Centroid.java
+++ b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/Centroid.java
@@ -27,7 +27,7 @@ import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
/**
* Class to compute the centroid of some data.
@@ -42,7 +42,7 @@ import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
*/
public class Centroid extends Vector {
/**
- * The current weight
+ * The current weight.
*/
protected double wsum;
@@ -57,14 +57,14 @@ public class Centroid extends Vector {
}
/**
- * Add a single value with weight 1.0
+ * Add a single value with weight 1.0.
*
* @param val Value
*/
public void put(double[] val) {
assert (val.length == elements.length);
wsum += 1.0;
- for(int i = 0; i < elements.length; i++) {
+ for (int i = 0; i < elements.length; i++) {
final double delta = val[i] - elements[i];
elements[i] += delta / wsum;
}
@@ -76,10 +76,13 @@ public class Centroid extends Vector {
* @param val data
* @param weight weight
*/
- public void put(double val[], double weight) {
+ public void put(double[] val, double weight) {
assert (val.length == elements.length);
+ if (weight == 0) {
+ return; // Skip zero weights.
+ }
final double nwsum = weight + wsum;
- for(int i = 0; i < elements.length; i++) {
+ for (int i = 0; i < elements.length; i++) {
final double delta = val[i] - elements[i];
final double rval = delta * weight / nwsum;
elements[i] += rval;
@@ -88,7 +91,7 @@ public class Centroid extends Vector {
}
/**
- * Add a single value with weight 1.0
+ * Add a single value with weight 1.0.
*
* @param val Value
*/
@@ -107,15 +110,15 @@ public class Centroid extends Vector {
}
/**
- * Add a single value with weight 1.0
+ * Add a single value with weight 1.0.
*
* @param val Value
*/
- public void put(NumberVector<?, ?> val) {
+ public void put(NumberVector<?> val) {
assert (val.getDimensionality() == elements.length);
wsum += 1.0;
- for(int i = 0; i < elements.length; i++) {
- final double delta = val.doubleValue(i + 1) - elements[i];
+ for (int i = 0; i < elements.length; i++) {
+ final double delta = val.doubleValue(i) - elements[i];
elements[i] += delta / wsum;
}
}
@@ -126,11 +129,14 @@ public class Centroid extends Vector {
* @param val data
* @param weight weight
*/
- public void put(NumberVector<?, ?> val, double weight) {
+ public void put(NumberVector<?> val, double weight) {
assert (val.getDimensionality() == elements.length);
+ if (weight == 0) {
+ return; // Skip zero weights.
+ }
final double nwsum = weight + wsum;
- for(int i = 0; i < elements.length; i++) {
- final double delta = val.doubleValue(i + 1) - elements[i];
+ for (int i = 0; i < elements.length; i++) {
+ final double delta = val.doubleValue(i) - elements[i];
final double rval = delta * weight / nwsum;
elements[i] += rval;
}
@@ -138,23 +144,26 @@ public class Centroid extends Vector {
}
/**
- * Get the data as vector
+ * Get the data as vector.
*
+ * @param relation Data relation
+ * @param <F> vector type
* @return the data
*/
- public <F extends NumberVector<? extends F, ?>> F toVector(Relation<? extends F> relation) {
- return DatabaseUtil.assumeVectorField(relation).getFactory().newNumberVector(elements);
+ public <F extends NumberVector<?>> F toVector(Relation<? extends F> relation) {
+ return RelationUtil.getNumberVectorFactory(relation).newNumberVector(elements);
}
/**
* Static Constructor from an existing matrix columns.
*
* @param mat Matrix to use the columns from.
+ * @return Centroid vector
*/
public static Centroid make(Matrix mat) {
Centroid c = new Centroid(mat.getRowDimensionality());
int n = mat.getColumnDimensionality();
- for(int i = 0; i < n; i++) {
+ for (int i = 0; i < n; i++) {
// TODO: avoid constructing the vector objects?
c.put(mat.getCol(i));
}
@@ -167,9 +176,9 @@ public class Centroid extends Vector {
* @param relation Relation to use
* @return Centroid of relation
*/
- public static Centroid make(Relation<? extends NumberVector<?, ?>> relation) {
- Centroid c = new Centroid(DatabaseUtil.dimensionality(relation));
- for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
+ public static Centroid make(Relation<? extends NumberVector<?>> relation) {
+ Centroid c = new Centroid(RelationUtil.dimensionality(relation));
+ for (DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
c.put(relation.get(iditer));
}
return c;
@@ -180,12 +189,13 @@ public class Centroid extends Vector {
*
* @param relation Relation to use
* @param ids IDs to use
+ * @return Centroid
*/
- public static Centroid make(Relation<? extends NumberVector<?, ?>> relation, DBIDs ids) {
- Centroid c = new Centroid(DatabaseUtil.dimensionality(relation));
- for(DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
+ public static Centroid make(Relation<? extends NumberVector<?>> relation, DBIDs ids) {
+ Centroid c = new Centroid(RelationUtil.dimensionality(relation));
+ for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
c.put(relation.get(iter));
}
return c;
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/CholeskyDecomposition.java b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/CholeskyDecomposition.java
index 5209468f..8805b18c 100644
--- a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/CholeskyDecomposition.java
+++ b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/CholeskyDecomposition.java
@@ -87,10 +87,10 @@ public class CholeskyDecomposition implements java.io.Serializable {
}
Lrowj[k] = s = (A[j][k] - s) / L[k][k];
d = d + s * s;
- isspd = isspd & (A[k][j] == A[j][k]);
+ isspd &= (A[k][j] == A[j][k]);
}
d = A[j][j] - d;
- isspd = isspd & (d > 0.0);
+ isspd &= (d > 0.0);
L[j][j] = Math.sqrt(Math.max(d, 0.0));
for(int k = j + 1; k < n; k++) {
L[j][k] = 0.0;
diff --git a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/CovarianceMatrix.java b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/CovarianceMatrix.java
index a416b917..87b7bf1d 100644
--- a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/CovarianceMatrix.java
+++ b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/CovarianceMatrix.java
@@ -27,7 +27,7 @@ import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
/**
* Class for computing covariance matrixes using stable mean and variance
@@ -39,8 +39,9 @@ import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
* easier to use APIs.
*
* For use in algorithms, it is more appropriate to use
- * {@link de.lmu.ifi.dbs.elki.math.linearalgebra.pca.StandardCovarianceMatrixBuilder StandardCovarianceMatrixBuilder}
- * since this class can be overriden with a stabilized covariance matrix builder!
+ * {@link de.lmu.ifi.dbs.elki.math.linearalgebra.pca.StandardCovarianceMatrixBuilder
+ * StandardCovarianceMatrixBuilder} since this class can be overridden with a
+ * stabilized covariance matrix builder!
*
* @author Erich Schubert
*
@@ -50,22 +51,27 @@ import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
*/
public class CovarianceMatrix {
/**
- * The means
+ * Error message reported when too little data (weight &lt;= 1) in matrix.
+ */
+ public static final String ERR_TOO_LITTLE_WEIGHT = "Too few elements (too little total weight) used to obtain a valid covariance matrix.";
+
+ /**
+ * The means.
*/
double[] mean;
/**
- * The covariance matrix
+ * The covariance matrix.
*/
double[][] elements;
/**
- * Temporary storage, to avoid reallocations
+ * Temporary storage, to avoid reallocations.
*/
double[] nmea;
/**
- * The current weight
+ * The current weight.
*/
protected double wsum;
@@ -83,7 +89,7 @@ public class CovarianceMatrix {
}
/**
- * Add a single value with weight 1.0
+ * Add a single value with weight 1.0.
*
* @param val Value
*/
@@ -120,7 +126,7 @@ public class CovarianceMatrix {
* @param val data
* @param weight weight
*/
- public void put(double val[], double weight) {
+ public void put(double[] val, double weight) {
assert (val.length == mean.length);
final double nwsum = wsum + weight;
// Compute new means
@@ -149,7 +155,7 @@ public class CovarianceMatrix {
}
/**
- * Add a single value with weight 1.0
+ * Add a single value with weight 1.0.
*
* @param val Value
*/
@@ -168,16 +174,16 @@ public class CovarianceMatrix {
}
/**
- * Add a single value with weight 1.0
+ * Add a single value with weight 1.0.
*
* @param val Value
*/
- public void put(NumberVector<?, ?> val) {
+ public void put(NumberVector<?> val) {
assert (val.getDimensionality() == mean.length);
final double nwsum = wsum + 1.0;
// Compute new means
for(int i = 0; i < mean.length; i++) {
- final double delta = val.doubleValue(i + 1) - mean[i];
+ final double delta = val.doubleValue(i) - mean[i];
nmea[i] = mean[i] + delta / nwsum;
}
// Update covariance matrix
@@ -185,7 +191,7 @@ public class CovarianceMatrix {
for(int j = i; j < mean.length; j++) {
// We DO want to use the new mean once and the old mean once!
// It does not matter which one is which.
- double delta = (val.doubleValue(i + 1) - nmea[i]) * (val.doubleValue(j + 1) - mean[j]);
+ double delta = (val.doubleValue(i) - nmea[i]) * (val.doubleValue(j) - mean[j]);
elements[i][j] = elements[i][j] + delta;
// Optimize via symmetry
if(i != j) {
@@ -204,12 +210,12 @@ public class CovarianceMatrix {
* @param val data
* @param weight weight
*/
- public void put(NumberVector<?, ?> val, double weight) {
+ public void put(NumberVector<?> val, double weight) {
assert (val.getDimensionality() == mean.length);
final double nwsum = wsum + weight;
// Compute new means
for(int i = 0; i < mean.length; i++) {
- final double delta = val.doubleValue(i + 1) - mean[i];
+ final double delta = val.doubleValue(i) - mean[i];
final double rval = delta * weight / nwsum;
nmea[i] = mean[i] + rval;
}
@@ -218,7 +224,7 @@ public class CovarianceMatrix {
for(int j = i; j < mean.length; j++) {
// We DO want to use the new mean once and the old mean once!
// It does not matter which one is which.
- double delta = (val.doubleValue(i + 1) - nmea[i]) * (val.doubleValue(j + 1) - mean[j]) * weight;
+ double delta = (val.doubleValue(i) - nmea[i]) * (val.doubleValue(j) - mean[j]) * weight;
elements[i][j] = elements[i][j] + delta;
// Optimize via symmetry
if(i != j) {
@@ -243,10 +249,12 @@ public class CovarianceMatrix {
/**
* Get the mean as vector.
*
+ * @param relation Data relation
+ * @param <F> vector type
* @return Mean vector
*/
- public <F extends NumberVector<? extends F, ?>> F getMeanVector(Relation<? extends F> relation) {
- return DatabaseUtil.assumeVectorField(relation).getFactory().newNumberVector(mean);
+ public <F extends NumberVector<?>> F getMeanVector(Relation<? extends F> relation) {
+ return RelationUtil.getNumberVectorFactory(relation).newNumberVector(mean);
}
/**
@@ -261,7 +269,7 @@ public class CovarianceMatrix {
*/
public Matrix makeSampleMatrix() {
if(wsum <= 1.0) {
- throw new IllegalStateException("Too few elements used to obtain a valid covariance matrix.");
+ throw new IllegalStateException(ERR_TOO_LITTLE_WEIGHT);
}
Matrix mat = new Matrix(elements);
return mat.times(1.0 / (wsum - 1));
@@ -279,7 +287,7 @@ public class CovarianceMatrix {
*/
public Matrix makeNaiveMatrix() {
if(wsum <= 0.0) {
- throw new IllegalStateException("Too few elements used to obtain a valid covariance matrix.");
+ throw new IllegalStateException(ERR_TOO_LITTLE_WEIGHT);
}
Matrix mat = new Matrix(elements);
return mat.times(1.0 / wsum);
@@ -297,7 +305,7 @@ public class CovarianceMatrix {
*/
public Matrix destroyToSampleMatrix() {
if(wsum <= 1.0) {
- throw new IllegalStateException("Too few elements used to obtain a valid covariance matrix.");
+ throw new IllegalStateException(ERR_TOO_LITTLE_WEIGHT);
}
Matrix mat = new Matrix(elements).timesEquals(1.0 / (wsum - 1));
this.elements = null;
@@ -316,7 +324,7 @@ public class CovarianceMatrix {
*/
public Matrix destroyToNaiveMatrix() {
if(wsum <= 0.0) {
- throw new IllegalStateException("Too few elements used to obtain a valid covariance matrix.");
+ throw new IllegalStateException(ERR_TOO_LITTLE_WEIGHT);
}
Matrix mat = new Matrix(elements).timesEquals(1.0 / wsum);
this.elements = null;
@@ -327,6 +335,7 @@ public class CovarianceMatrix {
* Static Constructor.
*
* @param mat Matrix to use the columns of
+ * @return Covariance matrix
*/
public static CovarianceMatrix make(Matrix mat) {
CovarianceMatrix c = new CovarianceMatrix(mat.getRowDimensionality());
@@ -342,9 +351,10 @@ public class CovarianceMatrix {
* Static Constructor from a full relation.
*
* @param relation Relation to use.
+ * @return Covariance matrix
*/
- public static CovarianceMatrix make(Relation<? extends NumberVector<?, ?>> relation) {
- CovarianceMatrix c = new CovarianceMatrix(DatabaseUtil.dimensionality(relation));
+ public static CovarianceMatrix make(Relation<? extends NumberVector<?>> relation) {
+ CovarianceMatrix c = new CovarianceMatrix(RelationUtil.dimensionality(relation));
for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
c.put(relation.get(iditer));
}
@@ -356,10 +366,11 @@ public class CovarianceMatrix {
*
* @param relation Relation to use.
* @param ids IDs to add
+ * @return Covariance matrix
*/
- public static CovarianceMatrix make(Relation<? extends NumberVector<?, ?>> relation, DBIDs ids) {
- CovarianceMatrix c = new CovarianceMatrix(DatabaseUtil.dimensionality(relation));
- for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
+ public static CovarianceMatrix make(Relation<? extends NumberVector<?>> relation, DBIDs ids) {
+ CovarianceMatrix c = new CovarianceMatrix(RelationUtil.dimensionality(relation));
+ for(DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
c.put(relation.get(iter));
}
return c;
diff --git a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/EigenvalueDecomposition.java b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/EigenvalueDecomposition.java
index 48ce9c7b..54434cdd 100644
--- a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/EigenvalueDecomposition.java
+++ b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/EigenvalueDecomposition.java
@@ -96,80 +96,71 @@ public class EigenvalueDecomposition implements java.io.Serializable {
// Symmetric Householder reduction to tridiagonal form.
private void tred2() {
-
// This is derived from the Algol procedures tred2 by
// Bowdler, Martin, Reinsch, and Wilkinson, Handbook for
// Auto. Comp., Vol.ii-Linear Algebra, and the corresponding
// Fortran subroutine in EISPACK.
-
- for(int j = 0; j < n; j++) {
- d[j] = V[n - 1][j];
- }
+ System.arraycopy(V[n - 1], 0, d, 0, n);
// Householder reduction to tridiagonal form.
- for(int i = n - 1; i > 0; i--) {
-
+ for (int i = n - 1; i > 0; i--) {
// Scale to avoid under/overflow.
double scale = 0.0;
double h = 0.0;
- for(int k = 0; k < i; k++) {
+ for (int k = 0; k < i; k++) {
scale = scale + Math.abs(d[k]);
}
- if(scale == 0.0) {
+ if (scale < Double.MIN_NORMAL) {
e[i] = d[i - 1];
- for(int j = 0; j < i; j++) {
+ for (int j = 0; j < i; j++) {
d[j] = V[i - 1][j];
V[i][j] = 0.0;
V[j][i] = 0.0;
}
- }
- else {
-
+ } else {
// Generate Householder vector.
-
- for(int k = 0; k < i; k++) {
+ for (int k = 0; k < i; k++) {
d[k] /= scale;
h += d[k] * d[k];
}
double f = d[i - 1];
double g = Math.sqrt(h);
- if(f > 0) {
+ if (f > 0) {
g = -g;
}
e[i] = scale * g;
h = h - f * g;
d[i - 1] = f - g;
- for(int j = 0; j < i; j++) {
+ for (int j = 0; j < i; j++) {
e[j] = 0.0;
}
// Apply similarity transformation to remaining columns.
-
- for(int j = 0; j < i; j++) {
+ for (int j = 0; j < i; j++) {
f = d[j];
V[j][i] = f;
g = e[j] + V[j][j] * f;
- for(int k = j + 1; k <= i - 1; k++) {
+ for (int k = j + 1; k <= i - 1; k++) {
g += V[k][j] * d[k];
e[k] += V[k][j] * f;
}
e[j] = g;
}
f = 0.0;
- for(int j = 0; j < i; j++) {
+ for (int j = 0; j < i; j++) {
e[j] /= h;
f += e[j] * d[j];
}
double hh = f / (h + h);
- for(int j = 0; j < i; j++) {
+ for (int j = 0; j < i; j++) {
e[j] -= hh * d[j];
}
- for(int j = 0; j < i; j++) {
+ for (int j = 0; j < i; j++) {
f = d[j];
g = e[j];
- for(int k = j; k <= i - 1; k++) {
+ for (int k = j; k <= i - 1; k++) {
V[k][j] -= (f * e[k] + g * d[k]);
}
d[j] = V[i - 1][j];
@@ -181,29 +172,29 @@ public class EigenvalueDecomposition implements java.io.Serializable {
// Accumulate transformations.
- for(int i = 0; i < n - 1; i++) {
+ for (int i = 0; i < n - 1; i++) {
V[n - 1][i] = V[i][i];
V[i][i] = 1.0;
double h = d[i + 1];
- if(h != 0.0) {
- for(int k = 0; k <= i; k++) {
+ if (h > 0.0 || h < 0.0) {
+ for (int k = 0; k <= i; k++) {
d[k] = V[k][i + 1] / h;
}
- for(int j = 0; j <= i; j++) {
+ for (int j = 0; j <= i; j++) {
double g = 0.0;
- for(int k = 0; k <= i; k++) {
+ for (int k = 0; k <= i; k++) {
g += V[k][i + 1] * V[k][j];
}
- for(int k = 0; k <= i; k++) {
+ for (int k = 0; k <= i; k++) {
V[k][j] -= g * d[k];
}
}
}
- for(int k = 0; k <= i; k++) {
+ for (int k = 0; k <= i; k++) {
V[k][i + 1] = 0.0;
}
}
- for(int j = 0; j < n; j++) {
+ for (int j = 0; j < n; j++) {
d[j] = V[n - 1][j];
V[n - 1][j] = 0.0;
}
@@ -214,28 +205,23 @@ public class EigenvalueDecomposition implements java.io.Serializable {
// Symmetric tridiagonal QL algorithm.
private void tql2() {
-
// This is derived from the Algol procedures tql2, by
// Bowdler, Martin, Reinsch, and Wilkinson, Handbook for
// Auto. Comp., Vol.ii-Linear Algebra, and the corresponding
// Fortran subroutine in EISPACK.
- for(int i = 1; i < n; i++) {
- e[i - 1] = e[i];
- }
+ System.arraycopy(e, 1, e, 0, n - 1);
e[n - 1] = 0.0;
double f = 0.0;
double tst1 = 0.0;
double eps = Math.pow(2.0, -52.0);
- for(int l = 0; l < n; l++) {
-
+ for (int l = 0; l < n; l++) {
// Find small subdiagonal element
-
tst1 = Math.max(tst1, Math.abs(d[l]) + Math.abs(e[l]));
int m = l;
- while(m < n) {
- if(Math.abs(e[m]) <= eps * tst1) {
+ while (m < n) {
+ if (Math.abs(e[m]) <= eps * tst1) {
break;
}
m++;
@@ -244,30 +230,28 @@ public class EigenvalueDecomposition implements java.io.Serializable {
// If m == l, d[l] is an eigenvalue,
// otherwise, iterate.
- if(m > l) {
+ if (m > l) {
int iter = 0;
do {
iter = iter + 1; // (Could check iteration count here.)
// Compute implicit shift
-
double g = d[l];
double p = (d[l + 1] - g) / (2.0 * e[l]);
double r = MathUtil.fastHypot(p, 1.0);
- if(p < 0) {
+ if (p < 0) {
r = -r;
}
d[l] = e[l] / (p + r);
d[l + 1] = e[l] * (p + r);
double dl1 = d[l + 1];
double h = g - d[l];
- for(int i = l + 2; i < n; i++) {
+ for (int i = l + 2; i < n; i++) {
d[i] -= h;
}
f = f + h;
// Implicit QL transformation.
-
p = d[m];
double c = 1.0;
double c2 = c;
@@ -275,7 +259,7 @@ public class EigenvalueDecomposition implements java.io.Serializable {
double el1 = e[l + 1];
double s = 0.0;
double s2 = 0.0;
- for(int i = m - 1; i >= l; i--) {
+ for (int i = m - 1; i >= l; i--) {
c3 = c2;
c2 = c;
s2 = s;
@@ -289,8 +273,7 @@ public class EigenvalueDecomposition implements java.io.Serializable {
d[i + 1] = h + s * (c * g + s * d[i]);
// Accumulate transformation.
-
- for(int k = 0; k < n; k++) {
+ for (int k = 0; k < n; k++) {
h = V[k][i + 1];
V[k][i + 1] = s * V[k][i] + c * h;
V[k][i] = c * V[k][i] - s * h;
@@ -301,30 +284,27 @@ public class EigenvalueDecomposition implements java.io.Serializable {
d[l] = c * p;
// Check for convergence.
-
}
- while(Math.abs(e[l]) > eps * tst1);
+ while (Math.abs(e[l]) > eps * tst1);
}
d[l] = d[l] + f;
e[l] = 0.0;
-
}
// Sort eigenvalues and corresponding vectors.
-
- for(int i = 0; i < n - 1; i++) {
+ for (int i = 0; i < n - 1; i++) {
int k = i;
double p = d[i];
- for(int j = i + 1; j < n; j++) {
- if(d[j] < p) {
+ for (int j = i + 1; j < n; j++) {
+ if (d[j] < p) {
k = j;
p = d[j];
}
}
- if(k != i) {
+ if (k != i) {
d[k] = d[i];
d[i] = p;
- for(int j = 0; j < n; j++) {
+ for (int j = 0; j < n; j++) {
p = V[j][i];
V[j][i] = V[j][k];
V[j][k] = p;
@@ -346,25 +326,25 @@ public class EigenvalueDecomposition implements java.io.Serializable {
int low = 0;
int high = n - 1;
- for(int m = low + 1; m <= high - 1; m++) {
+ for (int m = low + 1; m <= high - 1; m++) {
// Scale column.
double scale = 0.0;
- for(int i = m; i <= high; i++) {
+ for (int i = m; i <= high; i++) {
scale = scale + Math.abs(H[i][m - 1]);
}
- if(scale != 0.0) {
+ if (scale > 0.0 || scale < 0.0) {
// Compute Householder transformation.
double h = 0.0;
- for(int i = high; i >= m; i--) {
+ for (int i = high; i >= m; i--) {
ort[i] = H[i][m - 1] / scale;
h += ort[i] * ort[i];
}
double g = Math.sqrt(h);
- if(ort[m] > 0) {
+ if (ort[m] > 0) {
g = -g;
}
h = h - ort[m] * g;
@@ -373,24 +353,24 @@ public class EigenvalueDecomposition implements java.io.Serializable {
// Apply Householder similarity transformation
// H = (I-u*u'/h)*H*(I-u*u')/h)
- for(int j = m; j < n; j++) {
+ for (int j = m; j < n; j++) {
double f = 0.0;
- for(int i = high; i >= m; i--) {
+ for (int i = high; i >= m; i--) {
f += ort[i] * H[i][j];
}
f = f / h;
- for(int i = m; i <= high; i++) {
+ for (int i = m; i <= high; i++) {
H[i][j] -= f * ort[i];
}
}
- for(int i = 0; i <= high; i++) {
+ for (int i = 0; i <= high; i++) {
double f = 0.0;
- for(int j = high; j >= m; j--) {
+ for (int j = high; j >= m; j--) {
f += ort[j] * H[i][j];
}
f = f / h;
- for(int j = m; j <= high; j++) {
+ for (int j = m; j <= high; j++) {
H[i][j] -= f * ort[j];
}
}
@@ -401,25 +381,25 @@ public class EigenvalueDecomposition implements java.io.Serializable {
// Accumulate transformations (Algol's ortran).
- for(int i = 0; i < n; i++) {
- for(int j = 0; j < n; j++) {
+ for (int i = 0; i < n; i++) {
+ for (int j = 0; j < n; j++) {
V[i][j] = (i == j ? 1.0 : 0.0);
}
}
- for(int m = high - 1; m >= low + 1; m--) {
- if(H[m][m - 1] != 0.0) {
- for(int i = m + 1; i <= high; i++) {
+ for (int m = high - 1; m >= low + 1; m--) {
+ if (H[m][m - 1] != 0.0) {
+ for (int i = m + 1; i <= high; i++) {
ort[i] = H[i][m - 1];
}
- for(int j = m; j <= high; j++) {
+ for (int j = m; j <= high; j++) {
double g = 0.0;
- for(int i = m; i <= high; i++) {
+ for (int i = m; i <= high; i++) {
g += ort[i] * V[i][j];
}
// Double division avoids possible underflow
g = (g / ort[m]) / H[m][m - 1];
- for(int i = m; i <= high; i++) {
+ for (int i = m; i <= high; i++) {
V[i][j] += g * ort[i];
}
}
@@ -433,13 +413,12 @@ public class EigenvalueDecomposition implements java.io.Serializable {
private void cdiv(double xr, double xi, double yr, double yi) {
double r, d;
- if(Math.abs(yr) > Math.abs(yi)) {
+ if (Math.abs(yr) > Math.abs(yi)) {
r = yi / yr;
d = yr + r * yi;
cdivr = (xr + r * xi) / d;
cdivi = (xi - r * xr) / d;
- }
- else {
+ } else {
r = yr / yi;
d = yi + r * yr;
cdivr = (r * xr + xi) / d;
@@ -470,12 +449,12 @@ public class EigenvalueDecomposition implements java.io.Serializable {
// Store roots isolated by balanc and compute matrix norm
double norm = 0.0;
- for(int i = 0; i < nn; i++) {
- if(i < low | i > high) {
+ for (int i = 0; i < nn; i++) {
+ if (i < low || i > high) {
d[i] = H[i][i];
e[i] = 0.0;
}
- for(int j = Math.max(i - 1, 0); j < nn; j++) {
+ for (int j = Math.max(i - 1, 0); j < nn; j++) {
norm = norm + Math.abs(H[i][j]);
}
}
@@ -483,17 +462,17 @@ public class EigenvalueDecomposition implements java.io.Serializable {
// Outer loop over eigenvalue index
int iter = 0;
- while(n >= low) {
+ while (n >= low) {
// Look for single small sub-diagonal element
int l = n;
- while(l > low) {
+ while (l > low) {
s = Math.abs(H[l - 1][l - 1]) + Math.abs(H[l][l]);
- if(s == 0.0) {
+ if (s == 0.0) {
s = norm;
}
- if(Math.abs(H[l][l - 1]) < eps * s) {
+ if (Math.abs(H[l][l - 1]) < eps * s) {
break;
}
l--;
@@ -502,7 +481,7 @@ public class EigenvalueDecomposition implements java.io.Serializable {
// Check for convergence
// One root found
- if(l == n) {
+ if (l == n) {
H[n][n] = H[n][n] + exshift;
d[n] = H[n][n];
e[n] = 0.0;
@@ -511,8 +490,7 @@ public class EigenvalueDecomposition implements java.io.Serializable {
// Two roots found
- }
- else if(l == n - 1) {
+ } else if (l == n - 1) {
w = H[n][n - 1] * H[n - 1][n];
p = (H[n - 1][n - 1] - H[n][n]) / 2.0;
q = p * p + w;
@@ -523,16 +501,15 @@ public class EigenvalueDecomposition implements java.io.Serializable {
// Real pair
- if(q >= 0) {
- if(p >= 0) {
+ if (q >= 0) {
+ if (p >= 0) {
z = p + z;
- }
- else {
+ } else {
z = p - z;
}
d[n - 1] = x + z;
d[n] = d[n - 1];
- if(z != 0.0) {
+ if (z != 0.0) {
d[n] = x - w / z;
}
e[n - 1] = 0.0;
@@ -547,7 +524,7 @@ public class EigenvalueDecomposition implements java.io.Serializable {
// Row modification
- for(int j = n - 1; j < nn; j++) {
+ for (int j = n - 1; j < nn; j++) {
z = H[n - 1][j];
H[n - 1][j] = q * z + p * H[n][j];
H[n][j] = q * H[n][j] - p * z;
@@ -555,7 +532,7 @@ public class EigenvalueDecomposition implements java.io.Serializable {
// Column modification
- for(int i = 0; i <= n; i++) {
+ for (int i = 0; i <= n; i++) {
z = H[i][n - 1];
H[i][n - 1] = q * z + p * H[i][n];
H[i][n] = q * H[i][n] - p * z;
@@ -563,7 +540,7 @@ public class EigenvalueDecomposition implements java.io.Serializable {
// Accumulate transformations
- for(int i = low; i <= high; i++) {
+ for (int i = low; i <= high; i++) {
z = V[i][n - 1];
V[i][n - 1] = q * z + p * V[i][n];
V[i][n] = q * V[i][n] - p * z;
@@ -571,8 +548,7 @@ public class EigenvalueDecomposition implements java.io.Serializable {
// Complex pair
- }
- else {
+ } else {
d[n - 1] = x + p;
d[n] = x + p;
e[n - 1] = z;
@@ -583,24 +559,23 @@ public class EigenvalueDecomposition implements java.io.Serializable {
// No convergence yet
- }
- else {
+ } else {
// Form shift
x = H[n][n];
y = 0.0;
w = 0.0;
- if(l < n) {
+ if (l < n) {
y = H[n - 1][n - 1];
w = H[n][n - 1] * H[n - 1][n];
}
// Wilkinson's original ad hoc shift
- if(iter == 10) {
+ if (iter == 10) {
exshift += x;
- for(int i = low; i <= n; i++) {
+ for (int i = low; i <= n; i++) {
H[i][i] -= x;
}
s = Math.abs(H[n][n - 1]) + Math.abs(H[n - 1][n - 2]);
@@ -610,16 +585,16 @@ public class EigenvalueDecomposition implements java.io.Serializable {
// MATLAB's new ad hoc shift
- if(iter == 30) {
+ if (iter == 30) {
s = (y - x) / 2.0;
s = s * s + w;
- if(s > 0) {
+ if (s > 0) {
s = Math.sqrt(s);
- if(y < x) {
+ if (y < x) {
s = -s;
}
s = x - w / ((y - x) / 2.0 + s);
- for(int i = low; i <= n; i++) {
+ for (int i = low; i <= n; i++) {
H[i][i] -= s;
}
exshift += s;
@@ -632,7 +607,7 @@ public class EigenvalueDecomposition implements java.io.Serializable {
// Look for two consecutive small sub-diagonal elements
int m = n - 2;
- while(m >= l) {
+ while (m >= l) {
z = H[m][m];
r = x - z;
s = y - z;
@@ -643,49 +618,48 @@ public class EigenvalueDecomposition implements java.io.Serializable {
p = p / s;
q = q / s;
r = r / s;
- if(m == l) {
+ if (m == l) {
break;
}
- if(Math.abs(H[m][m - 1]) * (Math.abs(q) + Math.abs(r)) < eps * (Math.abs(p) * (Math.abs(H[m - 1][m - 1]) + Math.abs(z) + Math.abs(H[m + 1][m + 1])))) {
+ if (Math.abs(H[m][m - 1]) * (Math.abs(q) + Math.abs(r)) < eps * (Math.abs(p) * (Math.abs(H[m - 1][m - 1]) + Math.abs(z) + Math.abs(H[m + 1][m + 1])))) {
break;
}
m--;
}
- for(int i = m + 2; i <= n; i++) {
+ for (int i = m + 2; i <= n; i++) {
H[i][i - 2] = 0.0;
- if(i > m + 2) {
+ if (i > m + 2) {
H[i][i - 3] = 0.0;
}
}
// Double QR step involving rows l:n and columns m:n
- for(int k = m; k <= n - 1; k++) {
+ for (int k = m; k <= n - 1; k++) {
boolean notlast = (k != n - 1);
- if(k != m) {
+ if (k != m) {
p = H[k][k - 1];
q = H[k + 1][k - 1];
r = (notlast ? H[k + 2][k - 1] : 0.0);
x = Math.abs(p) + Math.abs(q) + Math.abs(r);
- if(x != 0.0) {
+ if (x != 0.0) {
p = p / x;
q = q / x;
r = r / x;
}
}
- if(x == 0.0) {
+ if (x == 0.0) {
break;
}
s = Math.sqrt(p * p + q * q + r * r);
- if(p < 0) {
+ if (p < 0) {
s = -s;
}
- if(s != 0) {
- if(k != m) {
+ if (s != 0) {
+ if (k != m) {
H[k][k - 1] = -s * x;
- }
- else if(l != m) {
+ } else if (l != m) {
H[k][k - 1] = -H[k][k - 1];
}
p = p + s;
@@ -697,9 +671,9 @@ public class EigenvalueDecomposition implements java.io.Serializable {
// Row modification
- for(int j = k; j < nn; j++) {
+ for (int j = k; j < nn; j++) {
p = H[k][j] + q * H[k + 1][j];
- if(notlast) {
+ if (notlast) {
p = p + r * H[k + 2][j];
H[k + 2][j] = H[k + 2][j] - p * z;
}
@@ -709,9 +683,9 @@ public class EigenvalueDecomposition implements java.io.Serializable {
// Column modification
- for(int i = 0; i <= Math.min(n, k + 3); i++) {
+ for (int i = 0; i <= Math.min(n, k + 3); i++) {
p = x * H[i][k] + y * H[i][k + 1];
- if(notlast) {
+ if (notlast) {
p = p + z * H[i][k + 2];
H[i][k + 2] = H[i][k + 2] - p * r;
}
@@ -721,9 +695,9 @@ public class EigenvalueDecomposition implements java.io.Serializable {
// Accumulate transformations
- for(int i = low; i <= high; i++) {
+ for (int i = low; i <= high; i++) {
p = x * V[i][k] + y * V[i][k + 1];
- if(notlast) {
+ if (notlast) {
p = p + z * V[i][k + 2];
V[i][k + 2] = V[i][k + 2] - p * r;
}
@@ -737,127 +711,108 @@ public class EigenvalueDecomposition implements java.io.Serializable {
// Backsubstitute to find vectors of upper triangular form
- if(norm == 0.0) {
+ if (norm == 0.0) {
return;
}
- for(n = nn - 1; n >= 0; n--) {
+ for (n = nn - 1; n >= 0; n--) {
p = d[n];
q = e[n];
// Real vector
- if(q == 0) {
+ if (q == 0) {
int l = n;
H[n][n] = 1.0;
- for(int i = n - 1; i >= 0; i--) {
+ for (int i = n - 1; i >= 0; i--) {
w = H[i][i] - p;
r = 0.0;
- for(int j = l; j <= n; j++) {
+ for (int j = l; j <= n; j++) {
r = r + H[i][j] * H[j][n];
}
- if(e[i] < 0.0) {
+ if (e[i] < 0.0) {
z = w;
s = r;
- }
- else {
+ } else {
l = i;
- if(e[i] == 0.0) {
- if(w != 0.0) {
+ if (!(e[i] > 0.0)) {
+ if (w > 0.0 || w < 0.0) {
H[i][n] = -r / w;
- }
- else {
+ } else {
H[i][n] = -r / (eps * norm);
}
-
// Solve real equations
-
- }
- else {
+ } else {
x = H[i][i + 1];
y = H[i + 1][i];
q = (d[i] - p) * (d[i] - p) + e[i] * e[i];
t = (x * s - z * r) / q;
H[i][n] = t;
- if(Math.abs(x) > Math.abs(z)) {
+ if (Math.abs(x) > Math.abs(z)) {
H[i + 1][n] = (-r - w * t) / x;
- }
- else {
+ } else {
H[i + 1][n] = (-s - y * t) / z;
}
}
// Overflow control
-
t = Math.abs(H[i][n]);
- if((eps * t) * t > 1) {
- for(int j = i; j <= n; j++) {
+ if ((eps * t) * t > 1) {
+ for (int j = i; j <= n; j++) {
H[j][n] = H[j][n] / t;
}
}
}
}
-
// Complex vector
-
- }
- else if(q < 0) {
+ } else if (q < 0) {
int l = n - 1;
// Last vector component imaginary so matrix is triangular
-
- if(Math.abs(H[n][n - 1]) > Math.abs(H[n - 1][n])) {
+ if (Math.abs(H[n][n - 1]) > Math.abs(H[n - 1][n])) {
H[n - 1][n - 1] = q / H[n][n - 1];
H[n - 1][n] = -(H[n][n] - p) / H[n][n - 1];
- }
- else {
+ } else {
cdiv(0.0, -H[n - 1][n], H[n - 1][n - 1] - p, q);
H[n - 1][n - 1] = cdivr;
H[n - 1][n] = cdivi;
}
H[n][n - 1] = 0.0;
H[n][n] = 1.0;
- for(int i = n - 2; i >= 0; i--) {
- double ra, sa, vr, vi;
- ra = 0.0;
- sa = 0.0;
- for(int j = l; j <= n; j++) {
+ for (int i = n - 2; i >= 0; i--) {
+ double ra = 0.0, sa = 0.0, vr, vi;
+ for (int j = l; j <= n; j++) {
ra = ra + H[i][j] * H[j][n - 1];
sa = sa + H[i][j] * H[j][n];
}
w = H[i][i] - p;
- if(e[i] < 0.0) {
+ if (e[i] < 0.0) {
z = w;
r = ra;
s = sa;
- }
- else {
+ } else {
l = i;
- if(e[i] == 0) {
+ if (!(e[i] > 0.0)) {
cdiv(-ra, -sa, w, q);
H[i][n - 1] = cdivr;
H[i][n] = cdivi;
- }
- else {
-
+ } else {
// Solve complex equations
-
x = H[i][i + 1];
y = H[i + 1][i];
vr = (d[i] - p) * (d[i] - p) + e[i] * e[i] - q * q;
vi = (d[i] - p) * 2.0 * q;
- if(vr == 0.0 & vi == 0.0) {
+ if (vr == 0.0 && vi == 0.0) {
vr = eps * norm * (Math.abs(w) + Math.abs(q) + Math.abs(x) + Math.abs(y) + Math.abs(z));
}
cdiv(x * r - z * ra + q * sa, x * s - z * sa - q * ra, vr, vi);
H[i][n - 1] = cdivr;
H[i][n] = cdivi;
- if(Math.abs(x) > (Math.abs(z) + Math.abs(q))) {
+ if (Math.abs(x) > (Math.abs(z) + Math.abs(q))) {
H[i + 1][n - 1] = (-ra - w * H[i][n - 1] + q * H[i][n]) / x;
H[i + 1][n] = (-sa - w * H[i][n] - q * H[i][n - 1]) / x;
- }
- else {
+ } else {
cdiv(-r - y * H[i][n - 1], -s - y * H[i][n], z, q);
H[i + 1][n - 1] = cdivr;
H[i + 1][n] = cdivi;
@@ -865,10 +820,9 @@ public class EigenvalueDecomposition implements java.io.Serializable {
}
// Overflow control
-
t = Math.max(Math.abs(H[i][n - 1]), Math.abs(H[i][n]));
- if((eps * t) * t > 1) {
- for(int j = i; j <= n; j++) {
+ if ((eps * t) * t > 1) {
+ for (int j = i; j <= n; j++) {
H[j][n - 1] = H[j][n - 1] / t;
H[j][n] = H[j][n] / t;
}
@@ -879,21 +833,17 @@ public class EigenvalueDecomposition implements java.io.Serializable {
}
// Vectors of isolated roots
-
- for(int i = 0; i < nn; i++) {
- if(i < low | i > high) {
- for(int j = i; j < nn; j++) {
- V[i][j] = H[i][j];
- }
+ for (int i = 0; i < nn; i++) {
+ if (i < low || i > high) {
+ System.arraycopy(H[i], i, V[i], i, nn - i);
}
}
// Back transformation to get eigenvectors of original matrix
-
- for(int j = nn - 1; j >= low; j--) {
- for(int i = low; i <= high; i++) {
+ for (int j = nn - 1; j >= low; j--) {
+ for (int i = low; i <= high; i++) {
z = 0.0;
- for(int k = low; k <= Math.min(j, high); k++) {
+ for (int k = low; k <= Math.min(j, high); k++) {
z = z + V[i][k] * H[k][j];
}
V[i][j] = z;
@@ -919,23 +869,21 @@ public class EigenvalueDecomposition implements java.io.Serializable {
e = new double[n];
issymmetric = true;
- for(int j = 0; (j < n) & issymmetric; j++) {
- for(int i = 0; (i < n) & issymmetric; i++) {
+ for (int j = 0; (j < n) && issymmetric; j++) {
+ for (int i = 0; (i < n) && issymmetric; i++) {
issymmetric = (A[i][j] == A[j][i]);
- if(Double.isNaN(A[i][j])) {
+ if (Double.isNaN(A[i][j])) {
throw new IllegalArgumentException("NaN in EigenvalueDecomposition!");
}
- if(Double.isInfinite(A[i][j])) {
+ if (Double.isInfinite(A[i][j])) {
throw new IllegalArgumentException("+-inf in EigenvalueDecomposition!");
}
}
}
- if(issymmetric) {
- for(int i = 0; i < n; i++) {
- for(int j = 0; j < n; j++) {
- V[i][j] = A[i][j];
- }
+ if (issymmetric) {
+ for (int i = 0; i < n; i++) {
+ System.arraycopy(A[i], 0, V[i], 0, n);
}
// Tridiagonalize.
@@ -944,13 +892,12 @@ public class EigenvalueDecomposition implements java.io.Serializable {
// Diagonalize.
tql2();
- }
- else {
+ } else {
H = new double[n][n];
ort = new double[n];
- for(int j = 0; j < n; j++) {
- for(int i = 0; i < n; i++) {
+ for (int j = 0; j < n; j++) {
+ for (int i = 0; i < n; i++) {
H[i][j] = A[i][j];
}
}
@@ -1002,18 +949,17 @@ public class EigenvalueDecomposition implements java.io.Serializable {
public Matrix getD() {
Matrix X = new Matrix(n, n);
double[][] D = X.getArrayRef();
- for(int i = 0; i < n; i++) {
- for(int j = 0; j < n; j++) {
+ for (int i = 0; i < n; i++) {
+ for (int j = 0; j < n; j++) {
D[i][j] = 0.0;
}
D[i][i] = d[i];
- if(e[i] > 0) {
+ if (e[i] > 0) {
D[i][i + 1] = e[i];
- }
- else if(e[i] < 0) {
+ } else if (e[i] < 0) {
D[i][i - 1] = e[i];
}
}
return X;
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/LUDecomposition.java b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/LUDecomposition.java
index 08634279..6e68352a 100644
--- a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/LUDecomposition.java
+++ b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/LUDecomposition.java
@@ -1,6 +1,5 @@
package de.lmu.ifi.dbs.elki.math.linearalgebra;
-
/*
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
@@ -94,7 +93,7 @@ public class LUDecomposition implements java.io.Serializable {
this.n = n;
// Use a "left-looking", dot-product, Crout/Doolittle algorithm.
piv = new int[m];
- for(int i = 0; i < m; i++) {
+ for (int i = 0; i < m; i++) {
piv[i] = i;
}
pivsign = 1;
@@ -103,23 +102,23 @@ public class LUDecomposition implements java.io.Serializable {
// Outer loop.
- for(int j = 0; j < n; j++) {
+ for (int j = 0; j < n; j++) {
// Make a copy of the j-th column to localize references.
- for(int i = 0; i < m; i++) {
+ for (int i = 0; i < m; i++) {
LUcolj[i] = LU[i][j];
}
// Apply previous transformations.
- for(int i = 0; i < m; i++) {
+ for (int i = 0; i < m; i++) {
LUrowi = LU[i];
// Most of the time is spent in the following dot product.
int kmax = Math.min(i, j);
double s = 0.0;
- for(int k = 0; k < kmax; k++) {
+ for (int k = 0; k < kmax; k++) {
s += LUrowi[k] * LUcolj[k];
}
@@ -129,13 +128,13 @@ public class LUDecomposition implements java.io.Serializable {
// Find pivot and exchange if necessary.
int p = j;
- for(int i = j + 1; i < m; i++) {
- if(Math.abs(LUcolj[i]) > Math.abs(LUcolj[p])) {
+ for (int i = j + 1; i < m; i++) {
+ if (Math.abs(LUcolj[i]) > Math.abs(LUcolj[p])) {
p = i;
}
}
- if(p != j) {
- for(int k = 0; k < n; k++) {
+ if (p != j) {
+ for (int k = 0; k < n; k++) {
double t = LU[p][k];
LU[p][k] = LU[j][k];
LU[j][k] = t;
@@ -148,8 +147,8 @@ public class LUDecomposition implements java.io.Serializable {
// Compute multipliers.
- if(j < m & LU[j][j] != 0.0) {
- for(int i = j + 1; i < m; i++) {
+ if (j < m && LU[j][j] != 0.0) {
+ for (int i = j + 1; i < m; i++) {
LU[i][j] /= LU[j][j];
}
}
@@ -166,8 +165,8 @@ public class LUDecomposition implements java.io.Serializable {
* @return true if U, and hence A, is nonsingular.
*/
public boolean isNonsingular() {
- for(int j = 0; j < n; j++) {
- if(LU[j][j] == 0) {
+ for (int j = 0; j < n; j++) {
+ if (LU[j][j] == 0) {
return false;
}
}
@@ -182,15 +181,13 @@ public class LUDecomposition implements java.io.Serializable {
public Matrix getL() {
Matrix X = new Matrix(m, n);
double[][] L = X.getArrayRef();
- for(int i = 0; i < m; i++) {
- for(int j = 0; j < n; j++) {
- if(i > j) {
+ for (int i = 0; i < m; i++) {
+ for (int j = 0; j < n; j++) {
+ if (i > j) {
L[i][j] = LU[i][j];
- }
- else if(i == j) {
+ } else if (i == j) {
L[i][j] = 1.0;
- }
- else {
+ } else {
L[i][j] = 0.0;
}
}
@@ -206,12 +203,11 @@ public class LUDecomposition implements java.io.Serializable {
public Matrix getU() {
Matrix X = new Matrix(n, n);
double[][] U = X.getArrayRef();
- for(int i = 0; i < n; i++) {
- for(int j = 0; j < n; j++) {
- if(i <= j) {
+ for (int i = 0; i < n; i++) {
+ for (int j = 0; j < n; j++) {
+ if (i <= j) {
U[i][j] = LU[i][j];
- }
- else {
+ } else {
U[i][j] = 0.0;
}
}
@@ -226,9 +222,7 @@ public class LUDecomposition implements java.io.Serializable {
*/
public int[] getPivot() {
int[] p = new int[m];
- for(int i = 0; i < m; i++) {
- p[i] = piv[i];
- }
+ System.arraycopy(piv, 0, p, 0, m);
return p;
}
@@ -239,8 +233,8 @@ public class LUDecomposition implements java.io.Serializable {
*/
public double[] getDoublePivot() {
double[] vals = new double[m];
- for(int i = 0; i < m; i++) {
- vals[i] = piv[i];
+ for (int i = 0; i < m; i++) {
+ vals[i] = (double) piv[i];
}
return vals;
}
@@ -252,11 +246,11 @@ public class LUDecomposition implements java.io.Serializable {
* @exception IllegalArgumentException Matrix must be square
*/
public double det() {
- if(m != n) {
+ if (m != n) {
throw new IllegalArgumentException("Matrix must be square.");
}
double d = pivsign;
- for(int j = 0; j < n; j++) {
+ for (int j = 0; j < n; j++) {
d *= LU[j][j];
}
return d;
@@ -271,10 +265,10 @@ public class LUDecomposition implements java.io.Serializable {
* @exception RuntimeException Matrix is singular.
*/
public Matrix solve(Matrix B) {
- if(B.getRowDimensionality() != m) {
+ if (B.getRowDimensionality() != m) {
throw new IllegalArgumentException("Matrix row dimensions must agree.");
}
- if(!this.isNonsingular()) {
+ if (!this.isNonsingular()) {
throw new RuntimeException("Matrix is singular.");
}
@@ -298,10 +292,10 @@ public class LUDecomposition implements java.io.Serializable {
public double[][] solve(double[][] B) {
int mx = B.length;
int nx = B[0].length;
- if(mx != m) {
+ if (mx != m) {
throw new IllegalArgumentException("Matrix row dimensions must agree.");
}
- if(!this.isNonsingular()) {
+ if (!this.isNonsingular()) {
throw new RuntimeException("Matrix is singular.");
}
double[][] Xmat = new Matrix(B).getMatrix(piv, 0, nx - 1).getArrayRef();
@@ -317,23 +311,23 @@ public class LUDecomposition implements java.io.Serializable {
*/
private void solveInplace(double[][] B, int nx) {
// Solve L*Y = B(piv,:)
- for(int k = 0; k < n; k++) {
- for(int i = k + 1; i < n; i++) {
- for(int j = 0; j < nx; j++) {
+ for (int k = 0; k < n; k++) {
+ for (int i = k + 1; i < n; i++) {
+ for (int j = 0; j < nx; j++) {
B[i][j] -= B[k][j] * LU[i][k];
}
}
}
// Solve U*X = Y;
- for(int k = n - 1; k >= 0; k--) {
- for(int j = 0; j < nx; j++) {
+ for (int k = n - 1; k >= 0; k--) {
+ for (int j = 0; j < nx; j++) {
B[k][j] /= LU[k][k];
}
- for(int i = 0; i < k; i++) {
- for(int j = 0; j < nx; j++) {
+ for (int i = 0; i < k; i++) {
+ for (int j = 0; j < nx; j++) {
B[i][j] -= B[k][j] * LU[i][k];
}
}
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/LinearEquationSystem.java b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/LinearEquationSystem.java
index 80cbe1e6..53954c08 100644
--- a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/LinearEquationSystem.java
+++ b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/LinearEquationSystem.java
@@ -23,12 +23,11 @@ package de.lmu.ifi.dbs.elki.math.linearalgebra;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+import gnu.trove.list.array.TIntArrayList;
+
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
import java.util.Locale;
import de.lmu.ifi.dbs.elki.logging.Logging;
@@ -44,7 +43,7 @@ public class LinearEquationSystem {
/**
* Logger.
*/
- private static final Logging logger = Logging.getLogger(LinearEquationSystem.class);
+ private static final Logging LOG = Logging.getLogger(LinearEquationSystem.class);
/**
* Indicates trivial pivot search strategy.
@@ -114,24 +113,24 @@ public class LinearEquationSystem {
* @param b the right hand side of the linear equation system
*/
public LinearEquationSystem(double[][] a, double[] b) {
- if(a == null) {
+ if (a == null) {
throw new IllegalArgumentException("Coefficient array is null!");
}
- if(b == null) {
+ if (b == null) {
throw new IllegalArgumentException("Right hand side is null!");
}
- if(a.length != b.length) {
+ if (a.length != b.length) {
throw new IllegalArgumentException("Coefficient matrix and right hand side " + "differ in row dimensionality!");
}
coeff = a;
rhs = b;
row = new int[coeff.length];
- for(int i = 0; i < coeff.length; i++) {
+ for (int i = 0; i < coeff.length; i++) {
row[i] = i;
}
col = new int[coeff[0].length];
- for(int j = 0; j < coeff[0].length; j++) {
+ for (int j = 0; j < coeff[0].length; j++) {
col[j] = j;
}
rank = 0;
@@ -152,19 +151,19 @@ public class LinearEquationSystem {
* column[i]
*/
public LinearEquationSystem(double[][] a, double[] b, int[] rowPermutations, int[] columnPermutations) {
- if(a == null) {
+ if (a == null) {
throw new IllegalArgumentException("Coefficient array is null!");
}
- if(b == null) {
+ if (b == null) {
throw new IllegalArgumentException("Right hand side is null!");
}
- if(a.length != b.length) {
+ if (a.length != b.length) {
throw new IllegalArgumentException("Coefficient matrix and right hand side " + "differ in row dimensionality!");
}
- if(rowPermutations.length != a.length) {
+ if (rowPermutations.length != a.length) {
throw new IllegalArgumentException("Coefficient matrix and row permutation array " + "differ in row dimensionality!");
}
- if(columnPermutations.length != a[0].length) {
+ if (columnPermutations.length != a[0].length) {
throw new IllegalArgumentException("Coefficient matrix and column permutation array " + "differ in column dimensionality!");
}
@@ -235,7 +234,7 @@ public class LinearEquationSystem {
* ( 0 ... a_ii &nbsp;&nbsp;&nbsp; ... a_in )<br>
* ( 0 ... a_(i+1)i ... a_(i+1)n ) <br>
* ( 0 ... a_ni &nbsp;&nbsp;&nbsp; ... a_nn ) <br>
- * </code> Then we search for x,y in {i,...n}, so that |a_xy| > |a_ij|
+ * </code> Then we search for x,y in {i,...n}, so that |a_xy| > |a_ij|
*/
public void solveByTotalPivotSearch() {
solve(TOTAL_PIVOT_SEARCH);
@@ -284,28 +283,27 @@ public class LinearEquationSystem {
* @return a string representation of this equation system
*/
public String equationsToString(String prefix, NumberFormat nf) {
- if((coeff == null) || (rhs == null) || (row == null) || (col == null)) {
+ if ((coeff == null) || (rhs == null) || (row == null) || (col == null)) {
throw new NullPointerException();
}
int[] coeffDigits = maxIntegerDigits(coeff);
int rhsDigits = maxIntegerDigits(rhs);
- StringBuffer buffer = new StringBuffer();
- buffer.append(prefix).append("\n").append(prefix);
- for(int i = 0; i < coeff.length; i++) {
- for(int j = 0; j < coeff[row[0]].length; j++) {
+ StringBuilder buffer = new StringBuilder();
+ buffer.append(prefix).append('\n').append(prefix);
+ for (int i = 0; i < coeff.length; i++) {
+ for (int j = 0; j < coeff[row[0]].length; j++) {
format(nf, buffer, coeff[row[i]][col[j]], coeffDigits[col[j]]);
- buffer.append(" * x_" + col[j]);
+ buffer.append(" * x_").append(col[j]);
}
buffer.append(" =");
format(nf, buffer, rhs[row[i]], rhsDigits);
- if(i < coeff.length - 1) {
- buffer.append("\n").append(prefix);
- }
- else {
- buffer.append("\n").append(prefix);
+ if (i < coeff.length - 1) {
+ buffer.append('\n').append(prefix);
+ } else {
+ buffer.append('\n').append(prefix);
}
}
return buffer.toString();
@@ -339,7 +337,7 @@ public class LinearEquationSystem {
* @return a string representation of the solution of this equation system
*/
public String solutionToString(int fractionDigits) {
- if(!isSolvable()) {
+ if (!isSolvable()) {
throw new IllegalStateException("System is not solvable!");
}
@@ -350,29 +348,28 @@ public class LinearEquationSystem {
nf.setNegativePrefix("");
nf.setPositivePrefix("");
- int row = coeff[0].length / 2;
+ int row = coeff[0].length >> 1;
int params = u.length;
int paramsDigits = integerDigits(params);
int x0Digits = maxIntegerDigits(x_0);
int[] uDigits = maxIntegerDigits(u);
- StringBuffer buffer = new StringBuffer();
- for(int i = 0; i < x_0.length; i++) {
+ StringBuilder buffer = new StringBuilder();
+ for (int i = 0; i < x_0.length; i++) {
double value = x_0[i];
format(nf, buffer, value, x0Digits);
- for(int j = 0; j < u[0].length; j++) {
- if(i == row) {
- buffer.append(" + a_" + j + " * ");
- }
- else {
+ for (int j = 0; j < u[0].length; j++) {
+ if (i == row) {
+ buffer.append(" + a_").append(j).append(" * ");
+ } else {
buffer.append(" ");
- for(int d = 0; d < paramsDigits; d++) {
- buffer.append(" ");
+ for (int d = 0; d < paramsDigits; d++) {
+ buffer.append(' ');
}
}
format(nf, buffer, u[i][j], uDigits[j]);
}
- buffer.append("\n");
+ buffer.append('\n');
}
return buffer.toString();
}
@@ -395,7 +392,7 @@ public class LinearEquationSystem {
// main loop, transformation to reduced row echelon form
boolean exitLoop = false;
- while(!exitLoop) {
+ while (!exitLoop) {
k++;
// pivot search for entry in remaining matrix
@@ -407,7 +404,7 @@ public class LinearEquationSystem {
IntIntPair pivotPos = new IntIntPair(0, 0);
IntIntPair currPos = new IntIntPair(k, k);
- switch(method){
+ switch(method) {
case TRIVAL_PIVOT_SEARCH:
pivotPos = nonZeroPivotSearch(k);
break;
@@ -419,11 +416,11 @@ public class LinearEquationSystem {
pivotCol = pivotPos.second;
pivot = coeff[this.row[pivotRow]][col[pivotCol]];
- if(logger.isDebugging()) {
- StringBuffer msg = new StringBuffer();
+ if (LOG.isDebugging()) {
+ StringBuilder msg = new StringBuilder();
msg.append("equations ").append(equationsToString(4));
- msg.append(" *** pivot at (").append(pivotRow).append(",").append(pivotCol).append(") = ").append(pivot).append("\n");
- logger.debugFine(msg.toString());
+ msg.append(" *** pivot at (").append(pivotRow).append(',').append(pivotCol).append(") = ").append(pivot).append('\n');
+ LOG.debugFine(msg.toString());
}
// permute rows and columns to get this entry onto
@@ -433,13 +430,13 @@ public class LinearEquationSystem {
// test conditions for exiting loop
// after this iteration
// reasons are: Math.abs(pivot) == 0
- if((Math.abs(pivot) <= Matrix.DELTA)) {
+ if ((Math.abs(pivot) <= Matrix.DELTA)) {
exitLoop = true;
}
// pivoting only if Math.abs(pivot) > 0
// and k <= m - 1
- if((Math.abs(pivot) > Matrix.DELTA)) {
+ if ((Math.abs(pivot) > Matrix.DELTA)) {
rank++;
pivotOperation(k);
}
@@ -448,7 +445,7 @@ public class LinearEquationSystem {
// after this iteration
// reasons are: k == rows-1 : no more rows
// k == cols-1 : no more columns
- if(k == rows - 1 || k == cols - 1) {
+ if (k == rows - 1 || k == cols - 1) {
exitLoop = true;
}
}// end while
@@ -467,15 +464,15 @@ public class LinearEquationSystem {
double max = 0;
int i, j, pivotRow = k, pivotCol = k;
double absValue;
- for(i = k; i < coeff.length; i++) {
- for(j = k; j < coeff[0].length; j++) {
+ for (i = k; i < coeff.length; i++) {
+ for (j = k; j < coeff[0].length; j++) {
// compute absolute value of
// current entry in absValue
absValue = Math.abs(coeff[row[i]][col[j]]);
// compare absValue with value max
// found so far
- if(max < absValue) {
+ if (max < absValue) {
// remember new value and position
max = absValue;
pivotRow = i;
@@ -496,14 +493,14 @@ public class LinearEquationSystem {
int i, j;
double absValue;
- for(i = k; i < coeff.length; i++) {
- for(j = k; j < coeff[0].length; j++) {
+ for (i = k; i < coeff.length; i++) {
+ for (j = k; j < coeff[0].length; j++) {
// compute absolute value of
// current entry in absValue
absValue = Math.abs(coeff[row[i]][col[j]]);
// check if absValue is non-zero
- if(absValue > 0) { // found a pivot element
+ if (absValue > 0) { // found a pivot element
return new IntIntPair(i, j);
}// end if
}// end for j
@@ -541,20 +538,20 @@ public class LinearEquationSystem {
// pivot row: set pivot to 1
coeff[row[k]][col[k]] = 1;
- for(int i = k + 1; i < coeff[k].length; i++) {
+ for (int i = k + 1; i < coeff[k].length; i++) {
coeff[row[k]][col[i]] /= pivot;
}
rhs[row[k]] /= pivot;
- if(logger.isDebugging()) {
- StringBuffer msg = new StringBuffer();
+ if (LOG.isDebugging()) {
+ StringBuilder msg = new StringBuilder();
msg.append("set pivot element to 1 ").append(equationsToString(4));
- logger.debugFine(msg.toString());
+ LOG.debugFine(msg.toString());
}
// for (int i = k + 1; i < coeff.length; i++) {
- for(int i = 0; i < coeff.length; i++) {
- if(i == k) {
+ for (int i = 0; i < coeff.length; i++) {
+ if (i == k) {
continue;
}
@@ -565,7 +562,7 @@ public class LinearEquationSystem {
coeff[row[i]][col[k]] = 0;
// modify entries a[i,j], i > k fixed, j = k+1...n-1
- for(int j = k + 1; j < coeff[0].length; j++) {
+ for (int j = k + 1; j < coeff[0].length; j++) {
coeff[row[i]][col[j]] = coeff[row[i]][col[j]] - coeff[row[k]][col[j]] * q;
}// end for j
@@ -573,10 +570,10 @@ public class LinearEquationSystem {
rhs[row[i]] = rhs[row[i]] - rhs[row[k]] * q;
}// end for k
- if(logger.isDebugging()) {
- StringBuffer msg = new StringBuffer();
+ if (LOG.isDebugging()) {
+ StringBuilder msg = new StringBuilder();
msg.append("after pivot operation ").append(equationsToString(4));
- logger.debugFine(msg.toString());
+ LOG.debugFine(msg.toString());
}
}
@@ -587,65 +584,62 @@ public class LinearEquationSystem {
*/
private void solve(int method) throws NullPointerException {
// solution exists
- if(solved) {
+ if (solved) {
return;
}
// bring in reduced row echelon form
- if(!reducedRowEchelonForm) {
+ if (!reducedRowEchelonForm) {
reducedRowEchelonForm(method);
}
- if(!isSolvable(method)) {
- if(logger.isDebugging()) {
- logger.debugFine("Equation system is not solvable!");
+ if (!isSolvable(method)) {
+ if (LOG.isDebugging()) {
+ LOG.debugFine("Equation system is not solvable!");
}
return;
}
// compute one special solution
int cols = coeff[0].length;
- List<Integer> boundIndices = new ArrayList<Integer>();
+ TIntArrayList boundIndices = new TIntArrayList();
x_0 = new double[cols];
- for(int i = 0; i < coeff.length; i++) {
- for(int j = i; j < coeff[row[i]].length; j++) {
- if(coeff[row[i]][col[j]] == 1) {
+ for (int i = 0; i < coeff.length; i++) {
+ for (int j = i; j < coeff[row[i]].length; j++) {
+ if (coeff[row[i]][col[j]] == 1) {
x_0[col[i]] = rhs[row[i]];
boundIndices.add(col[i]);
break;
}
}
}
- List<Integer> freeIndices = new ArrayList<Integer>();
- for(int i = 0; i < coeff[0].length; i++) {
- if(boundIndices.contains(i)) {
+ TIntArrayList freeIndices = new TIntArrayList();
+ for (int i = 0; i < coeff[0].length; i++) {
+ if (boundIndices.contains(i)) {
continue;
}
freeIndices.add(i);
}
- StringBuffer msg = new StringBuffer();
- if(logger.isDebugging()) {
- msg.append("\nSpecial solution x_0 = [").append(FormatUtil.format(x_0, ",", 4)).append("]");
+ StringBuilder msg = new StringBuilder();
+ if (LOG.isDebugging()) {
+ msg.append("\nSpecial solution x_0 = [").append(FormatUtil.format(x_0, ",", 4)).append(']');
msg.append("\nbound Indices ").append(boundIndices);
msg.append("\nfree Indices ").append(freeIndices);
}
// compute solution space of homogeneous linear equation system
- Integer[] freeParameters = freeIndices.toArray(new Integer[freeIndices.size()]);
- Integer[] boundParameters = boundIndices.toArray(new Integer[boundIndices.size()]);
- Arrays.sort(boundParameters);
+ boundIndices.sort();
int freeIndex = 0;
int boundIndex = 0;
u = new double[cols][freeIndices.size()];
- for(int j = 0; j < u[0].length; j++) {
- for(int i = 0; i < u.length; i++) {
- if(freeIndex < freeParameters.length && i == freeParameters[freeIndex]) {
+ for (int j = 0; j < u[0].length; j++) {
+ for (int i = 0; i < u.length; i++) {
+ if (freeIndex < freeIndices.size() && i == freeIndices.get(freeIndex)) {
u[i][j] = 1;
- }
- else if(boundIndex < boundParameters.length && i == boundParameters[boundIndex]) {
- u[i][j] = -coeff[row[boundIndex]][freeParameters[freeIndex]];
+ } else if (boundIndex < boundIndices.size() && i == boundIndices.get(boundIndex)) {
+ u[i][j] = -coeff[row[boundIndex]][freeIndices.get(freeIndex)];
boundIndex++;
}
}
@@ -654,12 +648,12 @@ public class LinearEquationSystem {
}
- if(logger.isDebugging()) {
+ if (LOG.isDebugging()) {
msg.append("\nU");
- for(double[] anU : u) {
- msg.append("\n").append(FormatUtil.format(anU, ",", 4));
+ for (double[] anU : u) {
+ msg.append('\n').append(FormatUtil.format(anU, ",", 4));
}
- logger.debugFine(msg.toString());
+ LOG.debugFine(msg.toString());
}
solved = true;
@@ -672,17 +666,17 @@ public class LinearEquationSystem {
* @return true if linear system in solvable
*/
private boolean isSolvable(int method) throws NullPointerException {
- if(solved) {
+ if (solved) {
return solvable;
}
- if(!reducedRowEchelonForm) {
+ if (!reducedRowEchelonForm) {
reducedRowEchelonForm(method);
}
// test if rank(coeff) == rank(coeff|rhs)
- for(int i = rank; i < rhs.length; i++) {
- if(Math.abs(rhs[row[i]]) > Matrix.DELTA) {
+ for (int i = rank; i < rhs.length; i++) {
+ if (Math.abs(rhs[row[i]]) > Matrix.DELTA) {
solvable = false;
return false; // not solvable
}
@@ -700,8 +694,8 @@ public class LinearEquationSystem {
*/
private int[] maxIntegerDigits(double[][] values) {
int[] digits = new int[values[0].length];
- for(int j = 0; j < values[0].length; j++) {
- for(double[] value : values) {
+ for (int j = 0; j < values[0].length; j++) {
+ for (double[] value : values) {
digits[j] = Math.max(digits[j], integerDigits(value[j]));
}
}
@@ -716,7 +710,7 @@ public class LinearEquationSystem {
*/
private int maxIntegerDigits(double[] values) {
int digits = 0;
- for(double value : values) {
+ for (double value : values) {
digits = Math.max(digits, integerDigits(value));
}
return digits;
@@ -730,7 +724,7 @@ public class LinearEquationSystem {
*/
private int integerDigits(double d) {
double value = Math.abs(d);
- if(value < 10) {
+ if (value < 10) {
return 1;
}
return (int) Math.log10(value) + 1;
@@ -746,16 +740,15 @@ public class LinearEquationSystem {
* @param value the value to append
* @param maxIntegerDigits the maximum number of integer digits
*/
- private void format(NumberFormat nf, StringBuffer buffer, double value, int maxIntegerDigits) {
- if(value >= 0) {
+ private void format(NumberFormat nf, StringBuilder buffer, double value, int maxIntegerDigits) {
+ if (value >= 0) {
buffer.append(" + ");
- }
- else {
+ } else {
buffer.append(" - ");
}
int digits = maxIntegerDigits - integerDigits(value);
- for(int d = 0; d < digits; d++) {
- buffer.append(" ");
+ for (int d = 0; d < digits; d++) {
+ buffer.append(' ');
}
buffer.append(nf.format(Math.abs(value)));
}
@@ -768,4 +761,4 @@ public class LinearEquationSystem {
public int subspacedim() {
return coeff[0].length - coeff.length;
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/Matrix.java b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/Matrix.java
index eec21404..5a0d1e74 100644
--- a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/Matrix.java
+++ b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/Matrix.java
@@ -23,8 +23,11 @@ package de.lmu.ifi.dbs.elki.math.linearalgebra;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+import gnu.trove.list.array.TDoubleArrayList;
+
import java.io.BufferedReader;
import java.io.StreamTokenizer;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.logging.Logger;
@@ -55,6 +58,26 @@ public class Matrix {
public static final double DELTA = 1E-3;
/**
+ * Error: matrix not square.
+ */
+ public static final String ERR_NOTSQUARE = "All rows must have the same length.";
+
+ /**
+ * Error: matrix indexes incorrect
+ */
+ public static final String ERR_REINDEX = "Submatrix indices incorrect.";
+
+ /**
+ * Error when matrix dimensions do not agree.
+ */
+ public static final String ERR_MATRIX_DIMENSIONS = "Matrix must consist of the same no of rows!";
+
+ /**
+ * Error when matrix inner dimensions do not agree.
+ */
+ private static final String ERR_MATRIX_INNERDIM = "Matrix inner dimensions must agree.";
+
+ /**
* Array for internal storage of elements.
*
* @serial internal array storage.
@@ -89,8 +112,8 @@ public class Matrix {
public Matrix(final int m, final int n, final double s) {
this.columndimension = n;
elements = new double[m][n];
- for(int i = 0; i < m; i++) {
- for(int j = 0; j < n; j++) {
+ for (int i = 0; i < m; i++) {
+ for (int j = 0; j < n; j++) {
elements[i][j] = s;
}
}
@@ -105,9 +128,9 @@ public class Matrix {
*/
public Matrix(final double[][] elements) {
columndimension = elements[0].length;
- for(int i = 0; i < elements.length; i++) {
- if(elements[i].length != columndimension) {
- throw new IllegalArgumentException("All rows must have the same length.");
+ for (int i = 0; i < elements.length; i++) {
+ if (elements[i].length != columndimension) {
+ throw new IllegalArgumentException(ERR_NOTSQUARE);
}
}
this.elements = elements;
@@ -122,8 +145,8 @@ public class Matrix {
public Matrix(final RationalNumber[][] q) {
columndimension = q[0].length;
elements = new double[q.length][columndimension];
- for(int row = 0; row < q.length; row++) {
- for(int col = 0; col < q[row].length; col++) {
+ for (int row = 0; row < q.length; row++) {
+ for (int col = 0; col < q[row].length; col++) {
elements[row][col] = q[row][col].doubleValue();
}
}
@@ -139,12 +162,12 @@ public class Matrix {
*/
public Matrix(final double values[], final int m) {
columndimension = (m != 0 ? values.length / m : 0);
- if(m * columndimension != values.length) {
+ if (m * columndimension != values.length) {
throw new IllegalArgumentException("Array length must be a multiple of m.");
}
elements = new double[m][columndimension];
- for(int i = 0; i < m; i++) {
- for(int j = 0; j < columndimension; j++) {
+ for (int i = 0; i < m; i++) {
+ for (int j = 0; j < columndimension; j++) {
elements[i][j] = values[i + j * m];
}
}
@@ -152,7 +175,7 @@ public class Matrix {
/**
* Constructor, cloning an existing matrix.
- *
+ *
* @param mat Matrix to clone
*/
public Matrix(Matrix mat) {
@@ -166,13 +189,13 @@ public class Matrix {
* @return new matrix
* @throws IllegalArgumentException All rows must have the same length
*/
- public final static Matrix constructWithCopy(final double[][] A) {
+ public static final Matrix constructWithCopy(final double[][] A) {
final int m = A.length;
final int n = A[0].length;
final Matrix X = new Matrix(m, n);
- for(int i = 0; i < m; i++) {
- if(A[i].length != n) {
- throw new IllegalArgumentException("All rows must have the same length.");
+ for (int i = 0; i < m; i++) {
+ if (A[i].length != n) {
+ throw new IllegalArgumentException(ERR_NOTSQUARE);
}
System.arraycopy(A[i], 0, X.elements[i], 0, n);
}
@@ -187,7 +210,7 @@ public class Matrix {
*/
public static final Matrix unitMatrix(final int dim) {
final double[][] e = new double[dim][dim];
- for(int i = 0; i < dim; i++) {
+ for (int i = 0; i < dim; i++) {
e[i][i] = 1;
}
return new Matrix(e);
@@ -213,8 +236,8 @@ public class Matrix {
*/
public static final Matrix random(final int m, final int n) {
final Matrix A = new Matrix(m, n);
- for(int i = 0; i < m; i++) {
- for(int j = 0; j < n; j++) {
+ for (int i = 0; i < m; i++) {
+ for (int j = 0; j < n; j++) {
A.elements[i][j] = Math.random();
}
}
@@ -230,7 +253,7 @@ public class Matrix {
*/
public static final Matrix identity(final int m, final int n) {
final Matrix A = new Matrix(m, n);
- for(int i = 0; i < Math.min(m, n); i++) {
+ for (int i = 0; i < Math.min(m, n); i++) {
A.elements[i][i] = 1.0;
}
return A;
@@ -245,7 +268,7 @@ public class Matrix {
*/
public static final Matrix diagonal(final double[] diagonal) {
final Matrix result = new Matrix(diagonal.length, diagonal.length);
- for(int i = 0; i < diagonal.length; i++) {
+ for (int i = 0; i < diagonal.length; i++) {
result.elements[i][i] = diagonal[i];
}
return result;
@@ -260,7 +283,7 @@ public class Matrix {
*/
public static final Matrix diagonal(final Vector diagonal) {
final Matrix result = new Matrix(diagonal.elements.length, diagonal.elements.length);
- for(int i = 0; i < diagonal.elements.length; i++) {
+ for (int i = 0; i < diagonal.elements.length; i++) {
result.elements[i][i] = diagonal.elements[i];
}
return result;
@@ -273,7 +296,7 @@ public class Matrix {
*/
public final Matrix copy() {
final Matrix X = new Matrix(elements.length, columndimension);
- for(int i = 0; i < elements.length; i++) {
+ for (int i = 0; i < elements.length; i++) {
System.arraycopy(elements[i], 0, X.elements[i], 0, columndimension);
}
return X;
@@ -302,11 +325,9 @@ public class Matrix {
* @return Two-dimensional array copy of matrix elements.
*/
public final double[][] getArrayCopy() {
- final double[][] C = new double[elements.length][columndimension];
- for(int i = 0; i < elements.length; i++) {
- for(int j = 0; j < columndimension; j++) {
- C[i][j] = elements[i][j];
- }
+ final double[][] C = new double[elements.length][];
+ for (int i = 0; i < elements.length; i++) {
+ C[i] = elements[i].clone();
}
return C;
}
@@ -367,7 +388,7 @@ public class Matrix {
public final Matrix increment(final int i, final int j, final double s) {
elements[i][j] += s;
return this;
-}
+ }
/**
* Make a one-dimensional row packed copy of the internal array.
@@ -376,10 +397,8 @@ public class Matrix {
*/
public final double[] getRowPackedCopy() {
double[] vals = new double[elements.length * columndimension];
- for(int i = 0; i < elements.length; i++) {
- for(int j = 0; j < columndimension; j++) {
- vals[i * columndimension + j] = elements[i][j];
- }
+ for (int i = 0; i < elements.length; i++) {
+ System.arraycopy(elements[i], 0, vals, i * columndimension, columndimension);
}
return vals;
}
@@ -391,8 +410,8 @@ public class Matrix {
*/
public final double[] getColumnPackedCopy() {
final double[] vals = new double[elements.length * columndimension];
- for(int i = 0; i < elements.length; i++) {
- for(int j = 0; j < columndimension; j++) {
+ for (int i = 0; i < elements.length; i++) {
+ for (int j = 0; j < columndimension; j++) {
vals[i + j * elements.length] = elements[i][j];
}
}
@@ -412,14 +431,11 @@ public class Matrix {
public final Matrix getMatrix(final int i0, final int i1, final int j0, final int j1) {
final Matrix X = new Matrix(i1 - i0 + 1, j1 - j0 + 1);
try {
- for(int i = i0; i <= i1; i++) {
- for(int j = j0; j <= j1; j++) {
- X.elements[i - i0][j - j0] = elements[i][j];
- }
+ for (int i = i0; i <= i1; i++) {
+ System.arraycopy(elements[i], j0, X.elements[i - i0], 0, j1 - j0 + 1);
}
- }
- catch(ArrayIndexOutOfBoundsException e) {
- throw new ArrayIndexOutOfBoundsException("Submatrix indices");
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new ArrayIndexOutOfBoundsException(ERR_REINDEX);
}
return X;
}
@@ -435,14 +451,13 @@ public class Matrix {
public final Matrix getMatrix(final int[] r, final int[] c) {
final Matrix X = new Matrix(r.length, c.length);
try {
- for(int i = 0; i < r.length; i++) {
- for(int j = 0; j < c.length; j++) {
+ for (int i = 0; i < r.length; i++) {
+ for (int j = 0; j < c.length; j++) {
X.elements[i][j] = elements[r[i]][c[j]];
}
}
- }
- catch(ArrayIndexOutOfBoundsException e) {
- throw new ArrayIndexOutOfBoundsException("Submatrix indices");
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new ArrayIndexOutOfBoundsException(ERR_REINDEX);
}
return X;
}
@@ -459,14 +474,11 @@ public class Matrix {
public final Matrix getMatrix(final int[] r, final int j0, final int j1) {
final Matrix X = new Matrix(r.length, j1 - j0 + 1);
try {
- for(int i = 0; i < r.length; i++) {
- for(int j = j0; j <= j1; j++) {
- X.elements[i][j - j0] = elements[r[i]][j];
- }
+ for (int i = 0; i < r.length; i++) {
+ System.arraycopy(elements[r[i]], j0, X.elements[i], 0, j1 - j0 + 1);
}
- }
- catch(ArrayIndexOutOfBoundsException e) {
- throw new ArrayIndexOutOfBoundsException("Submatrix indices");
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new ArrayIndexOutOfBoundsException(ERR_REINDEX);
}
return X;
}
@@ -483,14 +495,13 @@ public class Matrix {
public final Matrix getMatrix(final int i0, final int i1, final int[] c) {
final Matrix X = new Matrix(i1 - i0 + 1, c.length);
try {
- for(int i = i0; i <= i1; i++) {
- for(int j = 0; j < c.length; j++) {
+ for (int i = i0; i <= i1; i++) {
+ for (int j = 0; j < c.length; j++) {
X.elements[i - i0][j] = elements[i][c[j]];
}
}
- }
- catch(ArrayIndexOutOfBoundsException e) {
- throw new ArrayIndexOutOfBoundsException("Submatrix indices");
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new ArrayIndexOutOfBoundsException(ERR_REINDEX);
}
return X;
}
@@ -507,14 +518,11 @@ public class Matrix {
*/
public final void setMatrix(final int i0, final int i1, final int j0, final int j1, final Matrix X) {
try {
- for(int i = i0; i <= i1; i++) {
- for(int j = j0; j <= j1; j++) {
- elements[i][j] = X.elements[i - i0][j - j0];
- }
+ for (int i = i0; i <= i1; i++) {
+ System.arraycopy(X.elements[i - i0], 0, elements[i], j0, j1 - j0 + 1);
}
- }
- catch(ArrayIndexOutOfBoundsException e) {
- throw new ArrayIndexOutOfBoundsException("Submatrix indices: " + e);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new ArrayIndexOutOfBoundsException(ERR_REINDEX);
}
}
@@ -528,14 +536,13 @@ public class Matrix {
*/
public final void setMatrix(final int[] r, final int[] c, final Matrix X) {
try {
- for(int i = 0; i < r.length; i++) {
- for(int j = 0; j < c.length; j++) {
+ for (int i = 0; i < r.length; i++) {
+ for (int j = 0; j < c.length; j++) {
elements[r[i]][c[j]] = X.elements[i][j];
}
}
- }
- catch(ArrayIndexOutOfBoundsException e) {
- throw new ArrayIndexOutOfBoundsException("Submatrix indices");
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new ArrayIndexOutOfBoundsException(ERR_REINDEX);
}
}
@@ -550,14 +557,11 @@ public class Matrix {
*/
public final void setMatrix(final int[] r, final int j0, final int j1, final Matrix X) {
try {
- for(int i = 0; i < r.length; i++) {
- for(int j = j0; j <= j1; j++) {
- elements[r[i]][j] = X.elements[i][j - j0];
- }
+ for (int i = 0; i < r.length; i++) {
+ System.arraycopy(X.elements[i], 0, elements[r[i]], j0, j1 - j0 + 1);
}
- }
- catch(ArrayIndexOutOfBoundsException e) {
- throw new ArrayIndexOutOfBoundsException("Submatrix indices");
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new ArrayIndexOutOfBoundsException(ERR_REINDEX);
}
}
@@ -572,14 +576,13 @@ public class Matrix {
*/
public final void setMatrix(final int i0, final int i1, final int[] c, final Matrix X) {
try {
- for(int i = i0; i <= i1; i++) {
- for(int j = 0; j < c.length; j++) {
+ for (int i = i0; i <= i1; i++) {
+ for (int j = 0; j < c.length; j++) {
elements[i][c[j]] = X.elements[i - i0][j];
}
}
- }
- catch(ArrayIndexOutOfBoundsException e) {
- throw new ArrayIndexOutOfBoundsException("Submatrix indices");
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new ArrayIndexOutOfBoundsException(ERR_REINDEX);
}
}
@@ -601,12 +604,10 @@ public class Matrix {
* @param row the value of the column to be set
*/
public final void setRow(final int j, final Vector row) {
- if(row.elements.length != columndimension) {
- throw new IllegalArgumentException("Matrix must consist of the same no of columns!");
- }
- for(int i = 0; i < columndimension; i++) {
- elements[j][i] = row.elements[i];
+ if (row.elements.length != columndimension) {
+ throw new IllegalArgumentException(ERR_MATRIX_DIMENSIONS);
}
+ System.arraycopy(row.elements, 0, elements[j], 0, columndimension);
}
/**
@@ -617,7 +618,7 @@ public class Matrix {
*/
public final Vector getCol(final int j) {
final Vector v = new Vector(elements.length);
- for(int i = 0; i < elements.length; i++) {
+ for (int i = 0; i < elements.length; i++) {
v.elements[i] = elements[i][j];
}
return v;
@@ -630,10 +631,10 @@ public class Matrix {
* @param column the value of the column to be set
*/
public final void setCol(final int j, final Vector column) {
- if(column.elements.length != elements.length) {
- throw new IllegalArgumentException("Matrix must consist of the same no of rows!");
+ if (column.elements.length != elements.length) {
+ throw new IllegalArgumentException(ERR_MATRIX_DIMENSIONS);
}
- for(int i = 0; i < elements.length; i++) {
+ for (int i = 0; i < elements.length; i++) {
elements[i][j] = column.elements[i];
}
}
@@ -645,8 +646,8 @@ public class Matrix {
*/
public final Matrix transpose() {
final Matrix X = new Matrix(columndimension, elements.length);
- for(int i = 0; i < elements.length; i++) {
- for(int j = 0; j < columndimension; j++) {
+ for (int i = 0; i < elements.length; i++) {
+ for (int j = 0; j < columndimension; j++) {
X.elements[j][i] = elements[i][j];
}
}
@@ -682,8 +683,8 @@ public class Matrix {
*/
public final Matrix plusEquals(final Matrix B) {
checkMatrixDimensions(B);
- for(int i = 0; i < elements.length; i++) {
- for(int j = 0; j < columndimension; j++) {
+ for (int i = 0; i < elements.length; i++) {
+ for (int j = 0; j < columndimension; j++) {
elements[i][j] += B.elements[i][j];
}
}
@@ -699,8 +700,8 @@ public class Matrix {
*/
public final Matrix plusTimesEquals(final Matrix B, final double s) {
checkMatrixDimensions(B);
- for(int i = 0; i < elements.length; i++) {
- for(int j = 0; j < columndimension; j++) {
+ for (int i = 0; i < elements.length; i++) {
+ for (int j = 0; j < columndimension; j++) {
elements[i][j] += s * B.elements[i][j];
}
}
@@ -736,8 +737,8 @@ public class Matrix {
*/
public final Matrix minusEquals(final Matrix B) {
checkMatrixDimensions(B);
- for(int i = 0; i < elements.length; i++) {
- for(int j = 0; j < columndimension; j++) {
+ for (int i = 0; i < elements.length; i++) {
+ for (int j = 0; j < columndimension; j++) {
elements[i][j] -= B.elements[i][j];
}
}
@@ -753,8 +754,8 @@ public class Matrix {
*/
public final Matrix minusTimesEquals(final Matrix B, final double s) {
checkMatrixDimensions(B);
- for(int i = 0; i < elements.length; i++) {
- for(int j = 0; j < columndimension; j++) {
+ for (int i = 0; i < elements.length; i++) {
+ for (int j = 0; j < columndimension; j++) {
elements[i][j] -= s * B.elements[i][j];
}
}
@@ -778,8 +779,8 @@ public class Matrix {
* @return replace A by s*A
*/
public final Matrix timesEquals(final double s) {
- for(int i = 0; i < elements.length; i++) {
- for(int j = 0; j < columndimension; j++) {
+ for (int i = 0; i < elements.length; i++) {
+ for (int j = 0; j < columndimension; j++) {
elements[i][j] *= s;
}
}
@@ -795,23 +796,22 @@ public class Matrix {
*/
public final Matrix times(final Matrix B) {
// Optimized implementation, exploiting the storage layout
- if(B.elements.length != this.columndimension) {
- throw new IllegalArgumentException("Matrix inner dimensions must agree: "+getRowDimensionality()+","+getColumnDimensionality()+" * "+B.getRowDimensionality()+","+B.getColumnDimensionality());
+ if (B.elements.length != this.columndimension) {
+ throw new IllegalArgumentException(ERR_MATRIX_INNERDIM);
}
final Matrix X = new Matrix(this.elements.length, B.columndimension);
// Optimized ala Jama. jik order.
final double[] Bcolj = new double[this.columndimension];
- for(int j = 0; j < X.columndimension; j++) {
+ for (int j = 0; j < X.columndimension; j++) {
// Make a linear copy of column j from B
- // TODO: use column getter from B?
- for(int k = 0; k < this.columndimension; k++) {
+ for (int k = 0; k < this.columndimension; k++) {
Bcolj[k] = B.elements[k][j];
}
// multiply it with each row from A
- for(int i = 0; i < this.elements.length; i++) {
+ for (int i = 0; i < this.elements.length; i++) {
final double[] Arowi = this.elements[i];
double s = 0;
- for(int k = 0; k < this.columndimension; k++) {
+ for (int k = 0; k < this.columndimension; k++) {
s += Arowi[k] * Bcolj[k];
}
X.elements[i][j] = s;
@@ -828,15 +828,15 @@ public class Matrix {
* @throws IllegalArgumentException Matrix inner dimensions must agree.
*/
public final Vector times(final Vector B) {
- if(B.elements.length != this.columndimension) {
- throw new IllegalArgumentException("Matrix inner dimensions must agree.");
+ if (B.elements.length != this.columndimension) {
+ throw new IllegalArgumentException(ERR_MATRIX_INNERDIM);
}
final Vector X = new Vector(this.elements.length);
// multiply it with each row from A
- for(int i = 0; i < this.elements.length; i++) {
+ for (int i = 0; i < this.elements.length; i++) {
final double[] Arowi = this.elements[i];
double s = 0;
- for(int k = 0; k < this.columndimension; k++) {
+ for (int k = 0; k < this.columndimension; k++) {
s += Arowi[k] * B.elements[k];
}
X.elements[i] = s;
@@ -852,14 +852,14 @@ public class Matrix {
* @throws IllegalArgumentException Matrix inner dimensions must agree.
*/
public final Vector transposeTimes(final Vector B) {
- if(B.elements.length != elements.length) {
- throw new IllegalArgumentException("Matrix inner dimensions must agree.");
+ if (B.elements.length != elements.length) {
+ throw new IllegalArgumentException(ERR_MATRIX_INNERDIM);
}
final Vector X = new Vector(this.columndimension);
// multiply it with each row from A
- for(int i = 0; i < this.columndimension; i++) {
+ for (int i = 0; i < this.columndimension; i++) {
double s = 0;
- for(int k = 0; k < elements.length; k++) {
+ for (int k = 0; k < elements.length; k++) {
s += elements[k][i] * B.elements[k];
}
X.elements[i] = s;
@@ -875,20 +875,20 @@ public class Matrix {
* @throws IllegalArgumentException Matrix inner dimensions must agree.
*/
public final Matrix transposeTimes(final Matrix B) {
- if(B.elements.length != elements.length) {
- throw new IllegalArgumentException("Matrix inner dimensions must agree.");
+ if (B.elements.length != elements.length) {
+ throw new IllegalArgumentException(ERR_MATRIX_INNERDIM);
}
final Matrix X = new Matrix(this.columndimension, B.columndimension);
final double[] Bcolj = new double[elements.length];
- for(int j = 0; j < X.columndimension; j++) {
+ for (int j = 0; j < X.columndimension; j++) {
// Make a linear copy of column j from B
- for(int k = 0; k < elements.length; k++) {
+ for (int k = 0; k < elements.length; k++) {
Bcolj[k] = B.elements[k][j];
}
// multiply it with each row from A
- for(int i = 0; i < this.columndimension; i++) {
+ for (int i = 0; i < this.columndimension; i++) {
double s = 0;
- for(int k = 0; k < elements.length; k++) {
+ for (int k = 0; k < elements.length; k++) {
s += elements[k][i] * Bcolj[k];
}
X.elements[i][j] = s;
@@ -905,17 +905,17 @@ public class Matrix {
* @throws IllegalArgumentException Matrix inner dimensions must agree.
*/
public final Matrix timesTranspose(final Matrix B) {
- if(B.columndimension != this.columndimension) {
- throw new IllegalArgumentException("Matrix inner dimensions must agree.");
+ if (B.columndimension != this.columndimension) {
+ throw new IllegalArgumentException(ERR_MATRIX_INNERDIM);
}
final Matrix X = new Matrix(this.elements.length, B.elements.length);
- for(int j = 0; j < X.elements.length; j++) {
+ for (int j = 0; j < X.elements.length; j++) {
final double[] Browj = B.elements[j];
// multiply it with each row from A
- for(int i = 0; i < this.elements.length; i++) {
+ for (int i = 0; i < this.elements.length; i++) {
final double[] Arowi = this.elements[i];
double s = 0;
- for(int k = 0; k < this.columndimension; k++) {
+ for (int k = 0; k < this.columndimension; k++) {
s += Arowi[k] * Browj[k];
}
X.elements[i][j] = s;
@@ -933,23 +933,23 @@ public class Matrix {
*/
public final Matrix transposeTimesTranspose(Matrix B) {
// Optimized implementation, exploiting the storage layout
- if(this.elements.length != B.columndimension) {
- throw new IllegalArgumentException("Matrix inner dimensions must agree: "+getRowDimensionality()+","+getColumnDimensionality()+" * "+B.getRowDimensionality()+","+B.getColumnDimensionality());
+ if (this.elements.length != B.columndimension) {
+ throw new IllegalArgumentException("Matrix inner dimensions must agree: " + getRowDimensionality() + "," + getColumnDimensionality() + " * " + B.getRowDimensionality() + "," + B.getColumnDimensionality());
}
final Matrix X = new Matrix(this.columndimension, B.elements.length);
// Optimized ala Jama. jik order.
final double[] Acolj = new double[this.elements.length];
- for(int j = 0; j < X.elements.length; j++) {
+ for (int j = 0; j < X.elements.length; j++) {
// Make a linear copy of column j from B
- for(int k = 0; k < this.elements.length; k++) {
+ for (int k = 0; k < this.elements.length; k++) {
Acolj[k] = this.elements[k][j];
}
final double[] Xrow = X.elements[j];
// multiply it with each row from A
- for(int i = 0; i < B.elements.length; i++) {
+ for (int i = 0; i < B.elements.length; i++) {
final double[] Browi = B.elements[i];
double s = 0;
- for(int k = 0; k < B.columndimension; k++) {
+ for (int k = 0; k < B.columndimension; k++) {
s += Browi[k] * Acolj[k];
}
Xrow[i] = s;
@@ -1011,7 +1011,7 @@ public class Matrix {
*/
public final double trace() {
double t = 0;
- for(int i = 0; i < Math.min(elements.length, columndimension); i++) {
+ for (int i = 0; i < Math.min(elements.length, columndimension); i++) {
t += elements[i][i];
}
return t;
@@ -1024,9 +1024,9 @@ public class Matrix {
*/
public final double norm1() {
double f = 0;
- for(int j = 0; j < columndimension; j++) {
+ for (int j = 0; j < columndimension; j++) {
double s = 0;
- for(int i = 0; i < elements.length; i++) {
+ for (int i = 0; i < elements.length; i++) {
s += Math.abs(elements[i][j]);
}
f = Math.max(f, s);
@@ -1050,9 +1050,9 @@ public class Matrix {
*/
public final double normInf() {
double f = 0;
- for(int i = 0; i < elements.length; i++) {
+ for (int i = 0; i < elements.length; i++) {
double s = 0;
- for(int j = 0; j < columndimension; j++) {
+ for (int j = 0; j < columndimension; j++) {
s += Math.abs(elements[i][j]);
}
f = Math.max(f, s);
@@ -1067,8 +1067,8 @@ public class Matrix {
*/
public final double normF() {
double f = 0;
- for(int i = 0; i < elements.length; i++) {
- for(int j = 0; j < columndimension; j++) {
+ for (int i = 0; i < elements.length; i++) {
+ for (int j = 0; j < columndimension; j++) {
f = MathUtil.fastHypot(f, elements[i][j]);
}
}
@@ -1079,14 +1079,14 @@ public class Matrix {
* Normalizes the columns of this matrix to length of 1.0.
*/
public final void normalizeColumns() {
- for(int col = 0; col < columndimension; col++) {
+ for (int col = 0; col < columndimension; col++) {
double norm = 0.0;
- for(int row = 0; row < elements.length; row++) {
+ for (int row = 0; row < elements.length; row++) {
norm = norm + (elements[row][col] * elements[row][col]);
}
norm = Math.sqrt(norm);
- if(norm != 0) {
- for(int row = 0; row < elements.length; row++) {
+ if (norm != 0) {
+ for (int row = 0; row < elements.length; row++) {
elements[row][col] /= norm;
}
}
@@ -1105,36 +1105,34 @@ public class Matrix {
* columns of this matrix
*/
public final boolean linearlyIndependent(final Matrix columnMatrix) {
- if(columnMatrix.columndimension != 1) {
+ if (columnMatrix.columndimension != 1) {
throw new IllegalArgumentException("a.getColumnDimension() != 1");
}
- if(this.elements.length != columnMatrix.elements.length) {
- throw new IllegalArgumentException("a.getRowDimension() != b.getRowDimension()");
+ if (this.elements.length != columnMatrix.elements.length) {
+ throw new IllegalArgumentException(ERR_MATRIX_DIMENSIONS);
}
- if(this.columndimension + columnMatrix.columndimension > this.elements.length) {
+ if (this.columndimension + columnMatrix.columndimension > this.elements.length) {
return false;
}
- final StringBuffer msg = LoggingConfiguration.DEBUG ? new StringBuffer() : null;
+ final StringBuilder msg = LoggingConfiguration.DEBUG ? new StringBuilder() : null;
final double[][] a = new double[columndimension + 1][elements.length - 1];
final double[] b = new double[columndimension + 1];
- for(int i = 0; i < a.length; i++) {
- for(int j = 0; j < a[i].length; j++) {
- if(i < columndimension) {
+ for (int i = 0; i < a.length; i++) {
+ for (int j = 0; j < a[i].length; j++) {
+ if (i < columndimension) {
a[i][j] = elements[j][i];
- }
- else {
+ } else {
a[i][j] = columnMatrix.elements[j][0];
}
}
}
- for(int i = 0; i < b.length; i++) {
- if(i < columndimension) {
+ for (int i = 0; i < b.length; i++) {
+ if (i < columndimension) {
b[i] = elements[elements.length - 1][i];
- }
- else {
+ } else {
b[i] = columnMatrix.elements[i][0];
}
}
@@ -1145,31 +1143,31 @@ public class Matrix {
final double[][] coefficients = les.getCoefficents();
final double[] rhs = les.getRHS();
- if(msg != null) {
- msg.append("\na' " + FormatUtil.format(this.getArrayRef()));
- msg.append("\nb' " + FormatUtil.format(columnMatrix.getColumnPackedCopy()));
+ if (msg != null) {
+ msg.append("\na' ").append(FormatUtil.format(this.getArrayRef()));
+ msg.append("\nb' ").append(FormatUtil.format(columnMatrix.getColumnPackedCopy()));
- msg.append("\na " + FormatUtil.format(a));
- msg.append("\nb " + FormatUtil.format(b));
- msg.append("\nleq " + les.equationsToString(4));
+ msg.append("\na ").append(FormatUtil.format(a));
+ msg.append("\nb ").append(FormatUtil.format(b));
+ msg.append("\nleq ").append(les.equationsToString(4));
}
- for(int i = 0; i < coefficients.length; i++) {
+ for (int i = 0; i < coefficients.length; i++) {
boolean allCoefficientsZero = true;
- for(int j = 0; j < coefficients[i].length; j++) {
+ for (int j = 0; j < coefficients[i].length; j++) {
final double value = coefficients[i][j];
- if(Math.abs(value) > DELTA) {
+ if (Math.abs(value) > DELTA) {
allCoefficientsZero = false;
break;
}
}
// allCoefficients=0 && rhs=0 -> linearly dependent
- if(allCoefficientsZero) {
+ if (allCoefficientsZero) {
final double value = rhs[i];
- if(Math.abs(value) < DELTA) {
- if(msg != null) {
- msg.append("\nvalue " + value + "[" + i + "]");
- msg.append("\nlinearly independent " + false);
+ if (Math.abs(value) < DELTA) {
+ if (msg != null) {
+ msg.append("\nvalue ").append(value).append('[').append(i).append(']');
+ msg.append("\nlinearly independent ").append(false);
Logger.getLogger(this.getClass().getName()).fine(msg.toString());
}
return false;
@@ -1177,8 +1175,8 @@ public class Matrix {
}
}
- if(msg != null) {
- msg.append("\nlinearly independent " + true);
+ if (msg != null) {
+ msg.append("\nlinearly independent ").append(true);
Logger.getLogger(this.getClass().getName()).fine(msg.toString());
}
return true;
@@ -1195,18 +1193,18 @@ public class Matrix {
final RationalNumber[][] gauss = exactGaussElimination();
// reduced form
- for(int row = gauss.length - 1; row > 0; row--) {
+ for (int row = gauss.length - 1; row > 0; row--) {
int firstCol = -1;
- for(int col = 0; col < gauss[row].length && firstCol == -1; col++) {
+ for (int col = 0; col < gauss[row].length && firstCol == -1; col++) {
// if(gauss.get(row, col) != 0.0) // i.e. == 1
- if(gauss[row][col].equals(RationalNumber.ONE)) {
+ if (gauss[row][col].equals(RationalNumber.ONE)) {
firstCol = col;
}
}
- if(firstCol > -1) {
- for(int currentRow = row - 1; currentRow >= 0; currentRow--) {
+ if (firstCol > -1) {
+ for (int currentRow = row - 1; currentRow >= 0; currentRow--) {
RationalNumber multiplier = gauss[currentRow][firstCol].copy();
- for(int col = firstCol; col < gauss[currentRow].length; col++) {
+ for (int col = firstCol; col < gauss[currentRow].length; col++) {
RationalNumber subtrahent = gauss[row][col].times(multiplier);
gauss[currentRow][col] = gauss[currentRow][col].minus(subtrahent);
}
@@ -1225,8 +1223,8 @@ public class Matrix {
*/
private final RationalNumber[][] exactGaussElimination() {
final RationalNumber[][] gauss = new RationalNumber[elements.length][this.columndimension];
- for(int row = 0; row < elements.length; row++) {
- for(int col = 0; col < this.columndimension; col++) {
+ for (int row = 0; row < elements.length; row++) {
+ for (int col = 0; col < this.columndimension; col++) {
gauss[row][col] = new RationalNumber(elements[row][col]);
}
}
@@ -1245,10 +1243,10 @@ public class Matrix {
int firstRow = -1;
// 1. find first column unequal to zero
- for(int col = 0; col < gauss[0].length && firstCol == -1; col++) {
- for(int row = 0; row < gauss.length && firstCol == -1; row++) {
+ for (int col = 0; col < gauss[0].length && firstCol == -1; col++) {
+ for (int row = 0; row < gauss.length && firstCol == -1; row++) {
// if(gauss.get(row, col) != 0.0)
- if(!gauss[row][col].equals(RationalNumber.ZERO)) {
+ if (!gauss[row][col].equals(RationalNumber.ZERO)) {
firstCol = col;
firstRow = row;
}
@@ -1256,8 +1254,8 @@ public class Matrix {
}
// 2. set row as first row
- if(firstCol != -1) {
- if(firstRow != 0) {
+ if (firstCol != -1) {
+ if (firstRow != 0) {
final RationalNumber[] row = new RationalNumber[gauss[firstRow].length];
System.arraycopy(gauss[firstRow], 0, row, 0, gauss[firstRow].length);
System.arraycopy(gauss[0], 0, gauss[firstRow], 0, gauss[firstRow].length);
@@ -1265,19 +1263,19 @@ public class Matrix {
}
// 3. create leading 1
- if(!gauss[0][firstCol].equals(RationalNumber.ONE)) {
+ if (!gauss[0][firstCol].equals(RationalNumber.ONE)) {
final RationalNumber inverse = gauss[0][firstCol].multiplicativeInverse();
- for(int col = 0; col < gauss[0].length; col++) {
+ for (int col = 0; col < gauss[0].length; col++) {
gauss[0][col] = gauss[0][col].times(inverse);
}
}
// 4. eliminate values unequal to zero below leading 1
- for(int row = 1; row < gauss.length; row++) {
+ for (int row = 1; row < gauss.length; row++) {
final RationalNumber multiplier = gauss[row][firstCol].copy();
// if(multiplier != 0.0)
- if(!multiplier.equals(RationalNumber.ZERO)) {
- for(int col = firstCol; col < gauss[row].length; col++) {
+ if (!multiplier.equals(RationalNumber.ZERO)) {
+ for (int col = firstCol; col < gauss[row].length; col++) {
final RationalNumber subtrahent = gauss[0][col].times(multiplier);
gauss[row][col] = gauss[row][col].minus(subtrahent);
}
@@ -1285,7 +1283,7 @@ public class Matrix {
}
// 5. recursion
- if(gauss.length > 1) {
+ if (gauss.length > 1) {
final RationalNumber[][] subMatrix = new RationalNumber[gauss.length - 1][gauss[1].length];
System.arraycopy(gauss, 1, subMatrix, 0, gauss.length - 1);
final RationalNumber[][] eliminatedSubMatrix = exactGaussElimination(subMatrix);
@@ -1301,12 +1299,12 @@ public class Matrix {
* @return true, if this matrix is symmetric, false otherwise
*/
public final boolean isSymmetric() {
- if(elements.length != columndimension) {
+ if (elements.length != columndimension) {
return false;
}
- for(int i = 0; i < elements.length; i++) {
- for(int j = i + 1; j < columndimension; j++) {
- if(elements[i][j] != elements[j][i]) {
+ for (int i = 0; i < elements.length; i++) {
+ for (int j = i + 1; j < columndimension; j++) {
+ if (elements[i][j] != elements[j][i]) {
return false;
}
}
@@ -1323,16 +1321,16 @@ public class Matrix {
public final Matrix completeBasis() {
Matrix basis = copy();
Matrix result = null;
- for(int i = 0; i < elements.length; i++) {
+ for (int i = 0; i < elements.length; i++) {
final Matrix e_i = new Matrix(elements.length, 1);
e_i.elements[0][i] = 1.0;
final boolean li = basis.linearlyIndependent(e_i);
- if(li) {
- if(result == null) {
+ // TODO: efficiency - appendColumns is expensive.
+ if (li) {
+ if (result == null) {
result = e_i.copy();
- }
- else {
+ } else {
result = result.appendColumns(e_i);
}
basis = basis.appendColumns(e_i);
@@ -1350,16 +1348,16 @@ public class Matrix {
public final Matrix completeToOrthonormalBasis() {
Matrix basis = copy();
Matrix result = null;
- for(int i = 0; i < elements.length; i++) {
+ for (int i = 0; i < elements.length; i++) {
final Matrix e_i = new Matrix(elements.length, 1);
e_i.elements[i][0] = 1.0;
final boolean li = basis.linearlyIndependent(e_i);
- if(li) {
- if(result == null) {
+ // TODO: efficiency - appendColumns is expensive.
+ if (li) {
+ if (result == null) {
result = e_i.copy();
- }
- else {
+ } else {
result = result.appendColumns(e_i);
}
basis = basis.appendColumns(e_i);
@@ -1376,17 +1374,16 @@ public class Matrix {
* @return the new matrix with the appended columns
*/
public final Matrix appendColumns(final Matrix columns) {
- if(elements.length != columns.elements.length) {
- throw new IllegalArgumentException("m.getRowDimension() != column.getRowDimension()");
+ if (elements.length != columns.elements.length) {
+ throw new IllegalArgumentException(ERR_MATRIX_DIMENSIONS);
}
final Matrix result = new Matrix(elements.length, columndimension + columns.columndimension);
- for(int i = 0; i < result.columndimension; i++) {
+ for (int i = 0; i < result.columndimension; i++) {
// FIXME: optimize - excess copying!
- if(i < columndimension) {
+ if (i < columndimension) {
result.setCol(i, getCol(i));
- }
- else {
+ } else {
result.setCol(i, columns.getCol(i - columndimension));
}
}
@@ -1402,10 +1399,10 @@ public class Matrix {
Matrix v = copy();
// FIXME: optimize - excess copying!
- for(int i = 1; i < columndimension; i++) {
+ for (int i = 1; i < columndimension; i++) {
final Vector u_i = getCol(i);
final Vector sum = new Vector(elements.length);
- for(int j = 0; j < i; j++) {
+ for (int j = 0; j < i; j++) {
final Vector v_j = v.getCol(j);
double scalar = u_i.transposeTimes(v_j) / v_j.transposeTimes(v_j);
sum.plusTimesEquals(v_j, scalar);
@@ -1428,7 +1425,7 @@ public class Matrix {
*/
public final Matrix cheatToAvoidSingularity(final double constant) {
final Matrix a = this.copy();
- for(int i = 0; i < a.columndimension && i < a.elements.length; i++) {
+ for (int i = 0; i < a.columndimension && i < a.elements.length; i++) {
// if(a.get(i, i) < constant)
{
a.elements[i][i] += constant;
@@ -1460,48 +1457,45 @@ public class Matrix {
tokenizer.wordChars(0, 255);
tokenizer.whitespaceChars(0, ' ');
tokenizer.eolIsSignificant(true);
- java.util.Vector<Double> v = new java.util.Vector<Double>();
+ TDoubleArrayList v = new TDoubleArrayList();
// Ignore initial empty lines
- while(tokenizer.nextToken() == StreamTokenizer.TT_EOL) {
+ while (tokenizer.nextToken() == StreamTokenizer.TT_EOL) {
// ignore initial empty lines
}
- if(tokenizer.ttype == StreamTokenizer.TT_EOF) {
+ if (tokenizer.ttype == StreamTokenizer.TT_EOF) {
throw new java.io.IOException("Unexpected EOF on matrix read.");
}
do {
- v.addElement(Double.valueOf(tokenizer.sval)); // Read & store 1st
+ v.add(Double.parseDouble(tokenizer.sval)); // Read & store 1st
// row.
}
- while(tokenizer.nextToken() == StreamTokenizer.TT_WORD);
+ while (tokenizer.nextToken() == StreamTokenizer.TT_WORD);
int n = v.size(); // Now we've got the number of columns!
- double row[] = new double[n];
- for(int j = 0; j < n; j++) {
- // extract the elements of the 1st row.
- row[j] = v.elementAt(j);
- }
- // v.removeAllElements();
- java.util.Vector<double[]> rowV = new java.util.Vector<double[]>();
- rowV.addElement(row); // Start storing rows instead of columns.
- while(tokenizer.nextToken() == StreamTokenizer.TT_WORD) {
+ double row[] = v.toArray();
+ ArrayList<double[]> rowV = new ArrayList<double[]>();
+ rowV.add(row); // Start storing rows instead of columns.
+ while (tokenizer.nextToken() == StreamTokenizer.TT_WORD) {
// While non-empty lines
- rowV.addElement(row = new double[n]);
+ rowV.add(row = new double[n]);
int j = 0;
do {
- if(j >= n) {
+ if (j >= n) {
throw new java.io.IOException("Row " + v.size() + " is too long.");
}
- row[j++] = (Double.valueOf(tokenizer.sval));
+ row[j++] = Double.parseDouble(tokenizer.sval);
}
- while(tokenizer.nextToken() == StreamTokenizer.TT_WORD);
- if(j < n) {
+ while (tokenizer.nextToken() == StreamTokenizer.TT_WORD);
+ if (j < n) {
throw new java.io.IOException("Row " + v.size() + " is too short.");
}
}
int m = rowV.size(); // Now we've got the number of rows.
double[][] A = new double[m][];
- rowV.copyInto(A); // copy the rows out of the vector
+ for (int i = 0; i < m; i++) {
+ A[i] = rowV.get(i);
+ }
return new Matrix(A);
}
@@ -1509,7 +1503,7 @@ public class Matrix {
* Check if size(A) == size(B)
*/
protected void checkMatrixDimensions(Matrix B) {
- if(B.getRowDimensionality() != getRowDimensionality() || B.getColumnDimensionality() != getColumnDimensionality()) {
+ if (B.getRowDimensionality() != getRowDimensionality() || B.getColumnDimensionality() != getColumnDimensionality()) {
throw new IllegalArgumentException("Matrix dimensions must agree.");
}
}
@@ -1526,25 +1520,25 @@ public class Matrix {
@Override
public boolean equals(Object obj) {
- if(this == obj) {
+ if (this == obj) {
return true;
}
- if(obj == null) {
+ if (obj == null) {
return false;
}
- if(getClass() != obj.getClass()) {
+ if (getClass() != obj.getClass()) {
return false;
}
final Matrix other = (Matrix) obj;
- if(this.elements.length != other.elements.length) {
+ if (this.elements.length != other.elements.length) {
return false;
}
- if(this.columndimension != other.columndimension) {
+ if (this.columndimension != other.columndimension) {
return false;
}
- for(int i = 0; i < this.elements.length; i++) {
- for(int j = 0; j < this.columndimension; j++) {
- if(this.elements[i][j] != other.elements[i][j]) {
+ for (int i = 0; i < this.elements.length; i++) {
+ for (int j = 0; j < this.columndimension; j++) {
+ if (this.elements[i][j] != other.elements[i][j]) {
return false;
}
}
@@ -1561,25 +1555,25 @@ public class Matrix {
* @return true if delta smaller than maximum
*/
public boolean almostEquals(Object obj, double maxdelta) {
- if(this == obj) {
+ if (this == obj) {
return true;
}
- if(obj == null) {
+ if (obj == null) {
return false;
}
- if(getClass() != obj.getClass()) {
+ if (getClass() != obj.getClass()) {
return false;
}
final Matrix other = (Matrix) obj;
- if(this.elements.length != other.elements.length) {
+ if (this.elements.length != other.elements.length) {
return false;
}
- if(this.columndimension != other.columndimension) {
+ if (this.columndimension != other.columndimension) {
return false;
}
- for(int i = 0; i < this.elements.length; i++) {
- for(int j = 0; j < this.columndimension; j++) {
- if(Math.abs(this.elements[i][j] - other.elements[i][j]) > maxdelta) {
+ for (int i = 0; i < this.elements.length; i++) {
+ for (int j = 0; j < this.columndimension; j++) {
+ if (Math.abs(this.elements[i][j] - other.elements[i][j]) > maxdelta) {
return false;
}
}
@@ -1605,4 +1599,4 @@ public class Matrix {
public String toString() {
return FormatUtil.format(this);
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/ProjectedCentroid.java b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/ProjectedCentroid.java
index 435ac3d7..2a24a74d 100644
--- a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/ProjectedCentroid.java
+++ b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/ProjectedCentroid.java
@@ -29,7 +29,7 @@ import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
/**
* Centroid only using a subset of dimensions.
@@ -57,11 +57,11 @@ public class ProjectedCentroid extends Centroid {
public ProjectedCentroid(BitSet dims, int dim) {
super(dim);
this.dims = dims;
- assert (dims.size() <= dim);
+ assert (dims.length() <= dim) : (dims.length() + " > " + dim + " ?!?");
}
/**
- * Add a single value with weight 1.0
+ * Add a single value with weight 1.0.
*
* @param val Value
*/
@@ -82,8 +82,11 @@ public class ProjectedCentroid extends Centroid {
* @param weight weight
*/
@Override
- public void put(double val[], double weight) {
+ public void put(double[] val, double weight) {
assert (val.length == elements.length);
+ if (weight == 0) {
+ return; // Skip zero weights.
+ }
final double nwsum = weight + wsum;
for(int i = dims.nextSetBit(0); i >= 0; i = dims.nextSetBit(i + 1)) {
final double delta = val[i] - elements[i];
@@ -94,16 +97,16 @@ public class ProjectedCentroid extends Centroid {
}
/**
- * Add a single value with weight 1.0
+ * Add a single value with weight 1.0.
*
* @param val Value
*/
@Override
- public void put(NumberVector<?, ?> val) {
+ public void put(NumberVector<?> val) {
assert (val.getDimensionality() == elements.length);
wsum += 1.0;
for(int i = dims.nextSetBit(0); i >= 0; i = dims.nextSetBit(i + 1)) {
- final double delta = val.doubleValue(i + 1) - elements[i];
+ final double delta = val.doubleValue(i) - elements[i];
elements[i] += delta / wsum;
}
}
@@ -115,11 +118,14 @@ public class ProjectedCentroid extends Centroid {
* @param weight weight
*/
@Override
- public void put(NumberVector<?, ?> val, double weight) {
+ public void put(NumberVector<?> val, double weight) {
assert (val.getDimensionality() == elements.length);
+ if (weight == 0) {
+ return; // Skip zero weights.
+ }
final double nwsum = weight + wsum;
for(int i = dims.nextSetBit(0); i >= 0; i = dims.nextSetBit(i + 1)) {
- final double delta = val.doubleValue(i + 1) - elements[i];
+ final double delta = val.doubleValue(i) - elements[i];
final double rval = delta * weight / nwsum;
elements[i] += rval;
}
@@ -131,10 +137,11 @@ public class ProjectedCentroid extends Centroid {
*
* @param dims Dimensions to use (indexed with 0)
* @param relation Relation to process
+ * @return Centroid
*/
- public static ProjectedCentroid make(BitSet dims, Relation<? extends NumberVector<?, ?>> relation) {
- ProjectedCentroid c = new ProjectedCentroid(dims, DatabaseUtil.dimensionality(relation));
- assert (dims.size() <= DatabaseUtil.dimensionality(relation));
+ public static ProjectedCentroid make(BitSet dims, Relation<? extends NumberVector<?>> relation) {
+ ProjectedCentroid c = new ProjectedCentroid(dims, RelationUtil.dimensionality(relation));
+ assert (dims.size() <= RelationUtil.dimensionality(relation));
for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
c.put(relation.get(iditer));
}
@@ -147,11 +154,12 @@ public class ProjectedCentroid extends Centroid {
* @param dims Dimensions to use (indexed with 0)
* @param relation Relation to process
* @param ids IDs to process
+ * @return Centroid
*/
- public static ProjectedCentroid make(BitSet dims, Relation<? extends NumberVector<?, ?>> relation, DBIDs ids) {
- ProjectedCentroid c = new ProjectedCentroid(dims, DatabaseUtil.dimensionality(relation));
- assert (dims.size() <= DatabaseUtil.dimensionality(relation));
- for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
+ public static ProjectedCentroid make(BitSet dims, Relation<? extends NumberVector<?>> relation, DBIDs ids) {
+ ProjectedCentroid c = new ProjectedCentroid(dims, RelationUtil.dimensionality(relation));
+ assert (dims.length() <= RelationUtil.dimensionality(relation));
+ for(DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
c.put(relation.get(iter));
}
return c;
diff --git a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/ProjectionResult.java b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/ProjectionResult.java
index d8858657..9aa1fa7c 100644
--- a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/ProjectionResult.java
+++ b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/ProjectionResult.java
@@ -38,12 +38,12 @@ public interface ProjectionResult {
*
* @return number of strong (correlated) dimensions
*/
- public int getCorrelationDimension();
+ int getCorrelationDimension();
/**
* Projection matrix
*
* @return projection matrix
*/
- public Matrix similarityMatrix();
+ Matrix similarityMatrix();
}
diff --git a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/SingularValueDecomposition.java b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/SingularValueDecomposition.java
index 183a8034..772e92eb 100644
--- a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/SingularValueDecomposition.java
+++ b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/SingularValueDecomposition.java
@@ -120,7 +120,7 @@ public class SingularValueDecomposition {
s[k] = -s[k];
}
for(int j = k + 1; j < n; j++) {
- if((k < nct) & (s[k] != 0.0)) {
+ if((k < nct) && (s[k] != 0.0)) {
// Apply the transformation.
double t = 0;
for(int i = k; i < m; i++) {
@@ -137,7 +137,7 @@ public class SingularValueDecomposition {
e[j] = A[k][j];
}
- if(wantu & (k < nct)) {
+ if(wantu && (k < nct)) {
// Place the transformation in U for subsequent back
// multiplication.
@@ -163,7 +163,7 @@ public class SingularValueDecomposition {
e[k + 1] += 1.0;
}
e[k] = -e[k];
- if((k + 1 < m) & (e[k] != 0.0)) {
+ if((k + 1 < m) && (e[k] != 0.0)) {
// Apply the transformation.
for(int i = k + 1; i < m; i++) {
work[i] = 0.0;
@@ -245,7 +245,7 @@ public class SingularValueDecomposition {
// If required, generate V.
if(wantv) {
for(int k = n - 1; k >= 0; k--) {
- if((k < nrt) & (e[k] != 0.0)) {
+ if((k < nrt) && (e[k] != 0.0)) {
for(int j = k + 1; j < nu; j++) {
double t = 0;
for(int i = k + 1; i < n; i++) {
@@ -383,7 +383,7 @@ public class SingularValueDecomposition {
double b = ((spm1 + sp) * (spm1 - sp) + epm1 * epm1) / 2.0;
double c = (sp * epm1) * (sp * epm1);
double shift = 0.0;
- if((b != 0.0) | (c != 0.0)) {
+ if((b != 0.0) || (c != 0.0)) {
shift = Math.sqrt(b * b + c);
if(b < 0.0) {
shift = -shift;
diff --git a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/SortedEigenPairs.java b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/SortedEigenPairs.java
index 5fa023ca..dcc2f2c0 100644
--- a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/SortedEigenPairs.java
+++ b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/SortedEigenPairs.java
@@ -185,9 +185,9 @@ public class SortedEigenPairs {
*/
@Override
public String toString() {
- StringBuffer result = new StringBuffer();
+ StringBuilder result = new StringBuilder();
for(EigenPair eigenPair : eigenPairs) {
- result.append("\n").append(eigenPair);
+ result.append('\n').append(eigenPair);
}
return result.toString();
}
diff --git a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/VMath.java b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/VMath.java
index 97466b20..6b6d7090 100644
--- a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/VMath.java
+++ b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/VMath.java
@@ -41,6 +41,26 @@ public final class VMath {
public static final double DELTA = 1E-5;
/**
+ * Error message (in assertions!) when vector dimensionalities do not agree.
+ */
+ public static final String ERR_VEC_DIMENSIONS = "Vector dimensions do not agree.";
+
+ /**
+ * Error message (in assertions!) when matrix dimensionalities do not agree.
+ */
+ public static final String ERR_MATRIX_DIMENSIONS = "Matrix dimensions do not agree.";
+
+ /**
+ * Error message (in assertions!) when matrix dimensionalities do not agree.
+ */
+ public static final String ERR_MATRIX_INNERDIM = "Matrix inner dimensions do not agree.";
+
+ /**
+ * Error message (in assertions!) when dimensionalities do not agree.
+ */
+ private static final String ERR_DIMENSIONS = "Dimensionalities do not agree.";
+
+ /**
* Fake constructor. Static class.
*/
private VMath() {
@@ -53,19 +73,18 @@ public final class VMath {
* @param dimensionality dimensionality
* @return Random vector of length 1.0
*/
- public final static double[] randomNormalizedVector(final int dimensionality) {
+ public static final double[] randomNormalizedVector(final int dimensionality) {
final double[] v = new double[dimensionality];
- for(int i = 0; i < dimensionality; i++) {
+ for (int i = 0; i < dimensionality; i++) {
v[i] = Math.random();
}
double norm = euclideanLength(v);
- if(norm != 0) {
- for(int row = 0; row < v.length; row++) {
+ if (norm != 0) {
+ for (int row = 0; row < v.length; row++) {
v[row] /= norm;
}
return v;
- }
- else {
+ } else {
return randomNormalizedVector(dimensionality);
}
}
@@ -77,7 +96,7 @@ public final class VMath {
* @param i the index
* @return the ith unit vector of the specified dimensionality
*/
- public final static double[] unitVector(final int dimensionality, final int i) {
+ public static final double[] unitVector(final int dimensionality, final int i) {
final double[] v = new double[dimensionality];
v[i] = 1;
return v;
@@ -89,7 +108,7 @@ public final class VMath {
* @param v original vector
* @return a copy of this vector
*/
- public final static double[] copy(final double[] v) {
+ public static final double[] copy(final double[] v) {
return Arrays.copyOf(v, v.length);
}
@@ -99,9 +118,9 @@ public final class VMath {
* @param v Vector
* @return Matrix
*/
- public final static double[][] transpose(final double[] v) {
+ public static final double[][] transpose(final double[] v) {
double[][] re = new double[v.length][1];
- for(int i = 0; i < v.length; i++) {
+ for (int i = 0; i < v.length; i++) {
re[i][0] = v[i];
}
return re;
@@ -114,10 +133,10 @@ public final class VMath {
* @param v2 second vector
* @return the sum v1 + v2
*/
- public final static double[] plus(final double[] v1, final double[] v2) {
- assert (v1.length == v2.length) : "Vector dimensions must agree.";
+ public static final double[] plus(final double[] v1, final double[] v2) {
+ assert (v1.length == v2.length) : ERR_VEC_DIMENSIONS;
final double[] result = new double[v1.length];
- for(int i = 0; i < result.length; i++) {
+ for (int i = 0; i < result.length; i++) {
result[i] = v1[i] + v2[i];
}
return result;
@@ -131,10 +150,10 @@ public final class VMath {
* @param s2 the scalar
* @return the result of v1 + v2 * s2
*/
- public final static double[] plusTimes(final double[] v1, final double[] v2, final double s2) {
- assert (v1.length == v2.length) : "Vector dimensions must agree.";
+ public static final double[] plusTimes(final double[] v1, final double[] v2, final double s2) {
+ assert (v1.length == v2.length) : ERR_VEC_DIMENSIONS;
final double[] result = new double[v1.length];
- for(int i = 0; i < result.length; i++) {
+ for (int i = 0; i < result.length; i++) {
result[i] = v1[i] + v2[i] * s2;
}
return result;
@@ -148,10 +167,10 @@ public final class VMath {
* @param v2 second vector
* @return the result of v1 * s1 + v2
*/
- public final static double[] timesPlus(final double[] v1, final double s1, final double[] v2) {
- assert (v1.length == v2.length) : "Vector dimensions must agree.";
+ public static final double[] timesPlus(final double[] v1, final double s1, final double[] v2) {
+ assert (v1.length == v2.length) : ERR_VEC_DIMENSIONS;
final double[] result = new double[v1.length];
- for(int i = 0; i < result.length; i++) {
+ for (int i = 0; i < result.length; i++) {
result[i] = v1[i] * s1 + v2[i];
}
return result;
@@ -166,10 +185,10 @@ public final class VMath {
* @param s2 the scalar for v2
* @return the result of v1 * s1 + v2 * s2
*/
- public final static double[] timesPlusTimes(final double[] v1, final double s1, final double[] v2, final double s2) {
- assert (v1.length == v2.length) : "Vector dimensions must agree.";
+ public static final double[] timesPlusTimes(final double[] v1, final double s1, final double[] v2, final double s2) {
+ assert (v1.length == v2.length) : ERR_VEC_DIMENSIONS;
final double[] result = new double[v1.length];
- for(int i = 0; i < result.length; i++) {
+ for (int i = 0; i < result.length; i++) {
result[i] = v1[i] * s1 + v2[i] * s2;
}
return result;
@@ -182,9 +201,9 @@ public final class VMath {
* @param v2 second vector
* @return v1 = v1 + v2
*/
- public final static double[] plusEquals(final double[] v1, final double[] v2) {
- assert (v1.length == v2.length) : "Vector dimensions must agree.";
- for(int i = 0; i < v1.length; i++) {
+ public static final double[] plusEquals(final double[] v1, final double[] v2) {
+ assert (v1.length == v2.length) : ERR_VEC_DIMENSIONS;
+ for (int i = 0; i < v1.length; i++) {
v1[i] += v2[i];
}
return v1;
@@ -198,9 +217,9 @@ public final class VMath {
* @param s2 scalar vor v2
* @return v1 = v1 + v2 * s2
*/
- public final static double[] plusTimesEquals(final double[] v1, final double[] v2, final double s2) {
- assert (v1.length == v2.length) : "Vector dimensions must agree.";
- for(int i = 0; i < v1.length; i++) {
+ public static final double[] plusTimesEquals(final double[] v1, final double[] v2, final double s2) {
+ assert (v1.length == v2.length) : ERR_VEC_DIMENSIONS;
+ for (int i = 0; i < v1.length; i++) {
v1[i] += s2 * v2[i];
}
return v1;
@@ -214,9 +233,9 @@ public final class VMath {
* @param v2 another vector
* @return v1 = v1 * s1 + v2
*/
- public final static double[] timesPlusEquals(final double[] v1, final double s1, final double[] v2) {
- assert (v1.length == v2.length) : "Vector dimensions must agree.";
- for(int i = 0; i < v1.length; i++) {
+ public static final double[] timesPlusEquals(final double[] v1, final double s1, final double[] v2) {
+ assert (v1.length == v2.length) : ERR_VEC_DIMENSIONS;
+ for (int i = 0; i < v1.length; i++) {
v1[i] = v1[i] * s1 + v2[i];
}
return v1;
@@ -231,9 +250,9 @@ public final class VMath {
* @param s2 scalar for v2
* @return v1 = v1 * s1 + v2 * s2
*/
- public final static double[] timesPlusTimesEquals(final double[] v1, final double s1, final double[] v2, final double s2) {
- assert (v1.length == v2.length) : "Vector dimensions must agree.";
- for(int i = 0; i < v1.length; i++) {
+ public static final double[] timesPlusTimesEquals(final double[] v1, final double s1, final double[] v2, final double s2) {
+ assert (v1.length == v2.length) : ERR_VEC_DIMENSIONS;
+ for (int i = 0; i < v1.length; i++) {
v1[i] = v1[i] * s1 + v2[i] * s2;
}
return v1;
@@ -246,9 +265,9 @@ public final class VMath {
* @param d value to add
* @return v1 + d
*/
- public final static double[] plus(final double[] v1, final double d) {
+ public static final double[] plus(final double[] v1, final double d) {
final double[] result = new double[v1.length];
- for(int i = 0; i < result.length; i++) {
+ for (int i = 0; i < result.length; i++) {
result[i] = v1[i] + d;
}
return result;
@@ -261,8 +280,8 @@ public final class VMath {
* @param d value to add
* @return Modified vector
*/
- public final static double[] plusEquals(final double[] v1, final double d) {
- for(int i = 0; i < v1.length; i++) {
+ public static final double[] plusEquals(final double[] v1, final double d) {
+ for (int i = 0; i < v1.length; i++) {
v1[i] += d;
}
return v1;
@@ -275,9 +294,9 @@ public final class VMath {
* @param v2 the vector to be subtracted from this vector
* @return v1 - v2
*/
- public final static double[] minus(final double[] v1, final double[] v2) {
+ public static final double[] minus(final double[] v1, final double[] v2) {
final double[] sub = new double[v1.length];
- for(int i = 0; i < v1.length; i++) {
+ for (int i = 0; i < v1.length; i++) {
sub[i] = v1[i] - v2[i];
}
return sub;
@@ -291,9 +310,9 @@ public final class VMath {
* @param s2 the scaling factor for v2
* @return v1 - v2 * s2
*/
- public final static double[] minusTimes(final double[] v1, final double[] v2, final double s2) {
+ public static final double[] minusTimes(final double[] v1, final double[] v2, final double s2) {
final double[] sub = new double[v1.length];
- for(int i = 0; i < v1.length; i++) {
+ for (int i = 0; i < v1.length; i++) {
sub[i] = v1[i] - v2[i] * s2;
}
return sub;
@@ -307,9 +326,9 @@ public final class VMath {
* @param v2 the vector to be subtracted from this vector
* @return v1 * s1 - v2
*/
- public final static double[] timesMinus(final double[] v1, final double s1, final double[] v2) {
+ public static final double[] timesMinus(final double[] v1, final double s1, final double[] v2) {
final double[] sub = new double[v1.length];
- for(int i = 0; i < v1.length; i++) {
+ for (int i = 0; i < v1.length; i++) {
sub[i] = v1[i] * s1 - v2[i];
}
return sub;
@@ -324,9 +343,9 @@ public final class VMath {
* @param s2 the scaling factor for v2
* @return v1 * s1 - v2 * s2
*/
- public final static double[] timesMinusTimes(final double[] v1, final double s1, final double[] v2, final double s2) {
+ public static final double[] timesMinusTimes(final double[] v1, final double s1, final double[] v2, final double s2) {
final double[] sub = new double[v1.length];
- for(int i = 0; i < v1.length; i++) {
+ for (int i = 0; i < v1.length; i++) {
sub[i] = v1[i] * s1 - v2[i] * s2;
}
return sub;
@@ -339,9 +358,9 @@ public final class VMath {
* @param v2 another vector
* @return v1 = v1 - v2
*/
- public final static double[] minusEquals(final double[] v1, final double[] v2) {
- assert (v1.length == v2.length) : "Vector dimensions must agree.";
- for(int i = 0; i < v1.length; i++) {
+ public static final double[] minusEquals(final double[] v1, final double[] v2) {
+ assert (v1.length == v2.length) : ERR_VEC_DIMENSIONS;
+ for (int i = 0; i < v1.length; i++) {
v1[i] -= v2[i];
}
return v1;
@@ -355,9 +374,9 @@ public final class VMath {
* @param s2 scalar for v2
* @return v1 = v1 - v2 * s2
*/
- public final static double[] minusTimesEquals(final double[] v1, final double[] v2, final double s2) {
- assert (v1.length == v2.length) : "Vector dimensions must agree.";
- for(int i = 0; i < v1.length; i++) {
+ public static final double[] minusTimesEquals(final double[] v1, final double[] v2, final double s2) {
+ assert (v1.length == v2.length) : ERR_VEC_DIMENSIONS;
+ for (int i = 0; i < v1.length; i++) {
v1[i] -= v2[i] * s2;
}
return v1;
@@ -371,9 +390,9 @@ public final class VMath {
* @param v2 another vector
* @return v1 = v1 * s1 - v2
*/
- public final static double[] timesMinusEquals(final double[] v1, final double s1, final double[] v2) {
- assert (v1.length == v2.length) : "Vector dimensions must agree.";
- for(int i = 0; i < v1.length; i++) {
+ public static final double[] timesMinusEquals(final double[] v1, final double s1, final double[] v2) {
+ assert (v1.length == v2.length) : ERR_VEC_DIMENSIONS;
+ for (int i = 0; i < v1.length; i++) {
v1[i] = v1[i] * s1 - v2[i];
}
return v1;
@@ -388,9 +407,9 @@ public final class VMath {
* @param s2 Scalar
* @return v1 = v1 * s1 - v2 * s2
*/
- public final static double[] timesMinusTimesEquals(final double[] v1, final double s1, final double[] v2, final double s2) {
- assert (v1.length == v2.length) : "Vector dimensions must agree.";
- for(int i = 0; i < v1.length; i++) {
+ public static final double[] timesMinusTimesEquals(final double[] v1, final double s1, final double[] v2, final double s2) {
+ assert (v1.length == v2.length) : ERR_VEC_DIMENSIONS;
+ for (int i = 0; i < v1.length; i++) {
v1[i] = v1[i] * s1 - v2[i] * s2;
}
return v1;
@@ -403,9 +422,9 @@ public final class VMath {
* @param d Value to subtract
* @return v1 - d
*/
- public final static double[] minus(final double[] v1, final double d) {
+ public static final double[] minus(final double[] v1, final double d) {
final double[] result = new double[v1.length];
- for(int i = 0; i < v1.length; i++) {
+ for (int i = 0; i < v1.length; i++) {
result[i] = v1[i] - d;
}
return result;
@@ -418,8 +437,8 @@ public final class VMath {
* @param d Value to subtract
* @return v1 = v1 - d
*/
- public final static double[] minusEquals(final double[] v1, final double d) {
- for(int i = 0; i < v1.length; i++) {
+ public static final double[] minusEquals(final double[] v1, final double d) {
+ for (int i = 0; i < v1.length; i++) {
v1[i] -= d;
}
return v1;
@@ -432,9 +451,9 @@ public final class VMath {
* @param s1 the scalar to be multiplied
* @return v1 * s1
*/
- public final static double[] times(final double[] v1, final double s1) {
+ public static final double[] times(final double[] v1, final double s1) {
final double[] v = new double[v1.length];
- for(int i = 0; i < v1.length; i++) {
+ for (int i = 0; i < v1.length; i++) {
v[i] = v1[i] * s1;
}
return v;
@@ -447,8 +466,8 @@ public final class VMath {
* @param s scalar
* @return v1 = v1 * s1
*/
- public final static double[] timesEquals(final double[] v1, final double s) {
- for(int i = 0; i < v1.length; i++) {
+ public static final double[] timesEquals(final double[] v1, final double s) {
+ for (int i = 0; i < v1.length; i++) {
v1[i] *= s;
}
return v1;
@@ -461,12 +480,12 @@ public final class VMath {
* @param m2 other matrix
* @return Matrix product, v1 * m2
*/
- public final static double[][] times(final double[] v1, final double[][] m2) {
- assert (m2.length == 1) : "Matrix inner dimensions must agree.";
+ public static final double[][] times(final double[] v1, final double[][] m2) {
+ assert (m2.length == 1) : ERR_MATRIX_INNERDIM;
final int columndimension = m2[0].length;
final double[][] re = new double[v1.length][columndimension];
- for(int j = 0; j < columndimension; j++) {
- for(int i = 0; i < v1.length; i++) {
+ for (int j = 0; j < columndimension; j++) {
+ for (int i = 0; i < v1.length; i++) {
re[i][j] = v1[i] * m2[0][j];
}
}
@@ -480,13 +499,13 @@ public final class VMath {
* @param m2 other matrix
* @return Matrix product, v1<sup>T</sup> * m2
*/
- public final static double[][] transposeTimes(final double[] v1, final double[][] m2) {
- assert (m2.length == v1.length) : "Matrix inner dimensions must agree.";
+ public static final double[][] transposeTimes(final double[] v1, final double[][] m2) {
+ assert (m2.length == v1.length) : ERR_MATRIX_INNERDIM;
final int columndimension = m2[0].length;
final double[][] re = new double[1][columndimension];
- for(int j = 0; j < columndimension; j++) {
+ for (int j = 0; j < columndimension; j++) {
double s = 0;
- for(int k = 0; k < v1.length; k++) {
+ for (int k = 0; k < v1.length; k++) {
s += v1[k] * m2[k][j];
}
re[0][j] = s;
@@ -501,10 +520,10 @@ public final class VMath {
* @param v2 other vector
* @return Matrix product, v1<sup>T</sup> * v2
*/
- public final static double transposeTimes(final double[] v1, final double[] v2) {
- assert (v2.length == v1.length) : "Matrix inner dimensions must agree.";
+ public static final double transposeTimes(final double[] v1, final double[] v2) {
+ assert (v2.length == v1.length) : ERR_MATRIX_INNERDIM;
double s = 0;
- for(int k = 0; k < v1.length; k++) {
+ for (int k = 0; k < v1.length; k++) {
s += v1[k] * v2[k];
}
return s;
@@ -517,12 +536,12 @@ public final class VMath {
* @param m2 other matrix
* @return Matrix product, v1 * m2^T
*/
- public final static double[][] timesTranspose(final double[] v1, final double[][] m2) {
- assert (m2[0].length == 1) : "Matrix inner dimensions must agree.";
+ public static final double[][] timesTranspose(final double[] v1, final double[][] m2) {
+ assert (m2[0].length == 1) : ERR_MATRIX_INNERDIM;
final double[][] re = new double[v1.length][m2.length];
- for(int j = 0; j < m2.length; j++) {
- for(int i = 0; i < v1.length; i++) {
+ for (int j = 0; j < m2.length; j++) {
+ for (int i = 0; i < v1.length; i++) {
re[i][j] = v1[i] * m2[j][0];
}
}
@@ -536,10 +555,10 @@ public final class VMath {
* @param v2 other vector
* @return Matrix product, v1 * v2^T
*/
- public final static double[][] timesTranspose(final double[] v1, final double[] v2) {
+ public static final double[][] timesTranspose(final double[] v1, final double[] v2) {
final double[][] re = new double[v1.length][v2.length];
- for(int j = 0; j < v2.length; j++) {
- for(int i = 0; i < v1.length; i++) {
+ for (int j = 0; j < v2.length; j++) {
+ for (int i = 0; i < v1.length; i++) {
re[i][j] = v1[i] * v2[j];
}
}
@@ -547,7 +566,7 @@ public final class VMath {
}
/**
- * Returns the scalar product of this vector and the specified vector v.
+ * Returns the scalar product (dot product) of this vector and the specified vector v.
*
* This is the same as transposeTimes.
*
@@ -555,10 +574,10 @@ public final class VMath {
* @param v2 other vector
* @return double the scalar product of vectors v1 and v2
*/
- public final static double scalarProduct(final double[] v1, final double[] v2) {
- assert (v1.length == v2.length) : "Vector dimensions must agree.";
+ public static final double scalarProduct(final double[] v1, final double[] v2) {
+ assert (v1.length == v2.length) : ERR_VEC_DIMENSIONS;
double scalarProduct = 0.0;
- for(int row = 0; row < v1.length; row++) {
+ for (int row = 0; row < v1.length; row++) {
scalarProduct += v1[row] * v2[row];
}
return scalarProduct;
@@ -570,9 +589,9 @@ public final class VMath {
* @param v1 vector
* @return euclidean length of this vector
*/
- public final static double euclideanLength(final double[] v1) {
+ public static final double euclideanLength(final double[] v1) {
double acc = 0.0;
- for(int row = 0; row < v1.length; row++) {
+ for (int row = 0; row < v1.length; row++) {
final double v = v1[row];
acc += v * v;
}
@@ -585,11 +604,11 @@ public final class VMath {
* @param v1 vector
* @return normalized copy of v1
*/
- public final static double[] normalize(final double[] v1) {
+ public static final double[] normalize(final double[] v1) {
double norm = euclideanLength(v1);
double[] re = new double[v1.length];
- if(norm != 0) {
- for(int row = 0; row < v1.length; row++) {
+ if (norm != 0) {
+ for (int row = 0; row < v1.length; row++) {
re[row] = v1[row] / norm;
}
}
@@ -602,10 +621,10 @@ public final class VMath {
* @param v1 vector
* @return normalized v1
*/
- public final static double[] normalizeEquals(final double[] v1) {
+ public static final double[] normalizeEquals(final double[] v1) {
double norm = euclideanLength(v1);
- if(norm != 0) {
- for(int row = 0; row < v1.length; row++) {
+ if (norm != 0) {
+ for (int row = 0; row < v1.length; row++) {
v1[row] /= norm;
}
}
@@ -619,12 +638,12 @@ public final class VMath {
* @param m2 the subspace matrix
* @return the projection of p into the subspace formed by v
*/
- public final static double[] project(final double[] v1, final double[][] m2) {
- assert (v1.length == m2.length) : "v1 and m2 differ in dimensionality!";
+ public static final double[] project(final double[] v1, final double[][] m2) {
+ assert (v1.length == m2.length) : ERR_DIMENSIONS;
final int columndimension = m2[0].length;
double[] sum = new double[v1.length];
- for(int i = 0; i < columndimension; i++) {
+ for (int i = 0; i < columndimension; i++) {
// TODO: optimize - copy less.
double[] v_i = getCol(m2, i);
plusTimesEquals(sum, v_i, scalarProduct(v1, v_i));
@@ -638,7 +657,7 @@ public final class VMath {
* @param v1 elements
* @return hash code
*/
- public final static int hashCode(final double[] v1) {
+ public static final int hashCode(final double[] v1) {
return Arrays.hashCode(v1);
}
@@ -649,7 +668,7 @@ public final class VMath {
* @param v2 second vector
* @return comparison result
*/
- public final static boolean equals(final double[] v1, final double[] v2) {
+ public static final boolean equals(final double[] v1, final double[] v2) {
return Arrays.equals(v1, v2);
}
@@ -658,7 +677,7 @@ public final class VMath {
*
* @param v1 vector
*/
- public final static void clear(final double[] v1) {
+ public static final void clear(final double[] v1) {
Arrays.fill(v1, 0.0);
}
@@ -668,7 +687,7 @@ public final class VMath {
* @param v1 first vector
* @return modified v1, rotated by 90 degrees
*/
- public final static double[] rotate90Equals(final double[] v1) {
+ public static final double[] rotate90Equals(final double[] v1) {
assert (v1.length == 2) : "rotate90Equals is only valid for 2d vectors.";
double temp = v1[0];
v1[0] = v1[1];
@@ -684,9 +703,9 @@ public final class VMath {
* @param dim the dimensionality of the unit matrix
* @return the unit matrix of the specified dimension
*/
- public final static double[][] unitMatrix(final int dim) {
+ public static final double[][] unitMatrix(final int dim) {
final double[][] e = new double[dim][dim];
- for(int i = 0; i < dim; i++) {
+ for (int i = 0; i < dim; i++) {
e[i][i] = 1;
}
return e;
@@ -698,7 +717,7 @@ public final class VMath {
* @param dim the dimensionality of the unit matrix
* @return the zero matrix of the specified dimension
*/
- public final static double[][] zeroMatrix(final int dim) {
+ public static final double[][] zeroMatrix(final int dim) {
final double[][] z = new double[dim][dim];
return z;
}
@@ -710,10 +729,10 @@ public final class VMath {
* @param n Number of columns.
* @return An m-by-n matrix with uniformly distributed random elements.
*/
- public final static double[][] random(final int m, final int n) {
+ public static final double[][] random(final int m, final int n) {
final double[][] A = new double[m][n];
- for(int i = 0; i < m; i++) {
- for(int j = 0; j < n; j++) {
+ for (int i = 0; i < m; i++) {
+ for (int j = 0; j < n; j++) {
A[i][j] = Math.random();
}
}
@@ -727,9 +746,9 @@ public final class VMath {
* @param n Number of columns.
* @return An m-by-n matrix with ones on the diagonal and zeros elsewhere.
*/
- public final static double[][] identity(final int m, final int n) {
+ public static final double[][] identity(final int m, final int n) {
final double[][] A = new double[m][n];
- for(int i = 0; i < Math.min(m, n); i++) {
+ for (int i = 0; i < Math.min(m, n); i++) {
A[i][i] = 1.0;
}
return A;
@@ -742,9 +761,9 @@ public final class VMath {
* @param v1 the values on the diagonal
* @return the resulting matrix
*/
- public final static double[][] diagonal(final double[] v1) {
+ public static final double[][] diagonal(final double[] v1) {
final double[][] result = new double[v1.length][v1.length];
- for(int i = 0; i < v1.length; i++) {
+ for (int i = 0; i < v1.length; i++) {
result[i][i] = v1[i];
}
return result;
@@ -759,7 +778,7 @@ public final class VMath {
public static final double[][] copy(final double[][] m1) {
final int columndimension = m1[0].length;
final double[][] X = new double[m1.length][columndimension];
- for(int i = 0; i < m1.length; i++) {
+ for (int i = 0; i < m1.length; i++) {
System.arraycopy(m1[i], 0, X[i], 0, columndimension);
}
return X;
@@ -774,10 +793,8 @@ public final class VMath {
public static final double[] rowPackedCopy(final double[][] m1) {
final int columndimension = m1[0].length;
double[] vals = new double[m1.length * columndimension];
- for(int i = 0; i < m1.length; i++) {
- for(int j = 0; j < columndimension; j++) {
- vals[i * columndimension + j] = m1[i][j];
- }
+ for (int i = 0; i < m1.length; i++) {
+ System.arraycopy(m1[i], 0, vals, i * columndimension, columndimension);
}
return vals;
}
@@ -791,8 +808,8 @@ public final class VMath {
public static final double[] columnPackedCopy(final double[][] m1) {
final int columndimension = m1[0].length;
final double[] vals = new double[m1.length * columndimension];
- for(int i = 0; i < m1.length; i++) {
- for(int j = 0; j < columndimension; j++) {
+ for (int i = 0; i < m1.length; i++) {
+ for (int j = 0; j < columndimension; j++) {
vals[i + j * m1.length] = m1[i][j];
}
}
@@ -811,10 +828,8 @@ public final class VMath {
*/
public static final double[][] getMatrix(final double[][] m1, final int r0, final int r1, final int c0, final int c1) {
final double[][] X = new double[r1 - r0 + 1][c1 - c0 + 1];
- for(int i = r0; i <= r1; i++) {
- for(int j = c0; j <= c1; j++) {
- X[i - r0][j - c0] = m1[i][j];
- }
+ for (int i = r0; i <= r1; i++) {
+ System.arraycopy(m1[i], c0, X[i - r0], 0, c1 - c0 + 1);
}
return X;
}
@@ -829,8 +844,8 @@ public final class VMath {
*/
public static final double[][] getMatrix(final double[][] m1, final int[] r, final int[] c) {
final double[][] X = new double[r.length][c.length];
- for(int i = 0; i < r.length; i++) {
- for(int j = 0; j < c.length; j++) {
+ for (int i = 0; i < r.length; i++) {
+ for (int j = 0; j < c.length; j++) {
X[i][j] = m1[r[i]][c[j]];
}
}
@@ -848,10 +863,8 @@ public final class VMath {
*/
public static final double[][] getMatrix(final double[][] m1, final int[] r, final int c0, final int c1) {
final double[][] X = new double[r.length][c1 - c0 + 1];
- for(int i = 0; i < r.length; i++) {
- for(int j = c0; j <= c1; j++) {
- X[i][j - c0] = m1[r[i]][j];
- }
+ for (int i = 0; i < r.length; i++) {
+ System.arraycopy(m1[r[i]], c0, X[i], 0, c1 - c0 + 1);
}
return X;
}
@@ -867,8 +880,8 @@ public final class VMath {
*/
public static final double[][] getMatrix(final double[][] m1, final int r0, final int r1, final int[] c) {
final double[][] X = new double[r1 - r0 + 1][c.length];
- for(int i = r0; i <= r1; i++) {
- for(int j = 0; j < c.length; j++) {
+ for (int i = r0; i <= r1; i++) {
+ for (int j = 0; j < c.length; j++) {
X[i - r0][j] = m1[i][c[j]];
}
}
@@ -886,10 +899,8 @@ public final class VMath {
* @param m2 New values for m1(r0:r1,c0:c1)
*/
public static final void setMatrix(final double[][] m1, final int r0, final int r1, final int c0, final int c1, final double[][] m2) {
- for(int i = r0; i <= r1; i++) {
- for(int j = c0; j <= c1; j++) {
- m1[i][j] = m2[i - r0][j - c0];
- }
+ for (int i = r0; i <= r1; i++) {
+ System.arraycopy(m2[i - r0], 0, m1[i], c0, c1 - c0 + 1);
}
}
@@ -902,8 +913,8 @@ public final class VMath {
* @param m2 New values for m1(r(:),c(:))
*/
public static final void setMatrix(final double[][] m1, final int[] r, final int[] c, final double[][] m2) {
- for(int i = 0; i < r.length; i++) {
- for(int j = 0; j < c.length; j++) {
+ for (int i = 0; i < r.length; i++) {
+ for (int j = 0; j < c.length; j++) {
m1[r[i]][c[j]] = m2[i][j];
}
}
@@ -919,10 +930,8 @@ public final class VMath {
* @param m2 New values for m1(r(:),c0:c1)
*/
public static final void setMatrix(final double[][] m1, final int[] r, final int c0, final int c1, final double[][] m2) {
- for(int i = 0; i < r.length; i++) {
- for(int j = c0; j <= c1; j++) {
- m1[r[i]][j] = m2[i][j - c0];
- }
+ for (int i = 0; i < r.length; i++) {
+ System.arraycopy(m2[i], 0, m1[r[i]], c0, c1 - c0 + 1);
}
}
@@ -936,8 +945,8 @@ public final class VMath {
* @param m2 New values for m1(r0:r1,c(:))
*/
public static final void setMatrix(final double[][] m1, final int r0, final int r1, final int[] c, final double[][] m2) {
- for(int i = r0; i <= r1; i++) {
- for(int j = 0; j < c.length; j++) {
+ for (int i = r0; i <= r1; i++) {
+ for (int j = 0; j < c.length; j++) {
m1[i][c[j]] = m2[i - r0][j];
}
}
@@ -963,10 +972,8 @@ public final class VMath {
*/
public static final void setRow(final double[][] m1, final int r, final double[] row) {
final int columndimension = getColumnDimensionality(m1);
- assert (row.length == columndimension) : "Matrix must consist of the same no of columns!";
- for(int i = 0; i < columndimension; i++) {
- m1[r][i] = row[i];
- }
+ assert (row.length == columndimension) : ERR_DIMENSIONS;
+ System.arraycopy(row, 0, m1[r], 0, columndimension);
}
/**
@@ -976,9 +983,9 @@ public final class VMath {
* @param col Column number
* @return Column
*/
- public final static double[] getCol(double[][] m1, int col) {
+ public static final double[] getCol(double[][] m1, int col) {
double[] ret = new double[m1.length];
- for(int i = 0; i < ret.length; i++) {
+ for (int i = 0; i < ret.length; i++) {
ret[i] = m1[i][col];
}
return ret;
@@ -992,8 +999,8 @@ public final class VMath {
* @param column the value of the column to be set
*/
public static final void setCol(final double[][] m1, final int c, final double[] column) {
- assert (column.length == m1.length) : "Matrix must consist of the same no of rows!";
- for(int i = 0; i < m1.length; i++) {
+ assert (column.length == m1.length) : ERR_DIMENSIONS;
+ for (int i = 0; i < m1.length; i++) {
m1[i][c] = column[i];
}
}
@@ -1007,8 +1014,8 @@ public final class VMath {
public static final double[][] transpose(final double[][] m1) {
final int columndimension = getColumnDimensionality(m1);
final double[][] re = new double[columndimension][m1.length];
- for(int i = 0; i < m1.length; i++) {
- for(int j = 0; j < columndimension; j++) {
+ for (int i = 0; i < m1.length; i++) {
+ for (int j = 0; j < columndimension; j++) {
re[j][i] = m1[i][j];
}
}
@@ -1047,9 +1054,9 @@ public final class VMath {
*/
public static final double[][] plusEquals(final double[][] m1, final double[][] m2) {
final int columndimension = getColumnDimensionality(m1);
- assert (getRowDimensionality(m1) == getRowDimensionality(m2) && columndimension == getColumnDimensionality(m2)) : "Matrix dimensions must agree.";
- for(int i = 0; i < m1.length; i++) {
- for(int j = 0; j < columndimension; j++) {
+ assert (getRowDimensionality(m1) == getRowDimensionality(m2) && columndimension == getColumnDimensionality(m2)) : ERR_MATRIX_DIMENSIONS;
+ for (int i = 0; i < m1.length; i++) {
+ for (int j = 0; j < columndimension; j++) {
m1[i][j] += m2[i][j];
}
}
@@ -1066,9 +1073,9 @@ public final class VMath {
*/
public static final double[][] plusTimesEquals(final double[][] m1, final double[][] m2, final double s2) {
final int columndimension = getColumnDimensionality(m1);
- assert (getRowDimensionality(m1) == getRowDimensionality(m2) && columndimension == getColumnDimensionality(m2)) : "Matrix dimensions must agree.";
- for(int i = 0; i < m1.length; i++) {
- for(int j = 0; j < columndimension; j++) {
+ assert (getRowDimensionality(m1) == getRowDimensionality(m2) && columndimension == getColumnDimensionality(m2)) : ERR_MATRIX_DIMENSIONS;
+ for (int i = 0; i < m1.length; i++) {
+ for (int j = 0; j < columndimension; j++) {
m1[i][j] += s2 * m2[i][j];
}
}
@@ -1107,9 +1114,9 @@ public final class VMath {
*/
public static final double[][] minusEquals(final double[][] m1, final double[][] m2) {
final int columndimension = getColumnDimensionality(m1);
- assert (getRowDimensionality(m1) == getRowDimensionality(m2) && columndimension == getColumnDimensionality(m2)) : "Matrix dimensions must agree.";
- for(int i = 0; i < m1.length; i++) {
- for(int j = 0; j < columndimension; j++) {
+ assert (getRowDimensionality(m1) == getRowDimensionality(m2) && columndimension == getColumnDimensionality(m2)) : ERR_MATRIX_DIMENSIONS;
+ for (int i = 0; i < m1.length; i++) {
+ for (int j = 0; j < columndimension; j++) {
m1[i][j] -= m2[i][j];
}
}
@@ -1125,11 +1132,11 @@ public final class VMath {
* @return m1 = m1 - s2 * m2, overwriting m1
*/
public static final double[][] minusTimesEquals(final double[][] m1, final double[][] m2, final double s2) {
- assert (getRowDimensionality(m1) == getRowDimensionality(m2) && getColumnDimensionality(m1) == getColumnDimensionality(m2)) : "Matrix dimensions must agree.";
- for(int i = 0; i < m1.length; i++) {
+ assert (getRowDimensionality(m1) == getRowDimensionality(m2) && getColumnDimensionality(m1) == getColumnDimensionality(m2)) : ERR_MATRIX_DIMENSIONS;
+ for (int i = 0; i < m1.length; i++) {
final double[] row1 = m1[i];
final double[] row2 = m2[i];
- for(int j = 0; j < row1.length; j++) {
+ for (int j = 0; j < row1.length; j++) {
row1[j] -= s2 * row2[j];
}
}
@@ -1155,9 +1162,9 @@ public final class VMath {
* @return m1 = s1 * m1, overwriting m1
*/
public static final double[][] timesEquals(final double[][] m1, final double s1) {
- for(int i = 0; i < m1.length; i++) {
+ for (int i = 0; i < m1.length; i++) {
final double[] row = m1[i];
- for(int j = 0; j < row.length; j++) {
+ for (int j = 0; j < row.length; j++) {
row[j] *= s1;
}
}
@@ -1175,21 +1182,21 @@ public final class VMath {
final int columndimension = getColumnDimensionality(m1);
final int bcolumndimension = getColumnDimensionality(m2);
// Optimized implementation, exploiting the storage layout
- assert (m2.length == columndimension) : "Matrix inner dimensions must agree: " + getRowDimensionality(m1) + "," + getColumnDimensionality(m1) + " * " + getRowDimensionality(m2) + "," + getColumnDimensionality(m2);
+ assert (m2.length == columndimension) : ERR_MATRIX_INNERDIM;
final double[][] r2 = new double[m1.length][bcolumndimension];
// Optimized ala Jama. jik order.
final double[] Bcolj = new double[columndimension];
- for(int j = 0; j < bcolumndimension; j++) {
+ for (int j = 0; j < bcolumndimension; j++) {
// Make a linear copy of column j from B
// TODO: use column getter from B?
- for(int k = 0; k < columndimension; k++) {
+ for (int k = 0; k < columndimension; k++) {
Bcolj[k] = m2[k][j];
}
// multiply it with each row from A
- for(int i = 0; i < m1.length; i++) {
+ for (int i = 0; i < m1.length; i++) {
final double[] Arowi = m1[i];
double s = 0;
- for(int k = 0; k < columndimension; k++) {
+ for (int k = 0; k < columndimension; k++) {
s += Arowi[k] * Bcolj[k];
}
r2[i][j] = s;
@@ -1206,13 +1213,13 @@ public final class VMath {
* @return Matrix product, m1 * v2
*/
public static final double[] times(final double[][] m1, final double[] v2) {
- assert (v2.length == getColumnDimensionality(m1)) : "Matrix inner dimensions must agree.";
+ assert (v2.length == getColumnDimensionality(m1)) : ERR_MATRIX_INNERDIM;
final double[] re = new double[m1.length];
// multiply it with each row from A
- for(int i = 0; i < m1.length; i++) {
+ for (int i = 0; i < m1.length; i++) {
final double[] Arowi = m1[i];
double s = 0;
- for(int k = 0; k < Arowi.length; k++) {
+ for (int k = 0; k < Arowi.length; k++) {
s += Arowi[k] * v2[k];
}
re[i] = s;
@@ -1229,12 +1236,12 @@ public final class VMath {
*/
public static final double[] transposeTimes(final double[][] m1, final double[] v2) {
final int columndimension = getColumnDimensionality(m1);
- assert (v2.length == m1.length) : "Matrix inner dimensions must agree.";
+ assert (v2.length == m1.length) : ERR_MATRIX_INNERDIM;
final double[] re = new double[columndimension];
// multiply it with each row from A
- for(int i = 0; i < columndimension; i++) {
+ for (int i = 0; i < columndimension; i++) {
double s = 0;
- for(int k = 0; k < m1.length; k++) {
+ for (int k = 0; k < m1.length; k++) {
s += m1[k][i] * v2[k];
}
re[i] = s;
@@ -1252,18 +1259,18 @@ public final class VMath {
public static final double[][] transposeTimes(final double[][] m1, final double[][] m2) {
final int coldim1 = getColumnDimensionality(m1);
final int coldim2 = getColumnDimensionality(m2);
- assert (m2.length == m1.length) : "Matrix inner dimensions must agree.";
+ assert (m2.length == m1.length) : ERR_MATRIX_INNERDIM;
final double[][] re = new double[coldim1][coldim2];
final double[] Bcolj = new double[m1.length];
- for(int j = 0; j < coldim2; j++) {
+ for (int j = 0; j < coldim2; j++) {
// Make a linear copy of column j from B
- for(int k = 0; k < m1.length; k++) {
+ for (int k = 0; k < m1.length; k++) {
Bcolj[k] = m2[k][j];
}
// multiply it with each row from A
- for(int i = 0; i < coldim1; i++) {
+ for (int i = 0; i < coldim1; i++) {
double s = 0;
- for(int k = 0; k < m1.length; k++) {
+ for (int k = 0; k < m1.length; k++) {
s += m1[k][i] * Bcolj[k];
}
re[i][j] = s;
@@ -1273,6 +1280,27 @@ public final class VMath {
}
/**
+ * Linear algebraic matrix multiplication, a<sup>T</sup> * B * c
+ *
+ * @param B matrix
+ * @param c vector on the right
+ * @return Matrix product, a<sup>T</sup> * B * c
+ */
+ public static double transposeTimesTimes(final double[] a, final double[][] B, final double[] c) {
+ assert (B.length == a.length) : ERR_MATRIX_INNERDIM;
+ double sum = 0.0;
+ for (int j = 0; j < B[0].length; j++) {
+ // multiply it with each row from A
+ double s = 0;
+ for (int k = 0; k < a.length; k++) {
+ s += a[k] * B[k][j];
+ }
+ sum += s * c[j];
+ }
+ return sum;
+ }
+
+ /**
* Linear algebraic matrix multiplication, m1 * m2^T
*
* @param m1 Input matrix
@@ -1280,15 +1308,15 @@ public final class VMath {
* @return Matrix product, m1 * m2^T
*/
public static final double[][] timesTranspose(final double[][] m1, final double[][] m2) {
- assert (getColumnDimensionality(m2) == getColumnDimensionality(m1)) : "Matrix inner dimensions must agree.";
+ assert (getColumnDimensionality(m2) == getColumnDimensionality(m1)) : ERR_MATRIX_INNERDIM;
final double[][] re = new double[m1.length][m2.length];
- for(int j = 0; j < re.length; j++) {
+ for (int j = 0; j < re.length; j++) {
final double[] Browj = m2[j];
// multiply it with each row from A
- for(int i = 0; i < m1.length; i++) {
+ for (int i = 0; i < m1.length; i++) {
final double[] Arowi = m1[i];
double s = 0;
- for(int k = 0; k < Browj.length; k++) {
+ for (int k = 0; k < Browj.length; k++) {
s += Arowi[k] * Browj[k];
}
re[i][j] = s;
@@ -1306,21 +1334,21 @@ public final class VMath {
*/
public static final double[][] transposeTimesTranspose(final double[][] m1, final double[][] m2) {
// Optimized implementation, exploiting the storage layout
- assert (m1.length == getColumnDimensionality(m2)) : "Matrix inner dimensions must agree: " + getRowDimensionality(m1) + "," + getColumnDimensionality(m1) + " * " + getRowDimensionality(m2) + "," + getColumnDimensionality(m2);
+ assert (m1.length == getColumnDimensionality(m2)) : ERR_MATRIX_INNERDIM;
final double[][] re = new double[getColumnDimensionality(m1)][m2.length];
// Optimized ala Jama. jik order.
final double[] Acolj = new double[m1.length];
- for(int j = 0; j < re.length; j++) {
+ for (int j = 0; j < re.length; j++) {
// Make a linear copy of column j from B
- for(int k = 0; k < m1.length; k++) {
+ for (int k = 0; k < m1.length; k++) {
Acolj[k] = m1[k][j];
}
final double[] Xrow = re[j];
// multiply it with each row from A
- for(int i = 0; i < m2.length; i++) {
+ for (int i = 0; i < m2.length; i++) {
final double[] Browi = m2[i];
double s = 0;
- for(int k = 0; k < m1.length; k++) {
+ for (int k = 0; k < m1.length; k++) {
s += Browi[k] * Acolj[k];
}
Xrow[i] = s;
@@ -1335,10 +1363,10 @@ public final class VMath {
* @param m1 Input matrix
* @return values on the diagonal of the Matrix
*/
- public final static double[] getDiagonal(final double[][] m1) {
+ public static final double[] getDiagonal(final double[][] m1) {
final int dim = Math.min(getColumnDimensionality(m1), m1.length);
final double[] diagonal = new double[dim];
- for(int i = 0; i < dim; i++) {
+ for (int i = 0; i < dim; i++) {
diagonal[i] = m1[i][i];
}
return diagonal;
@@ -1349,20 +1377,19 @@ public final class VMath {
*
* @param m1 Input matrix
*/
- public final static void normalizeColumns(final double[][] m1) {
+ public static final void normalizeColumns(final double[][] m1) {
final int columndimension = getColumnDimensionality(m1);
- for(int col = 0; col < columndimension; col++) {
+ for (int col = 0; col < columndimension; col++) {
double norm = 0.0;
- for(int row = 0; row < m1.length; row++) {
+ for (int row = 0; row < m1.length; row++) {
norm = norm + (m1[row][col] * m1[row][col]);
}
norm = Math.sqrt(norm);
- if(norm != 0) {
- for(int row = 0; row < m1.length; row++) {
+ if (norm != 0) {
+ for (int row = 0; row < m1.length; row++) {
m1[row][col] /= norm;
}
- }
- else {
+ } else {
// TODO: else: throw an exception?
}
}
@@ -1382,12 +1409,11 @@ public final class VMath {
final int rcolumndimension = columndimension + ccolumndimension;
final double[][] result = new double[m1.length][rcolumndimension];
- for(int i = 0; i < rcolumndimension; i++) {
+ for (int i = 0; i < rcolumndimension; i++) {
// FIXME: optimize - excess copying!
- if(i < columndimension) {
+ if (i < columndimension) {
setCol(result, i, getCol(m1, i));
- }
- else {
+ } else {
setCol(result, i, getCol(m2, i - columndimension));
}
}
@@ -1405,10 +1431,10 @@ public final class VMath {
double[][] v = copy(m1);
// FIXME: optimize - excess copying!
- for(int i = 1; i < columndimension; i++) {
+ for (int i = 1; i < columndimension; i++) {
final double[] u_i = getCol(m1, i);
final double[] sum = new double[m1.length];
- for(int j = 0; j < i; j++) {
+ for (int j = 0; j < i; j++) {
final double[] v_j = getCol(v, j);
double scalar = scalarProduct(u_i, v_j) / scalarProduct(v_j, v_j);
plusEquals(sum, times(v_j, scalar));
@@ -1452,25 +1478,25 @@ public final class VMath {
* @return true if delta smaller than maximum
*/
public static final boolean almostEquals(final double[][] m1, final double[][] m2, final double maxdelta) {
- if(m1 == m2) {
+ if (m1 == m2) {
return true;
}
- if(m2 == null) {
+ if (m2 == null) {
return false;
}
- if(m1.getClass() != m2.getClass()) {
+ if (m1.getClass() != m2.getClass()) {
return false;
}
- if(m1.length != m2.length) {
+ if (m1.length != m2.length) {
return false;
}
final int columndimension = getColumnDimensionality(m1);
- if(columndimension != getColumnDimensionality(m2)) {
+ if (columndimension != getColumnDimensionality(m2)) {
return false;
}
- for(int i = 0; i < m1.length; i++) {
- for(int j = 0; j < columndimension; j++) {
- if(Math.abs(m1[i][j] - m2[i][j]) > maxdelta) {
+ for (int i = 0; i < m1.length; i++) {
+ for (int j = 0; j < columndimension; j++) {
+ if (Math.abs(m1[i][j] - m2[i][j]) > maxdelta) {
return false;
}
}
@@ -1509,4 +1535,17 @@ public final class VMath {
public static final int getColumnDimensionality(final double[][] m1) {
return m1[0].length;
}
-} \ No newline at end of file
+
+ /**
+ * Cross product for 3d vectors, i.e. <code>vo = v1 x v2</code>
+ *
+ * @param vo Output vector
+ * @param v1 First input vector
+ * @param v2 Second input vector
+ */
+ public static void cross3D(double[] vo, double[] v1, double[] v2) {
+ vo[0] = (v1[1] * v2[2]) - (v1[2] * v2[1]);
+ vo[1] = (v1[2] * v2[0]) - (v1[0] * v2[2]);
+ vo[2] = (v1[0] * v2[1]) - (v1[1] * v2[0]);
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/Vector.java b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/Vector.java
index 0a674d87..b0e1c78e 100644
--- a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/Vector.java
+++ b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/Vector.java
@@ -27,8 +27,6 @@ import java.util.Arrays;
import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.utilities.FormatUtil;
-import de.lmu.ifi.dbs.elki.utilities.datastructures.arraylike.ArrayAdapter;
-import de.lmu.ifi.dbs.elki.utilities.datastructures.arraylike.NumberArrayAdapter;
/**
* Provides a vector object that encapsulates an m x 1 - matrix object.
@@ -37,7 +35,7 @@ import de.lmu.ifi.dbs.elki.utilities.datastructures.arraylike.NumberArrayAdapter
*
* @apiviz.landmark
*/
-public class Vector implements NumberVector<Vector, Double> {
+public class Vector implements NumberVector<Double> {
/**
* Array for internal storage of elements.
*
@@ -46,6 +44,21 @@ public class Vector implements NumberVector<Vector, Double> {
protected final double[] elements;
/**
+ * Error message (in assertions!) when vector dimensionalities do not agree.
+ */
+ public static final String ERR_VEC_DIMENSIONS = "Vector dimensions do not agree.";
+
+ /**
+ * Error message (in assertions!) when matrix dimensionalities do not agree.
+ */
+ public static final String ERR_MATRIX_INNERDIM = "Matrix inner dimensions do not agree.";
+
+ /**
+ * Error message (in assertions!) when dimensionalities do not agree.
+ */
+ private static final String ERR_DIMENSIONS = "Dimensionalities do not agree.";
+
+ /**
* Construct a vector from a given array.
*
* @param values array of doubles
@@ -64,7 +77,7 @@ public class Vector implements NumberVector<Vector, Double> {
}
/**
- * Returns a randomly created vector of length 1.0
+ * Returns a randomly created vector of length 1.0.
*
* @param dimensionality dimensionality
* @return the dimensionality of the vector
@@ -72,13 +85,13 @@ public class Vector implements NumberVector<Vector, Double> {
public static final Vector randomNormalizedVector(final int dimensionality) {
final Vector v = new Vector(dimensionality);
double norm = 0;
- while(norm <= 0) {
- for(int i = 0; i < dimensionality; i++) {
+ while (norm <= 0) {
+ for (int i = 0; i < dimensionality; i++) {
v.elements[i] = Math.random();
}
norm = v.euclideanLength();
}
- for(int row = 0; row < dimensionality; row++) {
+ for (int row = 0; row < dimensionality; row++) {
v.elements[row] /= norm;
}
return v;
@@ -106,9 +119,6 @@ public class Vector implements NumberVector<Vector, Double> {
return new Vector(elements.clone());
}
- /**
- * Clone the Vector object.
- */
@Override
public Vector clone() {
return this.copy();
@@ -173,9 +183,9 @@ public class Vector implements NumberVector<Vector, Double> {
* @return the resulting vector
*/
public final Vector plus(final Vector v) {
- assert (this.elements.length == v.elements.length) : "Vector dimensions must agree.";
+ assert (this.elements.length == v.elements.length) : ERR_VEC_DIMENSIONS;
final Vector result = new Vector(elements.length);
- for(int i = 0; i < elements.length; i++) {
+ for (int i = 0; i < elements.length; i++) {
result.elements[i] = elements[i] + v.elements[i];
}
return result;
@@ -190,39 +200,39 @@ public class Vector implements NumberVector<Vector, Double> {
* @return the resulting vector
*/
public final Vector plusTimes(final Vector v, final double s) {
- assert (this.elements.length == v.elements.length) : "Vector dimensions must agree.";
+ assert (this.elements.length == v.elements.length) : ERR_VEC_DIMENSIONS;
final Vector result = new Vector(elements.length);
- for(int i = 0; i < elements.length; i++) {
+ for (int i = 0; i < elements.length; i++) {
result.elements[i] = elements[i] + v.elements[i] * s;
}
return result;
}
/**
- * A = A + B
+ * a = a + b.
*
- * @param B another matrix
- * @return A + B in this Matrix
+ * @param b another vector
+ * @return a + b in this vector
*/
- public final Vector plusEquals(final Vector B) {
- assert (this.elements.length == B.elements.length) : "Vector dimensions must agree.";
- for(int i = 0; i < elements.length; i++) {
- elements[i] += B.elements[i];
+ public final Vector plusEquals(final Vector b) {
+ assert (this.elements.length == b.elements.length) : ERR_VEC_DIMENSIONS;
+ for (int i = 0; i < elements.length; i++) {
+ elements[i] += b.elements[i];
}
return this;
}
/**
- * A = A + s * B
+ * a = a + s * b.
*
- * @param B another matrix
+ * @param b another vector
* @param s Scalar
- * @return A + s * B in this Matrix
+ * @return a + s * b in this vector
*/
- public final Vector plusTimesEquals(final Vector B, final double s) {
- assert (this.elements.length == B.elements.length) : "Vector dimensions must agree.";
- for(int i = 0; i < elements.length; i++) {
- elements[i] += s * B.elements[i];
+ public final Vector plusTimesEquals(final Vector b, final double s) {
+ assert (this.elements.length == b.elements.length) : ERR_VEC_DIMENSIONS;
+ for (int i = 0; i < elements.length; i++) {
+ elements[i] += s * b.elements[i];
}
return this;
}
@@ -234,7 +244,7 @@ public class Vector implements NumberVector<Vector, Double> {
* @return Modified vector
*/
public final Vector plusEquals(final double d) {
- for(int i = 0; i < elements.length; i++) {
+ for (int i = 0; i < elements.length; i++) {
elements[i] += d;
}
return this;
@@ -248,7 +258,7 @@ public class Vector implements NumberVector<Vector, Double> {
*/
public final Vector minus(final Vector v) {
final Vector sub = new Vector(elements.length);
- for(int i = 0; i < elements.length; i++) {
+ for (int i = 0; i < elements.length; i++) {
sub.elements[i] = elements[i] - v.elements[i];
}
return sub;
@@ -263,37 +273,37 @@ public class Vector implements NumberVector<Vector, Double> {
*/
public final Vector minusTimes(final Vector v, final double s) {
final Vector sub = new Vector(elements.length);
- for(int i = 0; i < elements.length; i++) {
+ for (int i = 0; i < elements.length; i++) {
sub.elements[i] = elements[i] - v.elements[i] * s;
}
return sub;
}
/**
- * A = A - B
+ * a = a - b.
*
- * @param B another matrix
- * @return A - B in this Matrix
+ * @param b another vector
+ * @return a - b in this vector
*/
- public final Vector minusEquals(final Vector B) {
- assert (this.elements.length == B.elements.length) : "Vector dimensions must agree.";
- for(int i = 0; i < elements.length; i++) {
- elements[i] -= B.elements[i];
+ public final Vector minusEquals(final Vector b) {
+ assert (this.elements.length == b.elements.length) : ERR_VEC_DIMENSIONS;
+ for (int i = 0; i < elements.length; i++) {
+ elements[i] -= b.elements[i];
}
return this;
}
/**
- * A = A - s * B
+ * a = a - s * b.
*
- * @param B another matrix
+ * @param b another vector
* @param s Scalar
- * @return A - s * B in this Matrix
+ * @return a - s * b in this vector
*/
- public final Vector minusTimesEquals(final Vector B, final double s) {
- assert (this.elements.length == B.elements.length) : "Vector dimensions must agree.";
- for(int i = 0; i < elements.length; i++) {
- elements[i] -= s * B.elements[i];
+ public final Vector minusTimesEquals(final Vector b, final double s) {
+ assert (this.elements.length == b.elements.length) : ERR_VEC_DIMENSIONS;
+ for (int i = 0; i < elements.length; i++) {
+ elements[i] -= s * b.elements[i];
}
return this;
}
@@ -305,7 +315,7 @@ public class Vector implements NumberVector<Vector, Double> {
* @return Modified vector
*/
public final Vector minusEquals(final double d) {
- for(int i = 0; i < elements.length; i++) {
+ for (int i = 0; i < elements.length; i++) {
elements[i] -= d;
}
return this;
@@ -320,36 +330,36 @@ public class Vector implements NumberVector<Vector, Double> {
*/
public final Vector times(final double s) {
final Vector v = new Vector(elements.length);
- for(int i = 0; i < elements.length; i++) {
+ for (int i = 0; i < elements.length; i++) {
v.elements[i] = elements[i] * s;
}
return v;
}
/**
- * Multiply a matrix by a scalar in place, A = s*A
+ * Multiply a matrix by a scalar in place, A = s*A.
*
* @param s scalar
* @return replace A by s*A
*/
public final Vector timesEquals(final double s) {
- for(int i = 0; i < elements.length; i++) {
+ for (int i = 0; i < elements.length; i++) {
elements[i] *= s;
}
return this;
}
/**
- * Linear algebraic matrix multiplication, A * B
+ * Linear algebraic matrix multiplication, A * B.
*
* @param B another matrix
* @return Matrix product, A * B
*/
public final Matrix times(final Matrix B) {
- assert (B.elements.length == 1) : "Matrix inner dimensions must agree.";
+ assert (B.elements.length == 1) : ERR_MATRIX_INNERDIM;
final Matrix X = new Matrix(this.elements.length, B.columndimension);
- for(int j = 0; j < B.columndimension; j++) {
- for(int i = 0; i < this.elements.length; i++) {
+ for (int j = 0; j < B.columndimension; j++) {
+ for (int i = 0; i < this.elements.length; i++) {
X.elements[i][j] = elements[i] * B.elements[0][j];
}
}
@@ -357,18 +367,18 @@ public class Vector implements NumberVector<Vector, Double> {
}
/**
- * Linear algebraic matrix multiplication, A<sup>T</sup> * B
+ * Linear algebraic matrix multiplication, A<sup>T</sup> * B.
*
* @param B another matrix
* @return Matrix product, A<sup>T</sup> * B
*/
public final Matrix transposeTimes(final Matrix B) {
- assert (B.elements.length == this.elements.length) : "Matrix inner dimensions must agree.";
+ assert (B.elements.length == this.elements.length) : ERR_MATRIX_INNERDIM;
final Matrix X = new Matrix(1, B.columndimension);
- for(int j = 0; j < B.columndimension; j++) {
+ for (int j = 0; j < B.columndimension; j++) {
// multiply it with each row from A
double s = 0;
- for(int k = 0; k < this.elements.length; k++) {
+ for (int k = 0; k < this.elements.length; k++) {
s += this.elements[k] * B.elements[k][j];
}
X.elements[0][j] = s;
@@ -377,19 +387,19 @@ public class Vector implements NumberVector<Vector, Double> {
}
/**
- * Linear algebraic matrix multiplication, a<sup>T</sup> * B * c
+ * Linear algebraic matrix multiplication, a<sup>T</sup> * B * c.
*
* @param B matrix
* @param c vector on the right
- * @return Matrix product, a<sup>T</sup> * B
+ * @return Matrix product, a<sup>T</sup> * B * c
*/
public final double transposeTimesTimes(final Matrix B, final Vector c) {
- assert (B.elements.length == this.elements.length) : "Matrix inner dimensions must agree.";
+ assert (B.elements.length == this.elements.length) : ERR_MATRIX_INNERDIM;
double sum = 0.0;
- for(int j = 0; j < B.columndimension; j++) {
+ for (int j = 0; j < B.columndimension; j++) {
// multiply it with each row from A
double s = 0;
- for(int k = 0; k < this.elements.length; k++) {
+ for (int k = 0; k < this.elements.length; k++) {
s += this.elements[k] * B.elements[k][j];
}
sum += s * c.elements[j];
@@ -398,31 +408,31 @@ public class Vector implements NumberVector<Vector, Double> {
}
/**
- * Linear algebraic matrix multiplication, A<sup>T</sup> * B
+ * Linear algebraic matrix multiplication, A<sup>T</sup> * B.
*
* @param B another vector
* @return Matrix product, A<sup>T</sup> * B
*/
public final double transposeTimes(final Vector B) {
- assert (B.elements.length == this.elements.length) : "Matrix inner dimensions must agree.";
+ assert (B.elements.length == this.elements.length) : ERR_MATRIX_INNERDIM;
double s = 0;
- for(int k = 0; k < this.elements.length; k++) {
+ for (int k = 0; k < this.elements.length; k++) {
s += this.elements[k] * B.elements[k];
}
return s;
}
/**
- * Linear algebraic matrix multiplication, A * B^T
+ * Linear algebraic matrix multiplication, A * B^T.
*
* @param B another matrix
* @return Matrix product, A * B^T
*/
public final Matrix timesTranspose(final Matrix B) {
- assert (B.columndimension == 1) : "Matrix inner dimensions must agree.";
+ assert (B.columndimension == 1) : ERR_MATRIX_INNERDIM;
final Matrix X = new Matrix(this.elements.length, B.elements.length);
- for(int j = 0; j < B.elements.length; j++) {
- for(int i = 0; i < this.elements.length; i++) {
+ for (int j = 0; j < B.elements.length; j++) {
+ for (int i = 0; i < this.elements.length; i++) {
X.elements[i][j] = elements[i] * B.elements[j][0];
}
}
@@ -430,15 +440,15 @@ public class Vector implements NumberVector<Vector, Double> {
}
/**
- * Linear algebraic matrix multiplication, A * B^T
+ * Linear algebraic matrix multiplication, A * B^T.
*
* @param B another matrix
* @return Matrix product, A * B^T
*/
public final Matrix timesTranspose(final Vector B) {
final Matrix X = new Matrix(this.elements.length, B.elements.length);
- for(int j = 0; j < B.elements.length; j++) {
- for(int i = 0; i < this.elements.length; i++) {
+ for (int j = 0; j < B.elements.length; j++) {
+ for (int i = 0; i < this.elements.length; i++) {
X.elements[i][j] = elements[i] * B.elements[j];
}
}
@@ -452,7 +462,7 @@ public class Vector implements NumberVector<Vector, Double> {
*/
public final double euclideanLength() {
double acc = 0.0;
- for(int row = 0; row < elements.length; row++) {
+ for (int row = 0; row < elements.length; row++) {
final double v = elements[row];
acc += v * v;
}
@@ -461,11 +471,13 @@ public class Vector implements NumberVector<Vector, Double> {
/**
* Normalizes this vector to the length of 1.0.
+ *
+ * @return this vector
*/
public final Vector normalize() {
double norm = euclideanLength();
- if(norm != 0) {
- for(int row = 0; row < elements.length; row++) {
+ if (norm != 0) {
+ for (int row = 0; row < elements.length; row++) {
elements[row] /= norm;
}
}
@@ -480,9 +492,9 @@ public class Vector implements NumberVector<Vector, Double> {
* @return the projection of p into the subspace formed by v
*/
public final Vector projection(final Matrix v) {
- assert (elements.length == v.elements.length) : "p and v differ in row dimensionality!";
+ assert (elements.length == v.elements.length) : ERR_DIMENSIONS;
Vector sum = new Vector(elements.length);
- for(int i = 0; i < v.columndimension; i++) {
+ for (int i = 0; i < v.columndimension; i++) {
// TODO: optimize - copy less?
Vector v_i = v.getCol(i);
sum.plusTimesEquals(v_i, this.transposeTimes(v_i));
@@ -497,17 +509,17 @@ public class Vector implements NumberVector<Vector, Double> {
@Override
public boolean equals(Object obj) {
- if(this == obj) {
+ if (this == obj) {
return true;
}
- if(obj == null) {
+ if (obj == null) {
return false;
}
- if(getClass() != obj.getClass()) {
+ if (getClass() != obj.getClass()) {
return false;
}
final Vector other = (Vector) obj;
- if(this.elements.length != other.elements.length) {
+ if (this.elements.length != other.elements.length) {
return false;
}
return Arrays.equals(this.elements, other.elements);
@@ -525,7 +537,7 @@ public class Vector implements NumberVector<Vector, Double> {
/**
* Returns a string representation of this vector without adding extra
- * whitespace
+ * whitespace.
*
* @return a string representation of this vector.
*/
@@ -553,81 +565,71 @@ public class Vector implements NumberVector<Vector, Double> {
return this;
}
+ /**
+ * Cross product for 3d vectors, i.e. <code>this x other</code>
+ *
+ * @param other Other vector
+ * @return Cross product of this vector and the other vector
+ */
+ public Vector cross3D(Vector other) {
+ assert (elements.length == 3 && other.elements.length == 3);
+ Vector out = new Vector(3);
+ out.elements[0] = (elements[1] * other.elements[2]) - (elements[2] * other.elements[1]);
+ out.elements[1] = (elements[2] * other.elements[0]) - (elements[0] * other.elements[2]);
+ out.elements[2] = (elements[0] * other.elements[1]) - (elements[1] * other.elements[0]);
+ return out;
+ }
+
// ////// NumberVector API. A bit hackish. :-(
@Override
public double getMin(int dimension) {
- return elements[dimension - 1];
+ return elements[dimension];
}
@Override
public double getMax(int dimension) {
- return elements[dimension - 1];
+ return elements[dimension];
}
@Override
+ @Deprecated
public Double getValue(int dimension) {
- return elements[dimension - 1];
+ return Double.valueOf(elements[dimension]);
}
@Override
public double doubleValue(int dimension) {
- return elements[dimension - 1];
+ return elements[dimension];
}
@Override
public float floatValue(int dimension) {
- return (float) elements[dimension - 1];
+ return (float) elements[dimension];
}
@Override
public int intValue(int dimension) {
- return (int) elements[dimension - 1];
+ return (int) elements[dimension];
}
@Override
public long longValue(int dimension) {
- return (long) elements[dimension - 1];
+ return (long) elements[dimension];
}
@Override
public short shortValue(int dimension) {
- return (short) elements[dimension - 1];
+ return (short) elements[dimension];
}
@Override
public byte byteValue(int dimension) {
- return (byte) elements[dimension - 1];
+ return (byte) elements[dimension];
}
@Override
public Vector getColumnVector() {
return copy();
}
-
- @Override
- public Vector newNumberVector(double[] values) {
- return new Vector(values);
- }
-
- @Override
- public <A> Vector newNumberVector(A array, NumberArrayAdapter<?, A> adapter) {
- double[] raw = new double[adapter.size(array)];
- for(int i = 0; i < raw.length; i++) {
- raw[i] = adapter.getDouble(array, i);
- }
- return new Vector(raw);
- }
-
- @Override
- public <A> Vector newFeatureVector(A array, ArrayAdapter<Double, A> adapter) {
- if(adapter instanceof NumberArrayAdapter) {
- return newNumberVector(array, (NumberArrayAdapter<?, A>) adapter);
- }
- double[] raw = new double[adapter.size(array)];
- for(int i = 0; i < raw.length; i++) {
- raw[i] = adapter.get(array, i);
- }
- return new Vector(raw);
- }
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/fitting/FittingFunction.java b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/fitting/FittingFunction.java
index b04c64d5..9e77d810 100644
--- a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/fitting/FittingFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/fitting/FittingFunction.java
@@ -37,5 +37,5 @@ public interface FittingFunction {
* @param params Function parameters parameters
* @return Array consisting of y value and parameter gradients
*/
- public FittingFunctionResult eval(double x, double[] params);
+ FittingFunctionResult eval(double x, double[] params);
}
diff --git a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/fitting/LevenbergMarquardtMethod.java b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/fitting/LevenbergMarquardtMethod.java
index d87e1208..20200f99 100644
--- a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/fitting/LevenbergMarquardtMethod.java
+++ b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/fitting/LevenbergMarquardtMethod.java
@@ -248,10 +248,8 @@ public class LevenbergMarquardtMethod {
// build covmat out of fitting matrix by multiplying diagonal elements with
// 1+lambda
for(int i = 0; i < numfit; i++) {
- for(int j = 0; j < numfit; j++) {
- covmat[i][j] = alpha[i][j];
- }
- covmat[i][i] = alpha[i][i] * (1.0 + lambda);
+ System.arraycopy(alpha[i], 0, covmat[i], 0, numfit);
+ covmat[i][i] *= (1.0 + lambda);
}
// System.out.println("Chisq: " + chisq);
// System.out.println("Lambda: " + lambda);
@@ -289,14 +287,10 @@ public class LevenbergMarquardtMethod {
// keep modified covmat as new alpha matrix
// and da as new beta
for(int i = 0; i < numfit; i++) {
- for(int j = 0; j < numfit; j++) {
- alpha[i][j] = covmat[i][j];
- }
+ System.arraycopy(covmat[i], 0, alpha[i], 0, numfit);
beta[i] = deltaparams[i];
}
- for(int i = 0; i < numparams; i++) {
- params[i] = paramstry[i];
- }
+ System.arraycopy(paramstry, 0, params, 0, numparams);
}
else {
// TODO: Do we need a larger limit than MAX_VALUE?
diff --git a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/AbstractCovarianceMatrixBuilder.java b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/AbstractCovarianceMatrixBuilder.java
index c14986bd..3e12dc0e 100644
--- a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/AbstractCovarianceMatrixBuilder.java
+++ b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/AbstractCovarianceMatrixBuilder.java
@@ -23,15 +23,13 @@ package de.lmu.ifi.dbs.elki.math.linearalgebra.pca;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.util.Collection;
-import java.util.Iterator;
-
import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.ids.ModifiableDBIDs;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Matrix;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable;
@@ -44,7 +42,7 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable;
*
* @param <V> Vector class in use
*/
-public abstract class AbstractCovarianceMatrixBuilder<V extends NumberVector<? extends V, ?>> implements Parameterizable, CovarianceMatrixBuilder<V> {
+public abstract class AbstractCovarianceMatrixBuilder<V extends NumberVector<?>> implements Parameterizable, CovarianceMatrixBuilder<V> {
@Override
public Matrix processDatabase(Relation<? extends V> database) {
return processIds(database.getDBIDs(), database);
@@ -54,19 +52,17 @@ public abstract class AbstractCovarianceMatrixBuilder<V extends NumberVector<? e
public abstract Matrix processIds(DBIDs ids, Relation<? extends V> database);
@Override
- public <D extends NumberDistance<?, ?>> Matrix processQueryResults(Collection<? extends DistanceResultPair<D>> results, Relation<? extends V> database, int k) {
+ public <D extends NumberDistance<D, ?>> Matrix processQueryResults(DistanceDBIDResult<D> results, Relation<? extends V> database, int k) {
ModifiableDBIDs ids = DBIDUtil.newArray(k);
int have = 0;
- for(Iterator<? extends DistanceResultPair<D>> it = results.iterator(); it.hasNext() && have < k; have++) {
- ids.add(it.next().getDBID());
+ for(DBIDIter it = results.iter(); it.valid() && have < k; it.advance(), have++) {
+ ids.add(it);
}
return processIds(ids, database);
}
@Override
- final public <D extends NumberDistance<?, ?>> Matrix processQueryResults(Collection<? extends DistanceResultPair<D>> results, Relation<? extends V> database) {
+ public final <D extends NumberDistance<D, ?>> Matrix processQueryResults(DistanceDBIDResult<D> results, Relation<? extends V> database) {
return processQueryResults(results, database, results.size());
}
-
- // TODO: Allow KNNlist to avoid building the DBID array?
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/CompositeEigenPairFilter.java b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/CompositeEigenPairFilter.java
index bc9486c5..6a1c3898 100644
--- a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/CompositeEigenPairFilter.java
+++ b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/CompositeEigenPairFilter.java
@@ -42,7 +42,7 @@ public class CompositeEigenPairFilter implements EigenPairFilter {
/**
* The list of filters to use.
*/
- public static final OptionID EIGENPAIR_FILTER_COMPOSITE_LIST = OptionID.getOrCreateOptionID("pca.filter.composite.list", "A comma separated list of the class names of the filters to be used. " + "The specified filters will be applied sequentially in the given order.");
+ public static final OptionID EIGENPAIR_FILTER_COMPOSITE_LIST = new OptionID("pca.filter.composite.list", "A comma separated list of the class names of the filters to be used. " + "The specified filters will be applied sequentially in the given order.");
/**
* The filters to be applied.
diff --git a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/CovarianceMatrixBuilder.java b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/CovarianceMatrixBuilder.java
index 5098ffac..fce6dbb3 100644
--- a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/CovarianceMatrixBuilder.java
+++ b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/CovarianceMatrixBuilder.java
@@ -23,12 +23,10 @@ package de.lmu.ifi.dbs.elki.math.linearalgebra.pca;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.util.Collection;
-
import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Matrix;
@@ -39,44 +37,46 @@ import de.lmu.ifi.dbs.elki.math.linearalgebra.Matrix;
*
* @param <V> Vector base type
*/
-public interface CovarianceMatrixBuilder<V extends NumberVector<? extends V, ?>> {
+public interface CovarianceMatrixBuilder<V extends NumberVector<?>> {
/**
- * Compute Covariance Matrix for a complete database
+ * Compute Covariance Matrix for a complete database.
*
* @param database the database used
* @return Covariance Matrix
*/
- public Matrix processDatabase(Relation<? extends V> database);
+ Matrix processDatabase(Relation<? extends V> database);
/**
- * Compute Covariance Matrix for a collection of database IDs
+ * Compute Covariance Matrix for a collection of database IDs.
*
* @param ids a collection of ids
* @param database the database used
* @return Covariance Matrix
*/
- public Matrix processIds(DBIDs ids, Relation<? extends V> database);
+ Matrix processIds(DBIDs ids, Relation<? extends V> database);
/**
- * Compute Covariance Matrix for a QueryResult Collection
+ * Compute Covariance Matrix for a QueryResult Collection.
*
* By default it will just collect the ids and run processIds
*
* @param results a collection of QueryResults
* @param database the database used
* @param k the number of entries to process
+ * @param <D> distance type
* @return Covariance Matrix
*/
- public <D extends NumberDistance<?, ?>> Matrix processQueryResults(Collection<? extends DistanceResultPair<D>> results, Relation<? extends V> database, int k);
+ <D extends NumberDistance<D, ?>> Matrix processQueryResults(DistanceDBIDResult<D> results, Relation<? extends V> database, int k);
/**
- * Compute Covariance Matrix for a QueryResult Collection
+ * Compute Covariance Matrix for a QueryResult Collection.
*
* By default it will just collect the ids and run processIds
*
* @param results a collection of QueryResults
* @param database the database used
+ * @param <D> distance type
* @return Covariance Matrix
*/
- public <D extends NumberDistance<?, ?>> Matrix processQueryResults(Collection<? extends DistanceResultPair<D>> results, Relation<? extends V> database);
+ <D extends NumberDistance<D, ?>> Matrix processQueryResults(DistanceDBIDResult<D> results, Relation<? extends V> database);
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/DropEigenPairFilter.java b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/DropEigenPairFilter.java
index 1657a096..8791b6a2 100644
--- a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/DropEigenPairFilter.java
+++ b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/DropEigenPairFilter.java
@@ -84,7 +84,7 @@ public class DropEigenPairFilter implements EigenPairFilter {
double[] ev = eigenPairs.eigenValues();
// calc the eigenvalue sum.
double eigenValueSum = 0.0;
- for(int i = 0; i < ev.length; i++) {
+ for (int i = 0; i < ev.length; i++) {
eigenValueSum += ev[i];
}
// Minimum value
@@ -92,13 +92,13 @@ public class DropEigenPairFilter implements EigenPairFilter {
// Now find the maximum contrast, scanning backwards.
double prev_sum = ev[ev.length - 1];
double prev_rel = 1.0;
- for(int i = 2; i <= ev.length; i++) {
+ for (int i = 2; i <= ev.length; i++) {
double curr_sum = prev_sum + ev[ev.length - i];
double curr_rel = ev[ev.length - i] / curr_sum * i;
// not too weak?
- if(ev[ev.length - i] >= weakEigenvalue) {
+ if (ev[ev.length - i] >= weakEigenvalue) {
double contrast = curr_rel - prev_rel;
- if(contrast > maxContrast) {
+ if (contrast > maxContrast) {
maxContrast = contrast;
contrastMaximum = ev.length - i;
}
@@ -107,11 +107,11 @@ public class DropEigenPairFilter implements EigenPairFilter {
prev_rel = curr_rel;
}
- for(int i = 0; i <= contrastMaximum; i++) {
+ for (int i = 0; i <= contrastMaximum; i++) {
EigenPair eigenPair = eigenPairs.getEigenPair(i);
strongEigenPairs.add(eigenPair);
}
- for(int i = contrastMaximum + 1; i < eigenPairs.size(); i++) {
+ for (int i = contrastMaximum + 1; i < eigenPairs.size(); i++) {
EigenPair eigenPair = eigenPairs.getEigenPair(i);
weakEigenPairs.add(eigenPair);
}
@@ -132,8 +132,9 @@ public class DropEigenPairFilter implements EigenPairFilter {
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- DoubleParameter walphaP = new DoubleParameter(WeakEigenPairFilter.EIGENPAIR_FILTER_WALPHA, new GreaterEqualConstraint(0.0), DEFAULT_WALPHA);
- if(config.grab(walphaP)) {
+ DoubleParameter walphaP = new DoubleParameter(WeakEigenPairFilter.EIGENPAIR_FILTER_WALPHA, DEFAULT_WALPHA);
+ walphaP.addConstraint(new GreaterEqualConstraint(0.0));
+ if (config.grab(walphaP)) {
walpha = walphaP.getValue();
}
}
@@ -143,4 +144,4 @@ public class DropEigenPairFilter implements EigenPairFilter {
return new DropEigenPairFilter(walpha);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/EigenPairFilter.java b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/EigenPairFilter.java
index 553a111f..be1eae17 100644
--- a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/EigenPairFilter.java
+++ b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/EigenPairFilter.java
@@ -48,5 +48,5 @@ public interface EigenPairFilter extends Parameterizable {
* @param eigenPairs the eigenPairs (i.e. the eigenvectors and
* @return the filtered eigenpairs
*/
- public FilteredEigenPairs filter(SortedEigenPairs eigenPairs);
-}
+ FilteredEigenPairs filter(SortedEigenPairs eigenPairs);
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/FilteredEigenPairs.java b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/FilteredEigenPairs.java
index 94c00a7e..4ab120fb 100644
--- a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/FilteredEigenPairs.java
+++ b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/FilteredEigenPairs.java
@@ -23,10 +23,10 @@ package de.lmu.ifi.dbs.elki.math.linearalgebra.pca;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import de.lmu.ifi.dbs.elki.math.linearalgebra.EigenPair;
-
import java.util.List;
+import de.lmu.ifi.dbs.elki.math.linearalgebra.EigenPair;
+
/**
* Encapsulates weak and strong eigenpairs that have been filtered out
* by an eigenpair filter.
@@ -48,7 +48,7 @@ public class FilteredEigenPairs {
/**
* Creates a new object that encapsulates weak and strong eigenpairs
- * that have been filtered out by an eigenpair filter
+ * that have been filtered out by an eigenpair filter.
*
* @param weakEigenPairs the weak eigenpairs
* @param strongEigenPairs the strong eigenpairs
diff --git a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/FirstNEigenPairFilter.java b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/FirstNEigenPairFilter.java
index 08482b71..bd6b4400 100644
--- a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/FirstNEigenPairFilter.java
+++ b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/FirstNEigenPairFilter.java
@@ -50,12 +50,12 @@ public class FirstNEigenPairFilter implements EigenPairFilter {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(FirstNEigenPairFilter.class);
+ private static final Logging LOG = Logging.getLogger(FirstNEigenPairFilter.class);
/**
* Paremeter n
*/
- public static final OptionID EIGENPAIR_FILTER_N = OptionID.getOrCreateOptionID("pca.filter.n", "The number of strong eigenvectors: n eigenvectors with the n highest" + "eigenvalues are marked as strong eigenvectors.");
+ public static final OptionID EIGENPAIR_FILTER_N = new OptionID("pca.filter.n", "The number of strong eigenvectors: n eigenvectors with the n highest" + "eigenvalues are marked as strong eigenvectors.");
/**
* The threshold for strong eigenvectors: n eigenvectors with the n highest
@@ -75,8 +75,8 @@ public class FirstNEigenPairFilter implements EigenPairFilter {
@Override
public FilteredEigenPairs filter(SortedEigenPairs eigenPairs) {
- StringBuffer msg = new StringBuffer();
- if(logger.isDebugging()) {
+ StringBuilder msg = new StringBuilder();
+ if(LOG.isDebugging()) {
msg.append("sortedEigenPairs ").append(eigenPairs.toString());
msg.append("\nn = ").append(n);
}
@@ -96,10 +96,10 @@ public class FirstNEigenPairFilter implements EigenPairFilter {
}
}
- if(logger.isDebugging()) {
+ if(LOG.isDebugging()) {
msg.append("\nstrong EigenPairs = ").append(strongEigenPairs);
msg.append("\nweak EigenPairs = ").append(weakEigenPairs);
- logger.debugFine(msg.toString());
+ LOG.debugFine(msg.toString());
}
return new FilteredEigenPairs(weakEigenPairs, strongEigenPairs);
@@ -121,9 +121,10 @@ public class FirstNEigenPairFilter implements EigenPairFilter {
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- IntParameter nP = new IntParameter(EIGENPAIR_FILTER_N, new GreaterEqualConstraint(0));
+ IntParameter nP = new IntParameter(EIGENPAIR_FILTER_N);
+ nP.addConstraint(new GreaterEqualConstraint(0));
if(config.grab(nP)) {
- n = nP.getValue();
+ n = nP.intValue();
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/LimitEigenPairFilter.java b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/LimitEigenPairFilter.java
index e8d2b844..d7d999b4 100644
--- a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/LimitEigenPairFilter.java
+++ b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/LimitEigenPairFilter.java
@@ -25,7 +25,6 @@ package de.lmu.ifi.dbs.elki.math.linearalgebra.pca;
import java.util.ArrayList;
import java.util.List;
-import java.util.Vector;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.math.linearalgebra.EigenPair;
@@ -57,17 +56,17 @@ public class LimitEigenPairFilter implements EigenPairFilter {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(LimitEigenPairFilter.class);
+ private static final Logging LOG = Logging.getLogger(LimitEigenPairFilter.class);
/**
* "absolute" Flag
*/
- public static final OptionID EIGENPAIR_FILTER_ABSOLUTE = OptionID.getOrCreateOptionID("pca.filter.absolute", "Flag to mark delta as an absolute value.");
+ public static final OptionID EIGENPAIR_FILTER_ABSOLUTE = new OptionID("pca.filter.absolute", "Flag to mark delta as an absolute value.");
/**
* Parameter delta
*/
- public static final OptionID EIGENPAIR_FILTER_DELTA = OptionID.getOrCreateOptionID("pca.filter.delta", "The threshold for strong Eigenvalues. If not otherwise specified, delta " + "is a relative value w.r.t. the (absolute) highest Eigenvalues and has to be " + "a double between 0 and 1. To mark delta as an absolute value, use " + "the option -" + EIGENPAIR_FILTER_ABSOLUTE.getName() + ".");
+ public static final OptionID EIGENPAIR_FILTER_DELTA = new OptionID("pca.filter.delta", "The threshold for strong Eigenvalues. If not otherwise specified, delta " + "is a relative value w.r.t. the (absolute) highest Eigenvalues and has to be " + "a double between 0 and 1. To mark delta as an absolute value, use " + "the option -" + EIGENPAIR_FILTER_ABSOLUTE.getName() + ".");
/**
* The default value for delta.
@@ -98,28 +97,27 @@ public class LimitEigenPairFilter implements EigenPairFilter {
@Override
public FilteredEigenPairs filter(SortedEigenPairs eigenPairs) {
- StringBuffer msg = new StringBuffer();
- if(logger.isDebugging()) {
+ StringBuilder msg = new StringBuilder();
+ if (LOG.isDebugging()) {
msg.append("delta = ").append(delta);
}
// determine limit
double limit;
- if(absolute) {
+ if (absolute) {
limit = delta;
- }
- else {
+ } else {
double max = Double.NEGATIVE_INFINITY;
- for(int i = 0; i < eigenPairs.size(); i++) {
+ for (int i = 0; i < eigenPairs.size(); i++) {
EigenPair eigenPair = eigenPairs.getEigenPair(i);
double eigenValue = Math.abs(eigenPair.getEigenvalue());
- if(max < eigenValue) {
+ if (max < eigenValue) {
max = eigenValue;
}
}
limit = max * delta;
}
- if(logger.isDebugging()) {
+ if (LOG.isDebugging()) {
msg.append("\nlimit = ").append(limit);
}
@@ -128,20 +126,19 @@ public class LimitEigenPairFilter implements EigenPairFilter {
List<EigenPair> weakEigenPairs = new ArrayList<EigenPair>();
// determine strong and weak eigenpairs
- for(int i = 0; i < eigenPairs.size(); i++) {
+ for (int i = 0; i < eigenPairs.size(); i++) {
EigenPair eigenPair = eigenPairs.getEigenPair(i);
double eigenValue = Math.abs(eigenPair.getEigenvalue());
- if(eigenValue >= limit) {
+ if (eigenValue >= limit) {
strongEigenPairs.add(eigenPair);
- }
- else {
+ } else {
weakEigenPairs.add(eigenPair);
}
}
- if(logger.isDebugging()) {
+ if (LOG.isDebugging()) {
msg.append("\nstrong EigenPairs = ").append(strongEigenPairs);
msg.append("\nweak EigenPairs = ").append(weakEigenPairs);
- logger.debugFine(msg.toString());
+ LOG.debugFine(msg.toString());
}
return new FilteredEigenPairs(weakEigenPairs, strongEigenPairs);
@@ -169,15 +166,16 @@ public class LimitEigenPairFilter implements EigenPairFilter {
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
Flag absoluteF = new Flag(EIGENPAIR_FILTER_ABSOLUTE);
- if(config.grab(absoluteF)) {
- absolute = absoluteF.getValue();
+ if (config.grab(absoluteF)) {
+ absolute = absoluteF.isTrue();
}
- DoubleParameter deltaP = new DoubleParameter(EIGENPAIR_FILTER_DELTA, new GreaterEqualConstraint(0), DEFAULT_DELTA);
- if(config.grab(deltaP)) {
- delta = deltaP.getValue();
+ DoubleParameter deltaP = new DoubleParameter(EIGENPAIR_FILTER_DELTA, DEFAULT_DELTA);
+ deltaP.addConstraint(new GreaterEqualConstraint(0));
+ if (config.grab(deltaP)) {
+ delta = deltaP.doubleValue();
// TODO: make this a global constraint?
- if(absolute && deltaP.tookDefaultValue()) {
+ if (absolute && deltaP.tookDefaultValue()) {
config.reportError(new WrongParameterValueException("Illegal parameter setting: " + "Flag " + absoluteF.getName() + " is set, " + "but no value for " + deltaP.getName() + " is specified."));
}
}
@@ -186,16 +184,15 @@ public class LimitEigenPairFilter implements EigenPairFilter {
// delta must be >= 0 and <= 1 if it's a relative value
// Since relative or absolute is dependent on the absolute flag this is a
// global constraint!
- List<ParameterConstraint<Number>> cons = new Vector<ParameterConstraint<Number>>();
+ List<ParameterConstraint<? super Double>> cons = new ArrayList<ParameterConstraint<? super Double>>();
// TODO: Keep the constraint here - applies to non-conditional case as
- // well,
- // and is set above.
- ParameterConstraint<Number> aboveNull = new GreaterEqualConstraint(0);
+ // well, and is set above.
+ ParameterConstraint<Number> aboveNull = new GreaterEqualConstraint(0.);
cons.add(aboveNull);
- ParameterConstraint<Number> underOne = new LessEqualConstraint(1);
+ ParameterConstraint<Number> underOne = new LessEqualConstraint(1.);
cons.add(underOne);
- GlobalParameterConstraint gpc = new ParameterFlagGlobalConstraint<Number, Double>(deltaP, cons, absoluteF, false);
+ GlobalParameterConstraint gpc = new ParameterFlagGlobalConstraint<Double>(deltaP, cons, absoluteF, false);
config.checkConstraint(gpc);
}
@@ -204,4 +201,4 @@ public class LimitEigenPairFilter implements EigenPairFilter {
return new LimitEigenPairFilter(delta, absolute);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/NormalizingEigenPairFilter.java b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/NormalizingEigenPairFilter.java
index 29be965c..55c9c438 100644
--- a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/NormalizingEigenPairFilter.java
+++ b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/NormalizingEigenPairFilter.java
@@ -45,7 +45,7 @@ public class NormalizingEigenPairFilter implements EigenPairFilter {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(NormalizingEigenPairFilter.class);
+ private static final Logging LOG = Logging.getLogger(NormalizingEigenPairFilter.class);
/**
* Provides a new EigenPairFilter that normalizes all eigenvectors s.t.
@@ -67,11 +67,11 @@ public class NormalizingEigenPairFilter implements EigenPairFilter {
normalizeEigenPair(eigenPair);
strongEigenPairs.add(eigenPair);
}
- if(logger.isDebugging()) {
- final StringBuffer msg = new StringBuffer();
+ if(LOG.isDebugging()) {
+ final StringBuilder msg = new StringBuilder();
msg.append("strong EigenPairs = ").append(strongEigenPairs);
msg.append("\nweak EigenPairs = ").append(weakEigenPairs);
- logger.debugFine(msg.toString());
+ LOG.debugFine(msg.toString());
}
return new FilteredEigenPairs(weakEigenPairs, strongEigenPairs);
diff --git a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/PCAFilteredAutotuningRunner.java b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/PCAFilteredAutotuningRunner.java
index 157dfedc..73462c2f 100644
--- a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/PCAFilteredAutotuningRunner.java
+++ b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/PCAFilteredAutotuningRunner.java
@@ -23,27 +23,27 @@ package de.lmu.ifi.dbs.elki.math.linearalgebra.pca;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import de.lmu.ifi.dbs.elki.data.NumberVector;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
-import de.lmu.ifi.dbs.elki.database.query.DoubleDistanceResultPair;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.distance.distancefunction.EuclideanDistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResultIter;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DoubleDistanceDBIDList;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.ModifiableDistanceDBIDResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance;
+import de.lmu.ifi.dbs.elki.logging.LoggingUtil;
+import de.lmu.ifi.dbs.elki.math.linearalgebra.Centroid;
import de.lmu.ifi.dbs.elki.math.linearalgebra.EigenPair;
import de.lmu.ifi.dbs.elki.math.linearalgebra.EigenvalueDecomposition;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Matrix;
import de.lmu.ifi.dbs.elki.math.linearalgebra.SortedEigenPairs;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
/**
@@ -58,14 +58,14 @@ import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
* @param <V> vector type
*/
@Reference(authors = "H.-P. Kriegel, P. Kröger, E. Schubert, A. Zimek", title = "A General Framework for Increasing the Robustness of PCA-based Correlation Clustering Algorithms", booktitle = "Proceedings of the 20th International Conference on Scientific and Statistical Database Management (SSDBM), Hong Kong, China, 2008", url = "http://dx.doi.org/10.1007/978-3-540-69497-7_27")
-public class PCAFilteredAutotuningRunner<V extends NumberVector<? extends V, ?>> extends PCAFilteredRunner<V> {
+public class PCAFilteredAutotuningRunner<V extends NumberVector<?>> extends PCAFilteredRunner<V> {
/**
* Constructor.
*
- * @param covarianceMatrixBuilder
- * @param eigenPairFilter
- * @param big
- * @param small
+ * @param covarianceMatrixBuilder Covariance matrix builder
+ * @param eigenPairFilter Eigen pair filter
+ * @param big Replacement for large values
+ * @param small Replacement for small values
*/
public PCAFilteredAutotuningRunner(CovarianceMatrixBuilder<V> covarianceMatrixBuilder, EigenPairFilter eigenPairFilter, double big, double small) {
super(covarianceMatrixBuilder, eigenPairFilter, big, small);
@@ -75,43 +75,71 @@ public class PCAFilteredAutotuningRunner<V extends NumberVector<? extends V, ?>>
public PCAFilteredResult processIds(DBIDs ids, Relation<? extends V> database) {
// Assume Euclidean distance. In the context of PCA, the neighborhood should
// be L2-spherical to be unbiased.
- V center = DatabaseUtil.centroid(database, ids);
- List<DoubleDistanceResultPair> dres = new ArrayList<DoubleDistanceResultPair>(ids.size());
+ V center = Centroid.make(database, ids).toVector(database);
+ DoubleDistanceDBIDList dres = new DoubleDistanceDBIDList(ids.size());
for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
- DBID id = iter.getDBID();
- final double dist = EuclideanDistanceFunction.STATIC.doubleDistance(center, database.get(id));
- dres.add(new DoubleDistanceResultPair(dist, id));
+ final double dist = EuclideanDistanceFunction.STATIC.doubleDistance(center, database.get(iter));
+ dres.add(dist, iter);
}
- Collections.sort(dres);
+ dres.sort();
return processQueryResult(dres, database);
}
+ /**
+ * Candidate
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ static class Cand {
+ /** Candidate matrix */
+ Matrix m;
+
+ /** Score */
+ double explain;
+
+ /** Dimensionality */
+ int dim;
+
+ /**
+ * Constructor.
+ *
+ * @param m Matrix
+ * @param explain Explains core
+ * @param dim Dimensionality
+ */
+ Cand(Matrix m, double explain, int dim) {
+ this.m = m;
+ this.explain = explain;
+ this.dim = dim;
+ }
+ }
+
@Override
- public <D extends NumberDistance<?, ?>> PCAFilteredResult processQueryResult(Collection<? extends DistanceResultPair<D>> results, Relation<? extends V> database) {
+ public <D extends NumberDistance<D, ?>> PCAFilteredResult processQueryResult(DistanceDBIDResult<D> results, Relation<? extends V> database) {
assertSortedByDistance(results);
- final int dim = DatabaseUtil.dimensionality(database);
+ final int dim = RelationUtil.dimensionality(database);
List<Matrix> best = new LinkedList<Matrix>();
- for(int i = 0; i < dim; i++) {
+ for (int i = 0; i < dim; i++) {
best.add(null);
}
double[] beststrength = new double[dim];
- for(int i = 0; i < dim; i++) {
+ for (int i = 0; i < dim; i++) {
beststrength[i] = -1;
}
int[] bestk = new int[dim];
// 'history'
- LinkedList<Matrix> prevM = new LinkedList<Matrix>();
- LinkedList<Double> prevS = new LinkedList<Double>();
- LinkedList<Integer> prevD = new LinkedList<Integer>();
+ LinkedList<Cand> prev = new LinkedList<Cand>();
// TODO: starting parameter shouldn't be hardcoded...
int smooth = 3;
int startk = 4;
- if(startk > results.size() - 1) {
+ if (startk > results.size() - 1) {
startk = results.size() - 1;
}
// TODO: add smoothing options, handle border cases better.
- for(int k = startk; k < results.size(); k++) {
+ for (int k = startk; k < results.size(); k++) {
// sorted eigenpairs, eigenvectors, eigenvalues
Matrix covMat = covarianceMatrixBuilder.processQueryResults(results, database);
EigenvalueDecomposition evd = new EigenvalueDecomposition(covMat);
@@ -125,49 +153,41 @@ public class PCAFilteredAutotuningRunner<V extends NumberVector<? extends V, ?>>
assert ((thisdim > 0) && (thisdim <= dim));
double thisexplain = computeExplainedVariance(filteredEigenPairs);
- prevM.add(covMat);
- prevS.add(thisexplain);
- prevD.add(thisdim);
- assert (prevS.size() == prevM.size());
- assert (prevS.size() == prevD.size());
+ prev.add(new Cand(covMat, thisexplain, thisdim));
- if(prevS.size() >= 2 * smooth + 1) {
+ if (prev.size() >= 2 * smooth + 1) {
// all the same dimension?
boolean samedim = true;
- for(Iterator<Integer> it = prevD.iterator(); it.hasNext();) {
- if(it.next().intValue() != thisdim) {
+ for (Iterator<Cand> it = prev.iterator(); it.hasNext();) {
+ if (it.next().dim != thisdim) {
samedim = false;
}
}
- if(samedim) {
+ if (samedim) {
// average their explain values
double avgexplain = 0.0;
- for(Iterator<Double> it = prevS.iterator(); it.hasNext();) {
- avgexplain += it.next().doubleValue();
+ for (Iterator<Cand> it = prev.iterator(); it.hasNext();) {
+ avgexplain += it.next().explain;
}
- avgexplain /= prevS.size();
+ avgexplain /= prev.size();
- if(avgexplain > beststrength[thisdim - 1]) {
+ if (avgexplain > beststrength[thisdim - 1]) {
beststrength[thisdim - 1] = avgexplain;
- best.set(thisdim - 1, prevM.get(smooth));
+ best.set(thisdim - 1, prev.get(smooth).m);
bestk[thisdim - 1] = k - smooth;
}
}
- prevM.removeFirst();
- prevS.removeFirst();
- prevD.removeFirst();
- assert (prevS.size() == prevM.size());
- assert (prevS.size() == prevD.size());
+ prev.removeFirst();
}
}
// Try all dimensions, lowest first.
- for(int i = 0; i < dim; i++) {
- if(beststrength[i] > 0.0) {
+ for (int i = 0; i < dim; i++) {
+ if (beststrength[i] > 0.0) {
// If the best was the lowest or the biggest k, skip it!
- if(bestk[i] == startk + smooth) {
+ if (bestk[i] == startk + smooth) {
continue;
}
- if(bestk[i] == results.size() - smooth - 1) {
+ if (bestk[i] == results.size() - smooth - 1) {
continue;
}
Matrix covMat = best.get(i);
@@ -184,18 +204,18 @@ public class PCAFilteredAutotuningRunner<V extends NumberVector<? extends V, ?>>
}
/**
- * Compute the explained variance for a FilteredEigenPairs
+ * Compute the explained variance for a FilteredEigenPairs.
*
- * @param filteredEigenPairs
+ * @param filteredEigenPairs Filtered eigenpairs
* @return explained variance by the strong eigenvectors.
*/
private double computeExplainedVariance(FilteredEigenPairs filteredEigenPairs) {
double strongsum = 0.0;
double weaksum = 0.0;
- for(EigenPair ep : filteredEigenPairs.getStrongEigenPairs()) {
+ for (EigenPair ep : filteredEigenPairs.getStrongEigenPairs()) {
strongsum += ep.getEigenvalue();
}
- for(EigenPair ep : filteredEigenPairs.getWeakEigenPairs()) {
+ for (EigenPair ep : filteredEigenPairs.getWeakEigenPairs()) {
weaksum += ep.getEigenvalue();
}
return strongsum / (strongsum / weaksum);
@@ -204,18 +224,29 @@ public class PCAFilteredAutotuningRunner<V extends NumberVector<? extends V, ?>>
/**
* Ensure that the results are sorted by distance.
*
- * @param results
+ * @param results Results to process
+ * @param <D> distance type
*/
- private <D extends NumberDistance<?, ?>> void assertSortedByDistance(Collection<? extends DistanceResultPair<D>> results) {
+ private <D extends NumberDistance<D, ?>> void assertSortedByDistance(DistanceDBIDResult<D> results) {
// TODO: sort results instead?
double dist = -1.0;
- for(Iterator<? extends DistanceResultPair<D>> it = results.iterator(); it.hasNext();) {
- double qr = it.next().getDistance().doubleValue();
- if(qr < dist) {
- System.err.println("WARNING: results not sorted by distance!");
+ boolean sorted = true;
+ for (DistanceDBIDResultIter<D> it = results.iter(); it.valid(); it.advance()) {
+ double qr = it.getDistance().doubleValue();
+ if (qr < dist) {
+ sorted = false;
}
dist = qr;
}
+ if (!sorted) {
+ try {
+ ModifiableDistanceDBIDResult.class.cast(results).sort();
+ } catch (ClassCastException e) {
+ LoggingUtil.warning("WARNING: results not sorted by distance!", e);
+ } catch (UnsupportedOperationException e) {
+ LoggingUtil.warning("WARNING: results not sorted by distance!", e);
+ }
+ }
}
/**
@@ -225,10 +256,10 @@ public class PCAFilteredAutotuningRunner<V extends NumberVector<? extends V, ?>>
*
* @apiviz.exclude
*/
- public static class Parameterizer<V extends NumberVector<? extends V, ?>> extends PCAFilteredRunner.Parameterizer<V> {
+ public static class Parameterizer<V extends NumberVector<?>> extends PCAFilteredRunner.Parameterizer<V> {
@Override
protected PCAFilteredAutotuningRunner<V> makeInstance() {
return new PCAFilteredAutotuningRunner<V>(covarianceMatrixBuilder, eigenPairFilter, big, small);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/PCAFilteredRunner.java b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/PCAFilteredRunner.java
index 2391446d..59dca276 100644
--- a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/PCAFilteredRunner.java
+++ b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/PCAFilteredRunner.java
@@ -23,12 +23,10 @@ package de.lmu.ifi.dbs.elki.math.linearalgebra.pca;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.util.Collection;
-
import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance;
import de.lmu.ifi.dbs.elki.math.linearalgebra.EigenvalueDecomposition;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Matrix;
@@ -53,18 +51,18 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ObjectParameter;
*
* @param <V> Vector class to use
*/
-public class PCAFilteredRunner<V extends NumberVector<? extends V, ?>> extends PCARunner<V> {
+public class PCAFilteredRunner<V extends NumberVector<?>> extends PCARunner<V> {
/**
* Parameter to specify the filter for determination of the strong and weak
* eigenvectors, must be a subclass of {@link EigenPairFilter}.
- * <p/>
+ * <p>
* Default value: {@link PercentageEigenPairFilter}
* </p>
- * <p/>
+ * <p>
* Key: {@code -pca.filter}
* </p>
*/
- public static final OptionID PCA_EIGENPAIR_FILTER = OptionID.getOrCreateOptionID("pca.filter", "Filter class to determine the strong and weak eigenvectors.");
+ public static final OptionID PCA_EIGENPAIR_FILTER = new OptionID("pca.filter", "Filter class to determine the strong and weak eigenvectors.");
/**
* Parameter to specify a constant big value to reset high eigenvalues, must
@@ -76,7 +74,7 @@ public class PCAFilteredRunner<V extends NumberVector<? extends V, ?>> extends P
* Key: {@code -pca.big}
* </p>
*/
- public static final OptionID BIG_ID = OptionID.getOrCreateOptionID("pca.big", "A constant big value to reset high eigenvalues.");
+ public static final OptionID BIG_ID = new OptionID("pca.big", "A constant big value to reset high eigenvalues.");
/**
* Parameter to specify a constant small value to reset low eigenvalues, must
@@ -88,7 +86,7 @@ public class PCAFilteredRunner<V extends NumberVector<? extends V, ?>> extends P
* Key: {@code -pca.small}
* </p>
*/
- public static final OptionID SMALL_ID = OptionID.getOrCreateOptionID("pca.small", "A constant small value to reset low eigenvalues.");
+ public static final OptionID SMALL_ID = new OptionID("pca.small", "A constant small value to reset low eigenvalues.");
/**
* Holds the instance of the EigenPairFilter specified by
@@ -109,10 +107,10 @@ public class PCAFilteredRunner<V extends NumberVector<? extends V, ?>> extends P
/**
* Constructor.
*
- * @param covarianceMatrixBuilder
- * @param eigenPairFilter
- * @param big
- * @param small
+ * @param covarianceMatrixBuilder Covariance matrix builder
+ * @param eigenPairFilter Eigen pair filter
+ * @param big Replacement for large eigenvalues
+ * @param small Replacement for small eigenvalues
*/
public PCAFilteredRunner(CovarianceMatrixBuilder<V> covarianceMatrixBuilder, EigenPairFilter eigenPairFilter, double big, double small) {
super(covarianceMatrixBuilder);
@@ -122,7 +120,7 @@ public class PCAFilteredRunner<V extends NumberVector<? extends V, ?>> extends P
}
/**
- * Run PCA on a collection of database IDs
+ * Run PCA on a collection of database IDs.
*
* @param ids a collection of ids
* @param database the database used
@@ -134,21 +132,23 @@ public class PCAFilteredRunner<V extends NumberVector<? extends V, ?>> extends P
}
/**
- * Run PCA on a QueryResult Collection
+ * Run PCA on a QueryResult Collection.
*
* @param results a collection of QueryResults
* @param database the database used
+ * @param <D> distance type
* @return PCA result
*/
@Override
- public <D extends NumberDistance<?, ?>> PCAFilteredResult processQueryResult(Collection<? extends DistanceResultPair<D>> results, Relation<? extends V> database) {
+ public <D extends NumberDistance<D, ?>> PCAFilteredResult processQueryResult(DistanceDBIDResult<D> results, Relation<? extends V> database) {
return processCovarMatrix(covarianceMatrixBuilder.processQueryResults(results, database));
}
/**
- * Process an existing Covariance Matrix
+ * Process an existing Covariance Matrix.
*
* @param covarMatrix the matrix used for performing PCA
+ * @return Filtered result
*/
@Override
public PCAFilteredResult processCovarMatrix(Matrix covarMatrix) {
@@ -158,9 +158,10 @@ public class PCAFilteredRunner<V extends NumberVector<? extends V, ?>> extends P
}
/**
- * Process an existing eigenvalue decomposition
+ * Process an existing eigenvalue decomposition.
*
* @param evd eigenvalue decomposition to use
+ * @return filtered result
*/
@Override
public PCAFilteredResult processEVD(EigenvalueDecomposition evd) {
@@ -185,7 +186,7 @@ public class PCAFilteredRunner<V extends NumberVector<? extends V, ?>> extends P
*
* @apiviz.exclude
*/
- public static class Parameterizer<V extends NumberVector<? extends V, ?>> extends PCARunner.Parameterizer<V> {
+ public static class Parameterizer<V extends NumberVector<?>> extends PCARunner.Parameterizer<V> {
/**
* Holds the instance of the EigenPairFilter specified by
* {@link #PCA_EIGENPAIR_FILTER}.
@@ -205,24 +206,26 @@ public class PCAFilteredRunner<V extends NumberVector<? extends V, ?>> extends P
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- ObjectParameter<EigenPairFilter> EIGENPAIR_FILTER_PARAM = new ObjectParameter<EigenPairFilter>(PCA_EIGENPAIR_FILTER, EigenPairFilter.class, PercentageEigenPairFilter.class);
- if(config.grab(EIGENPAIR_FILTER_PARAM)) {
- eigenPairFilter = EIGENPAIR_FILTER_PARAM.instantiateClass(config);
+ ObjectParameter<EigenPairFilter> filterP = new ObjectParameter<EigenPairFilter>(PCA_EIGENPAIR_FILTER, EigenPairFilter.class, PercentageEigenPairFilter.class);
+ if (config.grab(filterP)) {
+ eigenPairFilter = filterP.instantiateClass(config);
}
- DoubleParameter BIG_PARAM = new DoubleParameter(BIG_ID, new GreaterConstraint(0), 1.0);
- if(config.grab(BIG_PARAM)) {
- big = BIG_PARAM.getValue();
+ DoubleParameter bigP = new DoubleParameter(BIG_ID, 1.0);
+ bigP.addConstraint(new GreaterConstraint(0));
+ if (config.grab(bigP)) {
+ big = bigP.doubleValue();
}
- DoubleParameter SMALL_PARAM = new DoubleParameter(SMALL_ID, new GreaterEqualConstraint(0), 0.0);
- if(config.grab(SMALL_PARAM)) {
- small = SMALL_PARAM.getValue();
+ DoubleParameter smallP = new DoubleParameter(SMALL_ID, 0.0);
+ smallP.addConstraint(new GreaterEqualConstraint(0));
+ if (config.grab(smallP)) {
+ small = smallP.doubleValue();
}
// global constraint small <--> big
- config.checkConstraint(new LessGlobalConstraint<Double>(SMALL_PARAM, BIG_PARAM));
+ config.checkConstraint(new LessGlobalConstraint<Double>(smallP, bigP));
}
@Override
@@ -230,4 +233,4 @@ public class PCAFilteredRunner<V extends NumberVector<? extends V, ?>> extends P
return new PCAFilteredRunner<V>(covarianceMatrixBuilder, eigenPairFilter, big, small);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/PCARunner.java b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/PCARunner.java
index 661fa5c5..4fb9dbdf 100644
--- a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/PCARunner.java
+++ b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/PCARunner.java
@@ -23,12 +23,11 @@ package de.lmu.ifi.dbs.elki.math.linearalgebra.pca;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.util.Collection;
import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance;
import de.lmu.ifi.dbs.elki.math.linearalgebra.EigenvalueDecomposition;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Matrix;
@@ -57,7 +56,7 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ObjectParameter;
*
* @param <V> Vector type
*/
-public class PCARunner<V extends NumberVector<? extends V, ?>> implements Parameterizable {
+public class PCARunner<V extends NumberVector<?>> implements Parameterizable {
/**
* Parameter to specify the class to compute the covariance matrix, must be a
* subclass of {@link CovarianceMatrixBuilder}.
@@ -68,7 +67,7 @@ public class PCARunner<V extends NumberVector<? extends V, ?>> implements Parame
* Key: {@code -pca.covariance}
* </p>
*/
- public static final OptionID PCA_COVARIANCE_MATRIX = OptionID.getOrCreateOptionID("pca.covariance", "Class used to compute the covariance matrix.");
+ public static final OptionID PCA_COVARIANCE_MATRIX = new OptionID("pca.covariance", "Class used to compute the covariance matrix.");
/**
* The covariance computation class.
@@ -86,7 +85,7 @@ public class PCARunner<V extends NumberVector<? extends V, ?>> implements Parame
}
/**
- * Run PCA on the complete database
+ * Run PCA on the complete database.
*
* @param database the database used
* @return PCA result
@@ -96,7 +95,7 @@ public class PCARunner<V extends NumberVector<? extends V, ?>> implements Parame
}
/**
- * Run PCA on a collection of database IDs
+ * Run PCA on a collection of database IDs.
*
* @param ids a collection of ids
* @param database the database used
@@ -107,18 +106,19 @@ public class PCARunner<V extends NumberVector<? extends V, ?>> implements Parame
}
/**
- * Run PCA on a QueryResult Collection
+ * Run PCA on a QueryResult Collection.
*
* @param results a collection of QueryResults
* @param database the database used
+ * @param <D> distance type
* @return PCA result
*/
- public <D extends NumberDistance<?, ?>> PCAResult processQueryResult(Collection<? extends DistanceResultPair<D>> results, Relation<? extends V> database) {
+ public <D extends NumberDistance<D, ?>> PCAResult processQueryResult(DistanceDBIDResult<D> results, Relation<? extends V> database) {
return processCovarMatrix(covarianceMatrixBuilder.processQueryResults(results, database));
}
/**
- * Process an existing covariance Matrix
+ * Process an existing covariance Matrix.
*
* @param covarMatrix the matrix used for performing pca
* @return PCA result
@@ -130,7 +130,7 @@ public class PCARunner<V extends NumberVector<? extends V, ?>> implements Parame
}
/**
- * Process an existing eigenvalue decomposition
+ * Process an existing eigenvalue decomposition.
*
* @param evd eigenvalue decomposition to use
* @return PCA result
@@ -141,7 +141,7 @@ public class PCARunner<V extends NumberVector<? extends V, ?>> implements Parame
}
/**
- * Get covariance matrix builder
+ * Get covariance matrix builder.
*
* @return covariance matrix builder in use
*/
@@ -165,7 +165,7 @@ public class PCARunner<V extends NumberVector<? extends V, ?>> implements Parame
*
* @apiviz.exclude
*/
- public static class Parameterizer<V extends NumberVector<? extends V, ?>> extends AbstractParameterizer {
+ public static class Parameterizer<V extends NumberVector<?>> extends AbstractParameterizer {
/**
* The covariance computation class.
*/
diff --git a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/PercentageEigenPairFilter.java b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/PercentageEigenPairFilter.java
index 321c12cc..0daa3d94 100644
--- a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/PercentageEigenPairFilter.java
+++ b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/PercentageEigenPairFilter.java
@@ -33,7 +33,8 @@ import de.lmu.ifi.dbs.elki.utilities.documentation.Description;
import de.lmu.ifi.dbs.elki.utilities.documentation.Title;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.IntervalConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.LessConstraint;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.DoubleParameter;
@@ -51,7 +52,7 @@ public class PercentageEigenPairFilter implements EigenPairFilter {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(PercentageEigenPairFilter.class);
+ private static final Logging LOG = Logging.getLogger(PercentageEigenPairFilter.class);
/**
* The threshold for 'strong' eigenvectors: the 'strong' eigenvectors explain
@@ -63,7 +64,7 @@ public class PercentageEigenPairFilter implements EigenPairFilter {
* Key: {@code -pca.filter.alpha}
* </p>
*/
- public static final OptionID ALPHA_ID = OptionID.getOrCreateOptionID("pca.filter.alpha", "The share (0.0 to 1.0) of variance that needs to be explained by the 'strong' eigenvectors." + "The filter class will choose the number of strong eigenvectors by this share.");
+ public static final OptionID ALPHA_ID = new OptionID("pca.filter.alpha", "The share (0.0 to 1.0) of variance that needs to be explained by the 'strong' eigenvectors." + "The filter class will choose the number of strong eigenvectors by this share.");
/**
* The default value for alpha.
@@ -88,8 +89,8 @@ public class PercentageEigenPairFilter implements EigenPairFilter {
@Override
public FilteredEigenPairs filter(SortedEigenPairs eigenPairs) {
- StringBuffer msg = new StringBuffer();
- if(logger.isDebugging()) {
+ StringBuilder msg = new StringBuilder();
+ if (LOG.isDebugging()) {
msg.append("alpha = ").append(alpha);
msg.append("\nsortedEigenPairs = ").append(eigenPairs);
}
@@ -100,37 +101,35 @@ public class PercentageEigenPairFilter implements EigenPairFilter {
// determine sum of eigenvalues
double totalSum = 0;
- for(int i = 0; i < eigenPairs.size(); i++) {
+ for (int i = 0; i < eigenPairs.size(); i++) {
EigenPair eigenPair = eigenPairs.getEigenPair(i);
totalSum += eigenPair.getEigenvalue();
}
- if(logger.isDebugging()) {
+ if (LOG.isDebugging()) {
msg.append("\ntotalSum = ").append(totalSum);
}
// determine strong and weak eigenpairs
double currSum = 0;
boolean found = false;
- for(int i = 0; i < eigenPairs.size(); i++) {
+ for (int i = 0; i < eigenPairs.size(); i++) {
EigenPair eigenPair = eigenPairs.getEigenPair(i);
currSum += eigenPair.getEigenvalue();
- if(currSum / totalSum >= alpha) {
- if(!found) {
+ if (currSum / totalSum >= alpha) {
+ if (!found) {
found = true;
strongEigenPairs.add(eigenPair);
- }
- else {
+ } else {
weakEigenPairs.add(eigenPair);
}
- }
- else {
+ } else {
strongEigenPairs.add(eigenPair);
}
}
- if(logger.isDebugging()) {
+ if (LOG.isDebugging()) {
msg.append("\nstrong EigenPairs = ").append(strongEigenPairs);
msg.append("\nweak EigenPairs = ").append(weakEigenPairs);
- logger.debugFine(msg.toString());
+ LOG.debugFine(msg.toString());
}
return new FilteredEigenPairs(weakEigenPairs, strongEigenPairs);
@@ -153,9 +152,11 @@ public class PercentageEigenPairFilter implements EigenPairFilter {
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- DoubleParameter alphaP = new DoubleParameter(ALPHA_ID, new IntervalConstraint(0.0, IntervalConstraint.IntervalBoundary.OPEN, 1.0, IntervalConstraint.IntervalBoundary.OPEN), DEFAULT_ALPHA);
- if(config.grab(alphaP)) {
- alpha = alphaP.getValue();
+ DoubleParameter alphaP = new DoubleParameter(ALPHA_ID, DEFAULT_ALPHA);
+ alphaP.addConstraint(new GreaterConstraint(0.0));
+ alphaP.addConstraint(new LessConstraint(1.0));
+ if (config.grab(alphaP)) {
+ alpha = alphaP.doubleValue();
}
}
@@ -164,4 +165,4 @@ public class PercentageEigenPairFilter implements EigenPairFilter {
return new PercentageEigenPairFilter(alpha);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/ProgressiveEigenPairFilter.java b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/ProgressiveEigenPairFilter.java
index f66a1e96..4c359dad 100644
--- a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/ProgressiveEigenPairFilter.java
+++ b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/ProgressiveEigenPairFilter.java
@@ -32,8 +32,9 @@ import de.lmu.ifi.dbs.elki.utilities.documentation.Description;
import de.lmu.ifi.dbs.elki.utilities.documentation.Title;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterConstraint;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterEqualConstraint;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.IntervalConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.LessConstraint;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.DoubleParameter;
@@ -86,7 +87,7 @@ public class ProgressiveEigenPairFilter implements EigenPairFilter {
/**
* Parameter progressive alpha.
*/
- public static final OptionID EIGENPAIR_FILTER_PALPHA = OptionID.getOrCreateOptionID("pca.filter.progressivealpha", "The share (0.0 to 1.0) of variance that needs to be explained by the 'strong' eigenvectors." + "The filter class will choose the number of strong eigenvectors by this share.");
+ public static final OptionID EIGENPAIR_FILTER_PALPHA = new OptionID("pca.filter.progressivealpha", "The share (0.0 to 1.0) of variance that needs to be explained by the 'strong' eigenvectors." + "The filter class will choose the number of strong eigenvectors by this share.");
/**
* The default value for alpha.
@@ -132,7 +133,7 @@ public class ProgressiveEigenPairFilter implements EigenPairFilter {
// determine sum of eigenvalues
double totalSum = 0;
- for(int i = 0; i < eigenPairs.size(); i++) {
+ for (int i = 0; i < eigenPairs.size(); i++) {
EigenPair eigenPair = eigenPairs.getEigenPair(i);
totalSum += eigenPair.getEigenvalue();
}
@@ -142,35 +143,35 @@ public class ProgressiveEigenPairFilter implements EigenPairFilter {
double currSum = 0;
boolean found = false;
int i;
- for(i = 0; i < eigenPairs.size(); i++) {
+ for (i = 0; i < eigenPairs.size(); i++) {
EigenPair eigenPair = eigenPairs.getEigenPair(i);
// weak Eigenvector?
- if(eigenPair.getEigenvalue() < expectedVariance) {
+ if (eigenPair.getEigenvalue() < expectedVariance) {
break;
}
currSum += eigenPair.getEigenvalue();
// calculate progressive alpha level
double alpha = 1.0 - (1.0 - palpha) * (1.0 - (i + 1) / eigenPairs.size());
- if(currSum / totalSum >= alpha || i == eigenPairs.size() - 1) {
+ if (currSum / totalSum >= alpha || i == eigenPairs.size() - 1) {
found = true;
strongEigenPairs.add(eigenPair);
break;
}
}
// if we didn't hit our alpha level, we consider all vectors to be weak!
- if(!found) {
+ if (!found) {
assert (weakEigenPairs.size() == 0);
weakEigenPairs = strongEigenPairs;
strongEigenPairs = new ArrayList<EigenPair>();
}
- for(; i < eigenPairs.size(); i++) {
+ for (; i < eigenPairs.size(); i++) {
EigenPair eigenPair = eigenPairs.getEigenPair(i);
weakEigenPairs.add(eigenPair);
}
// the code using this method doesn't expect an empty strong set,
// if we didn't find any strong ones, we make all vectors strong
- if(strongEigenPairs.size() == 0) {
+ if (strongEigenPairs.size() == 0) {
return new FilteredEigenPairs(new ArrayList<EigenPair>(), weakEigenPairs);
}
return new FilteredEigenPairs(weakEigenPairs, strongEigenPairs);
@@ -198,13 +199,16 @@ public class ProgressiveEigenPairFilter implements EigenPairFilter {
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- DoubleParameter palphaP = new DoubleParameter(EIGENPAIR_FILTER_PALPHA, new IntervalConstraint(0.0, IntervalConstraint.IntervalBoundary.OPEN, 1.0, IntervalConstraint.IntervalBoundary.OPEN), DEFAULT_PALPHA);
- if(config.grab(palphaP)) {
+ DoubleParameter palphaP = new DoubleParameter(EIGENPAIR_FILTER_PALPHA, DEFAULT_PALPHA);
+ palphaP.addConstraint(new GreaterConstraint(0.0));
+ palphaP.addConstraint(new LessConstraint(1.0));
+ if (config.grab(palphaP)) {
palpha = palphaP.getValue();
}
- DoubleParameter walphaP = new DoubleParameter(WeakEigenPairFilter.EIGENPAIR_FILTER_WALPHA, new GreaterEqualConstraint(0.0), DEFAULT_WALPHA);
- if(config.grab(walphaP)) {
+ DoubleParameter walphaP = new DoubleParameter(WeakEigenPairFilter.EIGENPAIR_FILTER_WALPHA, DEFAULT_WALPHA);
+ walphaP.addConstraint(new GreaterEqualConstraint(0.0));
+ if (config.grab(walphaP)) {
walpha = walphaP.getValue();
}
}
@@ -214,4 +218,4 @@ public class ProgressiveEigenPairFilter implements EigenPairFilter {
return new ProgressiveEigenPairFilter(palpha, walpha);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/RANSACCovarianceMatrixBuilder.java b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/RANSACCovarianceMatrixBuilder.java
new file mode 100644
index 00000000..e39820c4
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/RANSACCovarianceMatrixBuilder.java
@@ -0,0 +1,190 @@
+package de.lmu.ifi.dbs.elki.math.linearalgebra.pca;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
+import de.lmu.ifi.dbs.elki.database.ids.ModifiableDBIDs;
+import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
+import de.lmu.ifi.dbs.elki.math.linearalgebra.CovarianceMatrix;
+import de.lmu.ifi.dbs.elki.math.linearalgebra.Matrix;
+import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector;
+import de.lmu.ifi.dbs.elki.math.statistics.distribution.ChiSquaredDistribution;
+import de.lmu.ifi.dbs.elki.utilities.RandomFactory;
+import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.RandomParameter;
+
+/**
+ * RANSAC based approach to a more robust covariance matrix computation.
+ *
+ * This is an <b>experimental</b> adoption of RANSAC to this problem, not a
+ * generic RANSAC implementation!
+ *
+ * While using RANSAC for PCA at first sounds like a good idea, <b>it does not
+ * work very well in high-dimensional spaces</b>. The problem is that PCA has
+ * O(n^2) degrees of freedom, so we need to sample very many objects, then
+ * perform an O(n^3) matrix operation to compute PCA, for each attempt.
+ *
+ * References:
+ *
+ * RANSAC for PCA was a side note in:
+ * <p>
+ * Hans-Peter Kriegel, Peer Kröger, Erich Schubert, Arthur Zimek<br />
+ * Outlier Detection in Arbitrarily Oriented Subspaces<br />
+ * in: Proc. IEEE International Conference on Data Mining (ICDM 2012)
+ * </p>
+ *
+ * The basic RANSAC idea was explained in:
+ * <p>
+ * Random sample consensus: a paradigm for model fitting with applications to
+ * image analysis and automated cartography<br />
+ * M.A. Fischler, R.C. Bolles<br />
+ * Communications of the ACM, Vol. 24 Issue 6
+ * </p>
+ *
+ * @author Erich Schubert
+ *
+ * @param <V> Vector type
+ */
+@Reference(authors = "Hans-Peter Kriegel, Peer Kröger, Erich Schubert, Arthur Zimek", title = "Outlier Detection in Arbitrarily Oriented Subspaces", booktitle = "Proc. IEEE International Conference on Data Mining (ICDM 2012)")
+public class RANSACCovarianceMatrixBuilder<V extends NumberVector<?>> extends AbstractCovarianceMatrixBuilder<V> {
+ /**
+ * Number of iterations to perform
+ */
+ int iterations = 1000;
+
+ /**
+ * Random generator
+ */
+ RandomFactory rnd;
+
+ /**
+ * Constructor.
+ *
+ * @param iterations Number of iterations (attempts) to try
+ * @param rnd random generator
+ */
+ public RANSACCovarianceMatrixBuilder(int iterations, RandomFactory rnd) {
+ super();
+ this.iterations = iterations;
+ this.rnd = rnd;
+ }
+
+ @Reference(title = "Random sample consensus: a paradigm for model fitting with applications to image analysis and automated cartography", authors = "M.A. Fischler, R.C. Bolles", booktitle = "Communications of the ACM, Vol. 24 Issue 6", url = "http://dx.doi.org/10.1145/358669.358692")
+ @Override
+ public Matrix processIds(DBIDs ids, Relation<? extends V> relation) {
+ final int dim = RelationUtil.dimensionality(relation);
+
+ DBIDs best = DBIDUtil.EMPTYDBIDS;
+ double tresh = ChiSquaredDistribution.quantile(0.85, dim);
+
+ for (int i = 0; i < iterations; i++) {
+ DBIDs sample = DBIDUtil.randomSample(ids, dim + 1, rnd);
+ CovarianceMatrix cv = CovarianceMatrix.make(relation, sample);
+ Vector centroid = cv.getMeanVector();
+ Matrix p = cv.destroyToSampleMatrix().inverse();
+
+ ModifiableDBIDs support = DBIDUtil.newHashSet();
+ for (DBIDIter id = ids.iter(); id.valid(); id.advance()) {
+ Vector vec = relation.get(id).getColumnVector().minusEquals(centroid);
+ double sqlen = vec.transposeTimesTimes(p, vec);
+ if (sqlen < tresh) {
+ support.add(id);
+ }
+ }
+
+ if (support.size() > best.size()) {
+ best = support;
+ }
+ if (support.size() >= ids.size()) {
+ break; // Can't get better than this!
+ }
+ }
+ // logger.warning("Consensus size: "+best.size()+" of "+ids.size());
+ // Fall back to regular PCA
+ if (best.size() <= dim) {
+ return CovarianceMatrix.make(relation, ids).destroyToSampleMatrix();
+ }
+ // Return estimation based on consensus set.
+ return CovarianceMatrix.make(relation, best).destroyToSampleMatrix();
+ }
+
+ /**
+ * Parameterization class
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ *
+ * @param <V> Vector type
+ */
+ public static class Parameterizer<V extends NumberVector<?>> extends AbstractParameterizer {
+ /**
+ * Number of iterations.
+ */
+ public static final OptionID ITER_ID = new OptionID("ransacpca.iterations", "The number of iterations to perform.");
+
+ /**
+ * Random seed
+ */
+ public static final OptionID SEED_ID = new OptionID("ransacpca.seed", "Random seed (optional).");
+
+ /**
+ * Number of iterations to perform
+ */
+ int iterations = 1000;
+
+ /**
+ * Random generator
+ */
+ RandomFactory rnd;
+
+ @Override
+ protected void makeOptions(Parameterization config) {
+ super.makeOptions(config);
+ IntParameter iterP = new IntParameter(ITER_ID, 1000);
+ iterP.addConstraint(new GreaterConstraint(0));
+ if (config.grab(iterP)) {
+ iterations = iterP.intValue();
+ }
+ RandomParameter rndP = new RandomParameter(SEED_ID);
+ if (config.grab(rndP)) {
+ rnd = rndP.getValue();
+ }
+ }
+
+ @Override
+ protected RANSACCovarianceMatrixBuilder<V> makeInstance() {
+ return new RANSACCovarianceMatrixBuilder<V>(iterations, rnd);
+ }
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/RelativeEigenPairFilter.java b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/RelativeEigenPairFilter.java
index 59b2b750..38b0fcc2 100644
--- a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/RelativeEigenPairFilter.java
+++ b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/RelativeEigenPairFilter.java
@@ -57,7 +57,7 @@ public class RelativeEigenPairFilter implements EigenPairFilter {
/**
* Parameter relative alpha.
*/
- public static final OptionID EIGENPAIR_FILTER_RALPHA = OptionID.getOrCreateOptionID("pca.filter.relativealpha", "The sensitivity niveau for weak eigenvectors: An eigenvector which is at less than " + "the given share of the statistical average variance is considered weak.");
+ public static final OptionID EIGENPAIR_FILTER_RALPHA = new OptionID("pca.filter.relativealpha", "The sensitivity niveau for weak eigenvectors: An eigenvector which is at less than " + "the given share of the statistical average variance is considered weak.");
/**
* The default value for ralpha.
@@ -93,21 +93,21 @@ public class RelativeEigenPairFilter implements EigenPairFilter {
// find the last eigenvector that is considered 'strong' by the weak rule
// applied to the remaining vectors only
double eigenValueSum = eigenPairs.getEigenPair(eigenPairs.size() - 1).getEigenvalue();
- for(int i = eigenPairs.size() - 2; i >= 0; i--) {
+ for (int i = eigenPairs.size() - 2; i >= 0; i--) {
EigenPair eigenPair = eigenPairs.getEigenPair(i);
eigenValueSum += eigenPair.getEigenvalue();
double needEigenvalue = eigenValueSum / (eigenPairs.size() - i) * ralpha;
- if(eigenPair.getEigenvalue() >= needEigenvalue) {
+ if (eigenPair.getEigenvalue() >= needEigenvalue) {
contrastAtMax = i;
break;
}
}
- for(int i = 0; i <= contrastAtMax /* && i < eigenPairs.size() */; i++) {
+ for (int i = 0; i <= contrastAtMax /* && i < eigenPairs.size() */; i++) {
EigenPair eigenPair = eigenPairs.getEigenPair(i);
strongEigenPairs.add(eigenPair);
}
- for(int i = contrastAtMax + 1; i < eigenPairs.size(); i++) {
+ for (int i = contrastAtMax + 1; i < eigenPairs.size(); i++) {
EigenPair eigenPair = eigenPairs.getEigenPair(i);
weakEigenPairs.add(eigenPair);
}
@@ -128,8 +128,9 @@ public class RelativeEigenPairFilter implements EigenPairFilter {
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- DoubleParameter ralphaP = new DoubleParameter(EIGENPAIR_FILTER_RALPHA, new GreaterEqualConstraint(0.0), DEFAULT_RALPHA);
- if(config.grab(ralphaP)) {
+ DoubleParameter ralphaP = new DoubleParameter(EIGENPAIR_FILTER_RALPHA, DEFAULT_RALPHA);
+ ralphaP.addConstraint(new GreaterEqualConstraint(0.0));
+ if (config.grab(ralphaP)) {
ralpha = ralphaP.getValue();
}
}
@@ -139,4 +140,4 @@ public class RelativeEigenPairFilter implements EigenPairFilter {
return new RelativeEigenPairFilter(ralpha);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/SignificantEigenPairFilter.java b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/SignificantEigenPairFilter.java
index ab04cbb5..c22e9592 100644
--- a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/SignificantEigenPairFilter.java
+++ b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/SignificantEigenPairFilter.java
@@ -87,32 +87,32 @@ public class SignificantEigenPairFilter implements EigenPairFilter {
double maxContrast = 0.0;
// calc the eigenvalue sum.
double eigenValueSum = 0.0;
- for(int i = 0; i < eigenPairs.size(); i++) {
+ for (int i = 0; i < eigenPairs.size(); i++) {
EigenPair eigenPair = eigenPairs.getEigenPair(i);
eigenValueSum += eigenPair.getEigenvalue();
}
double weakEigenvalue = eigenValueSum / eigenPairs.size() * walpha;
// now find the maximum contrast.
double currSum = eigenPairs.getEigenPair(eigenPairs.size() - 1).getEigenvalue();
- for(int i = eigenPairs.size() - 2; i >= 0; i--) {
+ for (int i = eigenPairs.size() - 2; i >= 0; i--) {
EigenPair eigenPair = eigenPairs.getEigenPair(i);
currSum += eigenPair.getEigenvalue();
// weak?
- if(eigenPair.getEigenvalue() < weakEigenvalue) {
+ if (eigenPair.getEigenvalue() < weakEigenvalue) {
continue;
}
double contrast = eigenPair.getEigenvalue() / (currSum / (eigenPairs.size() - i));
- if(contrast > maxContrast) {
+ if (contrast > maxContrast) {
maxContrast = contrast;
contrastMaximum = i;
}
}
- for(int i = 0; i <= contrastMaximum /* && i < eigenPairs.size() */; i++) {
+ for (int i = 0; i <= contrastMaximum /* && i < eigenPairs.size() */; i++) {
EigenPair eigenPair = eigenPairs.getEigenPair(i);
strongEigenPairs.add(eigenPair);
}
- for(int i = contrastMaximum + 1; i < eigenPairs.size(); i++) {
+ for (int i = contrastMaximum + 1; i < eigenPairs.size(); i++) {
EigenPair eigenPair = eigenPairs.getEigenPair(i);
weakEigenPairs.add(eigenPair);
}
@@ -133,8 +133,9 @@ public class SignificantEigenPairFilter implements EigenPairFilter {
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- DoubleParameter walphaP = new DoubleParameter(WeakEigenPairFilter.EIGENPAIR_FILTER_WALPHA, new GreaterEqualConstraint(0.0), DEFAULT_WALPHA);
- if(config.grab(walphaP)) {
+ DoubleParameter walphaP = new DoubleParameter(WeakEigenPairFilter.EIGENPAIR_FILTER_WALPHA, DEFAULT_WALPHA);
+ walphaP.addConstraint(new GreaterEqualConstraint(0.0));
+ if (config.grab(walphaP)) {
walpha = walphaP.getValue();
}
}
@@ -144,4 +145,4 @@ public class SignificantEigenPairFilter implements EigenPairFilter {
return new SignificantEigenPairFilter(walpha);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/StandardCovarianceMatrixBuilder.java b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/StandardCovarianceMatrixBuilder.java
index 2c88d490..c7104e3b 100644
--- a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/StandardCovarianceMatrixBuilder.java
+++ b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/StandardCovarianceMatrixBuilder.java
@@ -39,9 +39,9 @@ import de.lmu.ifi.dbs.elki.math.linearalgebra.Matrix;
*
* @param <V> Vector class to use.
*/
-public class StandardCovarianceMatrixBuilder<V extends NumberVector<? extends V, ?>> extends AbstractCovarianceMatrixBuilder<V> {
+public class StandardCovarianceMatrixBuilder<V extends NumberVector<?>> extends AbstractCovarianceMatrixBuilder<V> {
/**
- * Compute Covariance Matrix for a complete database
+ * Compute Covariance Matrix for a complete database.
*
* @param database the database used
* @return Covariance Matrix
@@ -52,7 +52,7 @@ public class StandardCovarianceMatrixBuilder<V extends NumberVector<? extends V,
}
/**
- * Compute Covariance Matrix for a collection of database IDs
+ * Compute Covariance Matrix for a collection of database IDs.
*
* @param ids a collection of ids
* @param database the database used
diff --git a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/WeakEigenPairFilter.java b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/WeakEigenPairFilter.java
index fbca039d..3c60cd0d 100644
--- a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/WeakEigenPairFilter.java
+++ b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/WeakEigenPairFilter.java
@@ -52,7 +52,7 @@ public class WeakEigenPairFilter implements EigenPairFilter {
* and
* {@link de.lmu.ifi.dbs.elki.math.linearalgebra.pca.SignificantEigenPairFilter}
*/
- public static final OptionID EIGENPAIR_FILTER_WALPHA = OptionID.getOrCreateOptionID("pca.filter.weakalpha", "The minimum strength of the statistically expected variance (1/n) share an eigenvector " + "needs to have to be considered 'strong'.");
+ public static final OptionID EIGENPAIR_FILTER_WALPHA = new OptionID("pca.filter.weakalpha", "The minimum strength of the statistically expected variance (1/n) share an eigenvector " + "needs to have to be considered 'strong'.");
/**
* The default value for walpha.
@@ -85,26 +85,25 @@ public class WeakEigenPairFilter implements EigenPairFilter {
// determine sum of eigenvalues
double totalSum = 0;
- for(int i = 0; i < eigenPairs.size(); i++) {
+ for (int i = 0; i < eigenPairs.size(); i++) {
EigenPair eigenPair = eigenPairs.getEigenPair(i);
totalSum += eigenPair.getEigenvalue();
}
double expectEigenvalue = totalSum / eigenPairs.size() * walpha;
// determine strong and weak eigenpairs
- for(int i = 0; i < eigenPairs.size(); i++) {
+ for (int i = 0; i < eigenPairs.size(); i++) {
EigenPair eigenPair = eigenPairs.getEigenPair(i);
- if(eigenPair.getEigenvalue() > expectEigenvalue) {
+ if (eigenPair.getEigenvalue() > expectEigenvalue) {
strongEigenPairs.add(eigenPair);
- }
- else {
+ } else {
weakEigenPairs.add(eigenPair);
}
}
// the code using this method doesn't expect an empty strong set,
// if we didn't find any strong ones, we make all vectors strong
- if(strongEigenPairs.size() == 0) {
+ if (strongEigenPairs.size() == 0) {
return new FilteredEigenPairs(new ArrayList<EigenPair>(), weakEigenPairs);
}
return new FilteredEigenPairs(weakEigenPairs, strongEigenPairs);
@@ -127,8 +126,9 @@ public class WeakEigenPairFilter implements EigenPairFilter {
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- DoubleParameter walphaP = new DoubleParameter(EIGENPAIR_FILTER_WALPHA, new GreaterEqualConstraint(0.0), DEFAULT_WALPHA);
- if(config.grab(walphaP)) {
+ DoubleParameter walphaP = new DoubleParameter(EIGENPAIR_FILTER_WALPHA, DEFAULT_WALPHA);
+ walphaP.addConstraint(new GreaterEqualConstraint(0.0));
+ if (config.grab(walphaP)) {
walpha = walphaP.getValue();
}
}
@@ -138,4 +138,4 @@ public class WeakEigenPairFilter implements EigenPairFilter {
return new WeakEigenPairFilter(walpha);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/WeightedCovarianceMatrixBuilder.java b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/WeightedCovarianceMatrixBuilder.java
index fb7f60c3..3066a831 100644
--- a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/WeightedCovarianceMatrixBuilder.java
+++ b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/WeightedCovarianceMatrixBuilder.java
@@ -23,17 +23,17 @@ package de.lmu.ifi.dbs.elki.math.linearalgebra.pca;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.util.Collection;
-import java.util.Iterator;
-
import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
-import de.lmu.ifi.dbs.elki.database.query.DoubleDistanceResultPair;
+import de.lmu.ifi.dbs.elki.database.ids.DistanceDBIDPair;
+import de.lmu.ifi.dbs.elki.database.ids.DoubleDistanceDBIDPair;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.distance.distancefunction.EuclideanDistanceFunction;
import de.lmu.ifi.dbs.elki.distance.distancefunction.PrimitiveDistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResultIter;
import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Centroid;
@@ -41,7 +41,6 @@ import de.lmu.ifi.dbs.elki.math.linearalgebra.CovarianceMatrix;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Matrix;
import de.lmu.ifi.dbs.elki.math.linearalgebra.pca.weightfunctions.ConstantWeight;
import de.lmu.ifi.dbs.elki.math.linearalgebra.pca.weightfunctions.WeightFunction;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.utilities.documentation.Description;
import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
import de.lmu.ifi.dbs.elki.utilities.documentation.Title;
@@ -74,7 +73,7 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ObjectParameter;
@Title("Weighted Covariance Matrix / PCA")
@Description("A PCA modification by using weights while building the covariance matrix, to obtain more stable results")
@Reference(authors = "H.-P. Kriegel, P. Kröger, E. Schubert, A. Zimek", title = "A General Framework for Increasing the Robustness of PCA-based Correlation Clustering Algorithms", booktitle = "Proceedings of the 20th International Conference on Scientific and Statistical Database Management (SSDBM), Hong Kong, China, 2008", url = "http://dx.doi.org/10.1007/978-3-540-69497-7_27")
-public class WeightedCovarianceMatrixBuilder<V extends NumberVector<? extends V, ?>> extends AbstractCovarianceMatrixBuilder<V> {
+public class WeightedCovarianceMatrixBuilder<V extends NumberVector<?>> extends AbstractCovarianceMatrixBuilder<V> {
/**
* Parameter to specify the weight function to use in weighted PCA, must
* implement
@@ -84,7 +83,7 @@ public class WeightedCovarianceMatrixBuilder<V extends NumberVector<? extends V,
* Key: {@code -pca.weight}
* </p>
*/
- public static final OptionID WEIGHT_ID = OptionID.getOrCreateOptionID("pca.weight", "Weight function to use in weighted PCA.");
+ public static final OptionID WEIGHT_ID = new OptionID("pca.weight", "Weight function to use in weighted PCA.");
/**
* Holds the weight function.
@@ -92,7 +91,7 @@ public class WeightedCovarianceMatrixBuilder<V extends NumberVector<? extends V,
protected WeightFunction weightfunction;
/**
- * Holds the distance function used for weight calculation
+ * Holds the distance function used for weight calculation.
*/
// TODO: make configurable?
private PrimitiveDistanceFunction<? super V, DoubleDistance> weightDistance = EuclideanDistanceFunction.STATIC;
@@ -100,7 +99,7 @@ public class WeightedCovarianceMatrixBuilder<V extends NumberVector<? extends V,
/**
* Constructor.
*
- * @param weightfunction
+ * @param weightfunction Weighting function
*/
public WeightedCovarianceMatrixBuilder(WeightFunction weightfunction) {
super();
@@ -112,19 +111,23 @@ public class WeightedCovarianceMatrixBuilder<V extends NumberVector<? extends V,
* distance information, we'll need to compute it ourselves. Covariance is
* tied to Euclidean distance, so it probably does not make much sense to add
* support for other distance functions?
+ *
+ * @param ids Database ids to process
+ * @param relation Relation to process
+ * @return Covariance matrix
*/
@Override
- public Matrix processIds(DBIDs ids, Relation<? extends V> database) {
- final int dim = DatabaseUtil.dimensionality(database);
+ public Matrix processIds(DBIDs ids, Relation<? extends V> relation) {
+ final int dim = RelationUtil.dimensionality(relation);
final CovarianceMatrix cmat = new CovarianceMatrix(dim);
- final V centroid = Centroid.make(database, ids).toVector(database);
+ final V centroid = Centroid.make(relation, ids).toVector(relation);
// find maximum distance
double maxdist = 0.0;
double stddev = 0.0;
{
for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
- V obj = database.get(iter);
+ V obj = relation.get(iter);
double distance = weightDistance.distance(centroid, obj).doubleValue();
stddev += distance * distance;
if(distance > maxdist) {
@@ -139,7 +142,7 @@ public class WeightedCovarianceMatrixBuilder<V extends NumberVector<? extends V,
}
for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
- V obj = database.get(iter);
+ V obj = relation.get(iter);
double distance = weightDistance.distance(centroid, obj).doubleValue();
double weight = weightfunction.getWeight(distance, maxdist, stddev);
cmat.put(obj, weight);
@@ -148,18 +151,19 @@ public class WeightedCovarianceMatrixBuilder<V extends NumberVector<? extends V,
}
/**
- * Compute Covariance Matrix for a QueryResult Collection
+ * Compute Covariance Matrix for a QueryResult Collection.
*
* By default it will just collect the ids and run processIds
*
* @param results a collection of QueryResults
* @param database the database used
* @param k number of elements to process
+ * @param <D> distance type
* @return Covariance Matrix
*/
@Override
- public <D extends NumberDistance<?, ?>> Matrix processQueryResults(Collection<? extends DistanceResultPair<D>> results, Relation<? extends V> database, int k) {
- final int dim = DatabaseUtil.dimensionality(database);
+ public <D extends NumberDistance<D, ?>> Matrix processQueryResults(DistanceDBIDResult<D> results, Relation<? extends V> database, int k) {
+ final int dim = RelationUtil.dimensionality(database);
final CovarianceMatrix cmat = new CovarianceMatrix(dim);
// avoid bad parameters
@@ -172,11 +176,11 @@ public class WeightedCovarianceMatrixBuilder<V extends NumberVector<? extends V,
double stddev = 0.0;
{
int i = 0;
- for(Iterator<? extends DistanceResultPair<D>> it = results.iterator(); it.hasNext() && i < k; i++) {
- DistanceResultPair<D> res = it.next();
+ for (DistanceDBIDResultIter<D> it = results.iter(); it.valid() && i < k; it.advance(), k++) {
+ DistanceDBIDPair<D> res = it.getDistancePair();
final double dist;
- if(res instanceof DoubleDistanceResultPair) {
- dist = ((DoubleDistanceResultPair) res).getDoubleDistance();
+ if(res instanceof DoubleDistanceDBIDPair) {
+ dist = ((DoubleDistanceDBIDPair) res).doubleDistance();
}
else {
dist = res.getDistance().doubleValue();
@@ -194,11 +198,11 @@ public class WeightedCovarianceMatrixBuilder<V extends NumberVector<? extends V,
// calculate weighted PCA
int i = 0;
- for(Iterator<? extends DistanceResultPair<D>> it = results.iterator(); it.hasNext() && i < k; i++) {
- DistanceResultPair<? extends NumberDistance<?, ?>> res = it.next();
+ for (DistanceDBIDResultIter<D> it = results.iter(); it.valid() && i < k; it.advance(), k++) {
+ DistanceDBIDPair<D> res = it.getDistancePair();
final double dist;
- if(res instanceof DoubleDistanceResultPair) {
- dist = ((DoubleDistanceResultPair) res).getDoubleDistance();
+ if(res instanceof DoubleDistanceDBIDPair) {
+ dist = ((DoubleDistanceDBIDPair) res).doubleDistance();
}
else {
dist = res.getDistance().doubleValue();
@@ -218,7 +222,10 @@ public class WeightedCovarianceMatrixBuilder<V extends NumberVector<? extends V,
*
* @apiviz.exclude
*/
- public static class Parameterizer<V extends NumberVector<V, ?>> extends AbstractParameterizer {
+ public static class Parameterizer<V extends NumberVector<?>> extends AbstractParameterizer {
+ /**
+ * Weight function.
+ */
protected WeightFunction weightfunction = null;
@Override
diff --git a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/weightfunctions/GaussStddevWeight.java b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/weightfunctions/GaussStddevWeight.java
index f73d4a7d..9ef589d6 100644
--- a/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/weightfunctions/GaussStddevWeight.java
+++ b/src/de/lmu/ifi/dbs/elki/math/linearalgebra/pca/weightfunctions/GaussStddevWeight.java
@@ -40,7 +40,7 @@ public final class GaussStddevWeight implements WeightFunction {
*
* In fact, in most use cases we could leave this away.
*/
- private final static double scaling = 1 / MathUtil.SQRTTWOPI;
+ private static final double scaling = 1 / MathUtil.SQRTTWOPI;
/**
* Get Gaussian Weight using standard deviation for scaling. max is ignored.
diff --git a/src/de/lmu/ifi/dbs/elki/math/scales/Scales.java b/src/de/lmu/ifi/dbs/elki/math/scales/Scales.java
index edad211d..097b6fce 100644
--- a/src/de/lmu/ifi/dbs/elki/math/scales/Scales.java
+++ b/src/de/lmu/ifi/dbs/elki/math/scales/Scales.java
@@ -26,46 +26,53 @@ package de.lmu.ifi.dbs.elki.math.scales;
import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.math.DoubleMinMax;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.utilities.exceptions.AbortException;
/**
- * Scales helper class.
- * Currently, this will just compute a linear scale for each axis.
- * It is planned to add functionality to include some analysis to
- * be able to automatically choose log scales when appropriate.
+ * Scales helper class. Currently, this will just compute a linear scale for
+ * each axis. It is planned to add functionality to include some analysis to be
+ * able to automatically choose log scales when appropriate.
*
* @author Erich Schubert
- *
+ *
* @apiviz.has LinearScale oneway - - computes
*/
-public class Scales {
+public final class Scales {
+ /**
+ * Fake constructor.
+ */
+ private Scales() {
+ // Do not instantiate.
+ }
+
/**
* Compute a linear scale for each dimension.
*
* @param <O> vector type
* @param db Database
- * @return Scales, indexed starting with 0 (like Vector, not database objects!)
+ * @return Scales, indexed starting with 0 (like Vector, not database
+ * objects!)
*/
- public static <O extends NumberVector<?,? extends Number>> LinearScale[] calcScales(Relation<O> db) {
+ public static <O extends NumberVector<? extends Number>> LinearScale[] calcScales(Relation<O> db) {
if (db == null) {
throw new AbortException("No database was given to Scales.calcScales.");
}
- int dim = DatabaseUtil.dimensionality(db);
- DoubleMinMax minmax[] = DoubleMinMax.newArray(dim);
- LinearScale scales[] = new LinearScale[dim];
-
+ int dim = RelationUtil.dimensionality(db);
+ DoubleMinMax[] minmax = DoubleMinMax.newArray(dim);
+ LinearScale[] scales = new LinearScale[dim];
+
// analyze data
- for(DBIDIter iditer = db.iterDBIDs(); iditer.valid(); iditer.advance()) {
+ for (DBIDIter iditer = db.iterDBIDs(); iditer.valid(); iditer.advance()) {
O v = db.get(iditer);
- for(int d = 0; d < dim; d++) {
- minmax[d].put(v.doubleValue(d+1));
+ for (int d = 0; d < dim; d++) {
+ minmax[d].put(v.doubleValue(d));
}
}
-
+
// generate scales
- for(int d = 0; d < dim; d++) {
+ for (int d = 0; d < dim; d++) {
scales[d] = new LinearScale(minmax[d].getMin(), minmax[d].getMax());
}
return scales;
diff --git a/src/de/lmu/ifi/dbs/elki/math/spacefillingcurves/AbstractSpatialSorter.java b/src/de/lmu/ifi/dbs/elki/math/spacefillingcurves/AbstractSpatialSorter.java
index 942fe64b..7c1265dd 100644
--- a/src/de/lmu/ifi/dbs/elki/math/spacefillingcurves/AbstractSpatialSorter.java
+++ b/src/de/lmu/ifi/dbs/elki/math/spacefillingcurves/AbstractSpatialSorter.java
@@ -59,37 +59,37 @@ public abstract class AbstractSpatialSorter implements SpatialSorter {
* @param dim Dimension to sort by
* @param threshold Threshold value
* @param desc Inversion flag
+ * @param <T> Object type
* @return Pivot position
*/
protected <T extends SpatialComparable> int pivotizeList1D(List<T> objs, int start, int end, int dim, double threshold, boolean desc) {
threshold = 2 * threshold; // faster
int s = start, e = end;
- while(s < e) {
- if(!desc) {
+ while (s < e) {
+ if (!desc) {
double sminmax = getMinPlusMaxObject(objs, s, dim);
- while((sminmax < threshold) && s + 1 <= e && s + 1 < end) {
+ while ((sminmax < threshold) && s + 1 <= e && s + 1 < end) {
s++;
sminmax = getMinPlusMaxObject(objs, s, dim);
}
double eminmax = getMinPlusMaxObject(objs, e - 1, dim);
- while((eminmax >= threshold) && s < e - 1 && start < e - 1) {
+ while ((eminmax >= threshold) && s < e - 1 && start < e - 1) {
e--;
eminmax = getMinPlusMaxObject(objs, e - 1, dim);
}
- }
- else {
+ } else {
double sminmax = getMinPlusMaxObject(objs, s, dim);
- while((sminmax > threshold) && s + 1 <= e && s + 1 < end) {
+ while ((sminmax > threshold) && s + 1 <= e && s + 1 < end) {
s++;
sminmax = getMinPlusMaxObject(objs, s, dim);
}
double eminmax = getMinPlusMaxObject(objs, e - 1, dim);
- while((eminmax <= threshold) && s < e - 1 && start < e - 1) {
+ while ((eminmax <= threshold) && s < e - 1 && start < e - 1) {
e--;
eminmax = getMinPlusMaxObject(objs, e - 1, dim);
}
}
- if(s >= e) {
+ if (s >= e) {
assert (s == e);
break;
}
@@ -102,7 +102,7 @@ public abstract class AbstractSpatialSorter implements SpatialSorter {
}
/**
- * Compute getMin(dim) + getMax(dim) for the spatial object
+ * Compute getMin(dim) + getMax(dim) for the spatial object.
*
* @param objs Objects
* @param s index
@@ -120,25 +120,25 @@ public abstract class AbstractSpatialSorter implements SpatialSorter {
* @param objs Objects
* @return Array of min, max pairs (length = 2 * dim)
*/
- public static <T extends SpatialComparable> double[] computeMinMax(List<T> objs) {
+ public static double[] computeMinMax(List<? extends SpatialComparable> objs) {
final int dim = objs.get(0).getDimensionality();
// Compute min and max for each dimension:
- double[] mm = new double[dim * 2];
+ double[] mm = new double[dim << 1];
{
- for(int d = 0; d < dim; d++) {
- mm[d * 2] = Double.POSITIVE_INFINITY;
- mm[d * 2 + 1] = Double.NEGATIVE_INFINITY;
+ for (int d = 0; d < dim; d++) {
+ mm[d << 1] = Double.POSITIVE_INFINITY;
+ mm[(d << 1) + 1] = Double.NEGATIVE_INFINITY;
}
- for(SpatialComparable obj : objs) {
- for(int d = 0; d < dim; d++) {
- mm[2 * d] = Math.min(mm[2 * d], obj.getMin(d + 1));
- mm[2 * d + 1] = Math.max(mm[2 * d + 1], obj.getMax(d + 1));
+ for (SpatialComparable obj : objs) {
+ for (int d = 0; d < dim; d++) {
+ mm[d << 1] = Math.min(mm[d << 1], obj.getMin(d));
+ mm[(d << 1) + 1] = Math.max(mm[(d << 1) + 1], obj.getMax(d));
}
}
- for(int d = 0; d < dim; d++) {
- assert (mm[2 * d] <= mm[2 * d + 1]);
+ for (int d = 0; d < dim; d++) {
+ assert (mm[d << 1] <= mm[(d << 1) + 1]);
}
}
return mm;
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/math/spacefillingcurves/BinarySplitSpatialSorter.java b/src/de/lmu/ifi/dbs/elki/math/spacefillingcurves/BinarySplitSpatialSorter.java
index 0b45022c..1ba58511 100644
--- a/src/de/lmu/ifi/dbs/elki/math/spacefillingcurves/BinarySplitSpatialSorter.java
+++ b/src/de/lmu/ifi/dbs/elki/math/spacefillingcurves/BinarySplitSpatialSorter.java
@@ -73,6 +73,7 @@ public class BinarySplitSpatialSorter extends AbstractSpatialSorter {
* @param curdim Current dimension
* @param dims Number of dimensions
* @param comp Comparator to use
+ * @param <T> Object type
*/
private <T extends SpatialComparable> void binarySplitSort(List<T> objs, final int start, final int end, int curdim, final int dims, DimC comp) {
final int mid = start + ((end - start) >>> 1);
@@ -80,7 +81,7 @@ public class BinarySplitSpatialSorter extends AbstractSpatialSorter {
comp.dim = curdim;
QuickSelect.quickSelect(objs, comp, start, end, mid);
// Recurse
- final int nextdim = (curdim % dims) + 1;
+ final int nextdim = (curdim + 1) % dims;
if(start < mid - 1) {
binarySplitSort(objs, start, mid, nextdim, dims, comp);
}
@@ -100,6 +101,9 @@ public class BinarySplitSpatialSorter extends AbstractSpatialSorter {
* @apiviz.exclude
*/
private static class DimC implements Comparator<SpatialComparable> {
+ /**
+ * Dimension.
+ */
public int dim = -1;
@Override
diff --git a/src/de/lmu/ifi/dbs/elki/math/spacefillingcurves/HilbertSpatialSorter.java b/src/de/lmu/ifi/dbs/elki/math/spacefillingcurves/HilbertSpatialSorter.java
index 9b4af341..317e47c1 100644
--- a/src/de/lmu/ifi/dbs/elki/math/spacefillingcurves/HilbertSpatialSorter.java
+++ b/src/de/lmu/ifi/dbs/elki/math/spacefillingcurves/HilbertSpatialSorter.java
@@ -61,19 +61,19 @@ public class HilbertSpatialSorter extends AbstractSpatialSorter {
final int dim = minmax.length >> 1;
List<HilbertRef<T>> tmp = new ArrayList<HilbertRef<T>>(end - start);
int[] buf = new int[dim];
- for(int i = start; i < end; i++) {
+ for (int i = start; i < end; i++) {
T v = objs.get(i);
// Convert into integers
- for(int d = 0; d < dim; d++) {
- double val = (v.getMin(d + 1) + v.getMax(d + 1)) / 2;
- val = Integer.MAX_VALUE * ((val - minmax[2 * d]) / (minmax[2 * d + 1] - minmax[2 * d]));
+ for (int d = 0, d2 = 0; d < dim; d++, d2 += 2) {
+ double val = (v.getMin(d) + v.getMax(d)) * .5;
+ val = Integer.MAX_VALUE * ((val - minmax[d2]) / (minmax[d2 + 1] - minmax[d2]));
buf[d] = (int) val;
}
tmp.add(new HilbertRef<T>(v, coordinatesToHilbert(buf, Integer.SIZE - 1, 1)));
}
// Sort and copy back
Collections.sort(tmp);
- for(int i = start; i < end; i++) {
+ for (int i = start; i < end; i++) {
objs.set(i, tmp.get(i - start).vec);
}
}
@@ -86,19 +86,20 @@ public class HilbertSpatialSorter extends AbstractSpatialSorter {
*/
private static class HilbertRef<T extends SpatialComparable> implements Comparable<HilbertRef<T>> {
/**
- * The referenced object
+ * The referenced object.
*/
protected T vec;
/**
- * Hilbert representation
+ * Hilbert representation.
*/
protected long[] bits;
/**
* Constructor.
*
- * @param vec
+ * @param vec Vector
+ * @param bits Bit representation
*/
protected HilbertRef(T vec, long[] bits) {
super();
@@ -118,6 +119,7 @@ public class HilbertSpatialSorter extends AbstractSpatialSorter {
*
* @param coords Original coordinates
* @param bitsperdim Number of bits to use.
+ * @param offset offset
* @return Hilbert address
*/
public static long[] coordinatesToHilbert(long[] coords, int bitsperdim, int offset) {
@@ -127,7 +129,7 @@ public class HilbertSpatialSorter extends AbstractSpatialSorter {
int rotation = 0;
long[] refl = BitsUtil.zero(numdim);
- for(int i = 0; i < bitsperdim; i++) {
+ for (int i = 0; i < bitsperdim; i++) {
final long[] hist = interleaveBits(coords, i + offset);
// System.err.println(BitsUtil.toString(hist,
// numdim)+" rot:"+rotation+" refl: "+BitsUtil.toString(refl, numdim));
@@ -141,7 +143,7 @@ public class HilbertSpatialSorter extends AbstractSpatialSorter {
// numbits)+" bits: "+BitsUtil.toString(bits, numdim));
refl = hist;
BitsUtil.flipI(refl, rotation);
- if(!BitsUtil.get(bits, 0)) {
+ if (!BitsUtil.get(bits, 0)) {
BitsUtil.flipI(refl, (nextrot - 1 + numdim) % numdim);
}
rotation = nextrot;
@@ -151,11 +153,12 @@ public class HilbertSpatialSorter extends AbstractSpatialSorter {
}
/**
- * Interleave one int per dimension (using the "bitsperdim" highest bits) to
- * a hilbert address.
+ * Interleave one int per dimension (using the "bitsperdim" highest bits) to a
+ * hilbert address.
*
* @param coords Original coordinates
* @param bitsperdim Number of bits to use.
+ * @param offset offset
* @return Hilbert address
*/
public static long[] coordinatesToHilbert(int[] coords, int bitsperdim, int offset) {
@@ -165,7 +168,7 @@ public class HilbertSpatialSorter extends AbstractSpatialSorter {
int rotation = 0;
long[] refl = BitsUtil.zero(numdim);
- for(int i = 0; i < bitsperdim; i++) {
+ for (int i = 0; i < bitsperdim; i++) {
final long[] hist = interleaveBits(coords, i + offset);
// System.err.println(BitsUtil.toString(hist,
// numdim)+" rot:"+rotation+" refl: "+BitsUtil.toString(refl, numdim));
@@ -179,7 +182,7 @@ public class HilbertSpatialSorter extends AbstractSpatialSorter {
// numbits)+" bits: "+BitsUtil.toString(bits, numdim));
refl = hist;
BitsUtil.flipI(refl, rotation);
- if(!BitsUtil.get(bits, 0)) {
+ if (!BitsUtil.get(bits, 0)) {
BitsUtil.flipI(refl, (nextrot - 1 + numdim) % numdim);
}
rotation = nextrot;
@@ -194,6 +197,7 @@ public class HilbertSpatialSorter extends AbstractSpatialSorter {
*
* @param coords Original coordinates
* @param bitsperdim Number of bits to use.
+ * @param offset offset
* @return Hilbert address
*/
public static long[] coordinatesToHilbert(short[] coords, int bitsperdim, int offset) {
@@ -203,7 +207,7 @@ public class HilbertSpatialSorter extends AbstractSpatialSorter {
int rotation = 0;
long[] refl = BitsUtil.zero(numdim);
- for(int i = 0; i < bitsperdim; i++) {
+ for (int i = 0; i < bitsperdim; i++) {
final long[] hist = interleaveBits(coords, i + offset);
// System.err.println(BitsUtil.toString(hist,
// numdim)+" rot:"+rotation+" refl: "+BitsUtil.toString(refl, numdim));
@@ -217,7 +221,7 @@ public class HilbertSpatialSorter extends AbstractSpatialSorter {
// numbits)+" bits: "+BitsUtil.toString(bits, numdim));
refl = hist;
BitsUtil.flipI(refl, rotation);
- if(!BitsUtil.get(bits, 0)) {
+ if (!BitsUtil.get(bits, 0)) {
BitsUtil.flipI(refl, (nextrot - 1 + numdim) % numdim);
}
rotation = nextrot;
@@ -232,16 +236,17 @@ public class HilbertSpatialSorter extends AbstractSpatialSorter {
*
* @param coords Original coordinates
* @param bitsperdim Number of bits to use.
+ * @param offset offset
* @return Hilbert address
*/
public static long[] coordinatesToHilbert(byte[] coords, int bitsperdim, int offset) {
final int numdim = coords.length;
final int numbits = numdim * bitsperdim;
final long[] output = BitsUtil.zero(numbits);
-
+
int rotation = 0;
long[] refl = BitsUtil.zero(numdim);
- for(int i = 0; i < bitsperdim; i++) {
+ for (int i = 0; i < bitsperdim; i++) {
final long[] hist = interleaveBits(coords, i + offset);
// System.err.println(BitsUtil.toString(hist,
// numdim)+" rot:"+rotation+" refl: "+BitsUtil.toString(refl, numdim));
@@ -255,12 +260,12 @@ public class HilbertSpatialSorter extends AbstractSpatialSorter {
// numbits)+" bits: "+BitsUtil.toString(bits, numdim));
refl = hist;
BitsUtil.flipI(refl, rotation);
- if(!BitsUtil.get(bits, 0)) {
+ if (!BitsUtil.get(bits, 0)) {
BitsUtil.flipI(refl, (nextrot - 1 + numdim) % numdim);
}
rotation = nextrot;
}
-
+
return output;
}
@@ -276,8 +281,8 @@ public class HilbertSpatialSorter extends AbstractSpatialSorter {
final long[] bitset = BitsUtil.zero(numdim);
// convert longValues into zValues
final long mask = 1L << 63 - iter;
- for(int dim = 0; dim < numdim; dim++) {
- if((coords[dim] & mask) != 0) {
+ for (int dim = 0; dim < numdim; dim++) {
+ if ((coords[dim] & mask) != 0) {
BitsUtil.setI(bitset, dim);
}
}
@@ -296,8 +301,8 @@ public class HilbertSpatialSorter extends AbstractSpatialSorter {
final long[] bitset = BitsUtil.zero(numdim);
// convert longValues into zValues
final long mask = 1L << 31 - iter;
- for(int dim = 0; dim < numdim; dim++) {
- if((coords[dim] & mask) != 0) {
+ for (int dim = 0; dim < numdim; dim++) {
+ if ((coords[dim] & mask) != 0) {
BitsUtil.setI(bitset, dim);
}
}
@@ -316,8 +321,8 @@ public class HilbertSpatialSorter extends AbstractSpatialSorter {
final long[] bitset = BitsUtil.zero(numdim);
// convert longValues into zValues
final long mask = 1L << 15 - iter;
- for(int dim = 0; dim < numdim; dim++) {
- if((coords[dim] & mask) != 0) {
+ for (int dim = 0; dim < numdim; dim++) {
+ if ((coords[dim] & mask) != 0) {
BitsUtil.setI(bitset, dim);
}
}
@@ -336,11 +341,11 @@ public class HilbertSpatialSorter extends AbstractSpatialSorter {
final long[] bitset = BitsUtil.zero(numdim);
// convert longValues into zValues
final long mask = 1L << 7 - iter;
- for(int dim = 0; dim < numdim; dim++) {
- if((coords[dim] & mask) != 0) {
+ for (int dim = 0; dim < numdim; dim++) {
+ if ((coords[dim] & mask) != 0) {
BitsUtil.setI(bitset, dim);
}
}
return bitset;
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/math/spacefillingcurves/PeanoSpatialSorter.java b/src/de/lmu/ifi/dbs/elki/math/spacefillingcurves/PeanoSpatialSorter.java
index 50cf1946..865197ae 100644
--- a/src/de/lmu/ifi/dbs/elki/math/spacefillingcurves/PeanoSpatialSorter.java
+++ b/src/de/lmu/ifi/dbs/elki/math/spacefillingcurves/PeanoSpatialSorter.java
@@ -95,6 +95,7 @@ public class PeanoSpatialSorter extends AbstractSpatialSorter {
* @param desc Current ordering
*/
protected <T extends SpatialComparable> void peanoSort(List<T> objs, int start, int end, double[] mms, int dim, BitSet bits, boolean desc) {
+ final int dims = mms.length >> 1;
// Find the splitting points.
final double min = mms[2 * dim], max = mms[2 * dim + 1];
final double tfirst = (min + min + max) / 3.;
@@ -116,14 +117,14 @@ public class PeanoSpatialSorter extends AbstractSpatialSorter {
// Split the data set into three parts
int fsplit, ssplit;
if(!inv) {
- fsplit = pivotizeList1D(objs, start, end, dim + 1, tfirst, false);
- ssplit = (fsplit < end - 1) ? pivotizeList1D(objs, fsplit, end, dim + 1, tsecond, false) : fsplit;
+ fsplit = pivotizeList1D(objs, start, end, dim, tfirst, false);
+ ssplit = (fsplit < end - 1) ? pivotizeList1D(objs, fsplit, end, dim, tsecond, false) : fsplit;
}
else {
- fsplit = pivotizeList1D(objs, start, end, dim + 1, tsecond, true);
- ssplit = (fsplit < end - 1) ? pivotizeList1D(objs, fsplit, end, dim + 1, tfirst, true) : fsplit;
+ fsplit = pivotizeList1D(objs, start, end, dim, tsecond, true);
+ ssplit = (fsplit < end - 1) ? pivotizeList1D(objs, fsplit, end, dim, tfirst, true) : fsplit;
}
- int nextdim = (dim + 1) % objs.get(0).getDimensionality();
+ int nextdim = (dim + 1) % dims;
// Do we need to update the min/max values?
if(start < fsplit - 1) {
mms[2 * dim] = !inv ? min : tsecond;
diff --git a/src/de/lmu/ifi/dbs/elki/math/spacefillingcurves/SpatialSorter.java b/src/de/lmu/ifi/dbs/elki/math/spacefillingcurves/SpatialSorter.java
index 2473dff5..fe23a854 100644
--- a/src/de/lmu/ifi/dbs/elki/math/spacefillingcurves/SpatialSorter.java
+++ b/src/de/lmu/ifi/dbs/elki/math/spacefillingcurves/SpatialSorter.java
@@ -39,7 +39,7 @@ public interface SpatialSorter {
* @param <T> actual type we sort
* @param objs the spatial objects to be sorted
*/
- public <T extends SpatialComparable> void sort(List<T> objs);
+ <T extends SpatialComparable> void sort(List<T> objs);
/**
* Sort part of the list (start to end).
@@ -50,5 +50,5 @@ public interface SpatialSorter {
* @param end End of range (e.g. <code>site()</code>)
* @param minmax Array with dim pairs of (min, max) of value ranges
*/
- public <T extends SpatialComparable> void sort(List<T> objs, int start, int end, double[] minmax);
-}
+ <T extends SpatialComparable> void sort(List<T> objs, int start, int end, double[] minmax);
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/math/spacefillingcurves/ZCurveSpatialSorter.java b/src/de/lmu/ifi/dbs/elki/math/spacefillingcurves/ZCurveSpatialSorter.java
index b8fc63bd..c5a91699 100644
--- a/src/de/lmu/ifi/dbs/elki/math/spacefillingcurves/ZCurveSpatialSorter.java
+++ b/src/de/lmu/ifi/dbs/elki/math/spacefillingcurves/ZCurveSpatialSorter.java
@@ -68,7 +68,7 @@ public class ZCurveSpatialSorter extends AbstractSpatialSorter {
return;
}
}
- int split = pivotizeList1D(objs, start, end, dim + 1, spos, false);
+ int split = pivotizeList1D(objs, start, end, dim, spos, false);
assert (start <= split && split <= end);
int nextdim = (dim + 1) % objs.get(0).getDimensionality();
// LoggingUtil.warning("dim: " + dim + " min: " + min + " split: " + spos +
diff --git a/src/de/lmu/ifi/dbs/elki/math/spacefillingcurves/ZCurveTransformer.java b/src/de/lmu/ifi/dbs/elki/math/spacefillingcurves/ZCurveTransformer.java
index 108721eb..26137cb3 100644
--- a/src/de/lmu/ifi/dbs/elki/math/spacefillingcurves/ZCurveTransformer.java
+++ b/src/de/lmu/ifi/dbs/elki/math/spacefillingcurves/ZCurveTransformer.java
@@ -30,7 +30,7 @@ import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
/**
* Class to transform a relation to its Z coordinates.
@@ -39,17 +39,17 @@ import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
*/
public class ZCurveTransformer {
/**
- * Maximum values in each dimension
+ * Maximum values in each dimension.
*/
private final double[] maxValues;
/**
- * Minimum values in each dimension
+ * Minimum values in each dimension.
*/
private final double[] minValues;
/**
- * Dimensionality
+ * Dimensionality.
*/
private final int dimensionality;
@@ -59,8 +59,8 @@ public class ZCurveTransformer {
* @param relation Relation to transform
* @param ids IDs subset to process
*/
- public ZCurveTransformer(Relation<? extends NumberVector<?, ?>> relation, DBIDs ids) {
- this.dimensionality = DatabaseUtil.dimensionality(relation);
+ public ZCurveTransformer(Relation<? extends NumberVector<?>> relation, DBIDs ids) {
+ this.dimensionality = RelationUtil.dimensionality(relation);
this.minValues = new double[dimensionality];
this.maxValues = new double[dimensionality];
@@ -68,9 +68,9 @@ public class ZCurveTransformer {
Arrays.fill(minValues, Double.POSITIVE_INFINITY);
Arrays.fill(maxValues, Double.NEGATIVE_INFINITY);
for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
- NumberVector<?, ?> vector = relation.get(iter);
+ NumberVector<?> vector = relation.get(iter);
for(int dim = 0; dim < dimensionality; ++dim) {
- double dimValue = vector.doubleValue(dim + 1);
+ double dimValue = vector.doubleValue(dim);
minValues[dim] = Math.min(minValues[dim], dimValue);
maxValues[dim] = Math.max(maxValues[dim], dimValue);
}
@@ -84,7 +84,7 @@ public class ZCurveTransformer {
* @return Z curve value as bigint
*/
@Deprecated
- public BigInteger asBigInteger(NumberVector<?, ?> vector) {
+ public BigInteger asBigInteger(NumberVector<?> vector) {
return new BigInteger(asByteArray(vector));
}
@@ -94,13 +94,13 @@ public class ZCurveTransformer {
* @param vector Vector to transform
* @return Z curve value as byte array
*/
- public byte[] asByteArray(NumberVector<?, ?> vector) {
+ public byte[] asByteArray(NumberVector<?> vector) {
final long[] longValueList = new long[dimensionality];
for(int dim = 0; dim < dimensionality; ++dim) {
final double minValue = minValues[dim];
final double maxValue = maxValues[dim];
- double dimValue = vector.doubleValue(dim + 1);
+ double dimValue = vector.doubleValue(dim);
dimValue = (dimValue - minValue) / (maxValue - minValue);
longValueList[dim] = (long) (dimValue * (Long.MAX_VALUE));
@@ -120,5 +120,4 @@ public class ZCurveTransformer {
}
return bytes;
}
-
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/math/statistics/EpanechnikovKernelDensityFunction.java b/src/de/lmu/ifi/dbs/elki/math/statistics/EpanechnikovKernelDensityFunction.java
index ed9d8d58..25aa2ea7 100644
--- a/src/de/lmu/ifi/dbs/elki/math/statistics/EpanechnikovKernelDensityFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/math/statistics/EpanechnikovKernelDensityFunction.java
@@ -23,6 +23,7 @@ package de.lmu.ifi.dbs.elki.math.statistics;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
/**
* Epanechnikov kernel density estimator.
@@ -49,4 +50,18 @@ public final class EpanechnikovKernelDensityFunction implements KernelDensityFun
* Static instance.
*/
public static final EpanechnikovKernelDensityFunction KERNEL = new EpanechnikovKernelDensityFunction();
+
+ /**
+ * Parameterization stub.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public static class Parameterizer extends AbstractParameterizer {
+ @Override
+ protected EpanechnikovKernelDensityFunction makeInstance() {
+ return KERNEL;
+ }
+ }
}
diff --git a/src/de/lmu/ifi/dbs/elki/math/statistics/GaussianKernelDensityFunction.java b/src/de/lmu/ifi/dbs/elki/math/statistics/GaussianKernelDensityFunction.java
index 744a9108..2cd15408 100644
--- a/src/de/lmu/ifi/dbs/elki/math/statistics/GaussianKernelDensityFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/math/statistics/GaussianKernelDensityFunction.java
@@ -23,6 +23,7 @@ package de.lmu.ifi.dbs.elki.math.statistics;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
/**
* Gaussian kernel density estimator.
@@ -51,4 +52,18 @@ public final class GaussianKernelDensityFunction implements KernelDensityFunctio
* Static instance.
*/
public static final GaussianKernelDensityFunction KERNEL = new GaussianKernelDensityFunction();
+
+ /**
+ * Parameterization stub.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public static class Parameterizer extends AbstractParameterizer {
+ @Override
+ protected GaussianKernelDensityFunction makeInstance() {
+ return KERNEL;
+ }
+ }
}
diff --git a/src/de/lmu/ifi/dbs/elki/math/statistics/KernelDensityEstimator.java b/src/de/lmu/ifi/dbs/elki/math/statistics/KernelDensityEstimator.java
index 3bb0e1f6..d7ffefb8 100644
--- a/src/de/lmu/ifi/dbs/elki/math/statistics/KernelDensityEstimator.java
+++ b/src/de/lmu/ifi/dbs/elki/math/statistics/KernelDensityEstimator.java
@@ -71,7 +71,7 @@ public class KernelDensityEstimator {
dens = new double[data.length];
var = new double[data.length];
- double halfwidth = ((max - min) / windows) / 2;
+ double halfwidth = ((max - min) / windows) * .5;
// collect data points
for(int current = 0; current < data.length; current++) {
@@ -84,7 +84,7 @@ public class KernelDensityEstimator {
}
double realwidth = (Math.min(data[current] + halfwidth, max) - Math.max(min, data[current] - halfwidth));
double weight = realwidth / (2 * halfwidth);
- dens[current] = value / (data.length * realwidth / 2);
+ dens[current] = value / (data.length * realwidth * .5);
var[current] = 1 / weight;
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/math/statistics/MultipleLinearRegression.java b/src/de/lmu/ifi/dbs/elki/math/statistics/MultipleLinearRegression.java
index 0e674146..79c84701 100644
--- a/src/de/lmu/ifi/dbs/elki/math/statistics/MultipleLinearRegression.java
+++ b/src/de/lmu/ifi/dbs/elki/math/statistics/MultipleLinearRegression.java
@@ -138,7 +138,7 @@ public class MultipleLinearRegression {
*/
@Override
public String toString() {
- StringBuffer msg = new StringBuffer();
+ StringBuilder msg = new StringBuilder();
msg.append("x = ").append(FormatUtil.format(x, 9, 4));
msg.append("\ny = ").append(FormatUtil.format(y, 9, 4));
msg.append("\nb = ").append(FormatUtil.format(b, 9, 4));
diff --git a/src/de/lmu/ifi/dbs/elki/math/statistics/StudentDistribution.java b/src/de/lmu/ifi/dbs/elki/math/statistics/StudentDistribution.java
deleted file mode 100644
index a8f45f9d..00000000
--- a/src/de/lmu/ifi/dbs/elki/math/statistics/StudentDistribution.java
+++ /dev/null
@@ -1,189 +0,0 @@
-package de.lmu.ifi.dbs.elki.math.statistics;
-
-/*
- This file is part of ELKI:
- Environment for Developing KDD-Applications Supported by Index-Structures
-
- Copyright (C) 2012
- Ludwig-Maximilians-Universität München
- Lehr- und Forschungseinheit für Datenbanksysteme
- ELKI Development Team
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-import gnu.trove.map.TDoubleDoubleMap;
-import gnu.trove.map.TIntObjectMap;
-import gnu.trove.map.hash.TDoubleDoubleHashMap;
-import gnu.trove.map.hash.TIntObjectHashMap;
-
-/**
- * Tabelarizes the values for student distribution.
- *
- * @author Elke Achtert
- */
-public class StudentDistribution {
- /**
- * Available alpha values.
- */
- public static double _6000 = 0.6;
-
- /**
- * Available alpha values.
- */
- public static double _8000 = 0.8;
-
- /**
- * Available alpha values.
- */
- public static double _9000 = 0.9;
-
- /**
- * Available alpha values.
- */
- public static double _9500 = 0.95;
-
- /**
- * Available alpha values.
- */
- public static double _9750 = 0.975;
-
- /**
- * Available alpha values.
- */
- public static double _9900 = 0.99;
-
- /**
- * Available alpha values.
- */
- public static double _9950 = 0.995;
-
- /**
- * Available alpha values.
- */
- public static double _9990 = 0.999;
-
- /**
- * Available alpha values.
- */
- public static double _9995 = 0.9995;
-
- /**
- * Available alpha values.
- */
- public static double _4000 = 0.4;
-
- /**
- * Available alpha values.
- */
- public static double _2000 = 0.2;
-
- /**
- * Available alpha values.
- */
- public static double _1000 = 0.1;
-
- /**
- * Available alpha values.
- */
- public static double _0500 = 0.05;
-
- /**
- * Available alpha values.
- */
- public static double _0250 = 0.025;
-
- /**
- * Available alpha values.
- */
- public static double _0100 = 0.01;
-
- /**
- * Available alpha values.
- */
- public static double _0050 = 0.005;
-
- /**
- * Available alpha values.
- */
- public static double _0010 = 0.001;
-
- /**
- * Available alpha values.
- */
- public static double _0005 = 0.005;
-
- /**
- * Holds the t-values.
- */
- private static TIntObjectMap<TDoubleDoubleMap> tValues = new TIntObjectHashMap<TDoubleDoubleMap>();
-
- static {
- put(31, new double[] { 0.2533, 0.8416, 1.2816, 1.6449, 1.96, 2.3263, 2.5758, 3.0903, 3.2906 });
- }
-
- /**
- * Returns the t-value for the given alpha-value and degree of freedom.
- *
- * @param alpha the alpha value
- * @param n the degree of freedom
- * @return the t-value for the given alpha-value and degree of freedom
- */
- public static double tValue(double alpha, int n) {
- if(n > 30) {
- n = 31;
- }
- TDoubleDoubleMap map = tValues.get(n);
- if(map == null) {
- throw new IllegalArgumentException("t-values for n=" + n + " not yet tabularized!");
- }
-
- Double value = map.get(alpha);
- if(value == null) {
- throw new IllegalArgumentException("t-values for alpha=" + alpha + " not tabularized!");
- }
-
- return value;
- }
-
- /**
- * Stores the specified t-values for the given degree of freedom.
- *
- * @param n the degree of freedom
- * @param values the t-values
- */
- private static void put(int n, double[] values) {
- TDoubleDoubleMap map = new TDoubleDoubleHashMap();
- map.put(_6000, values[0]);
- map.put(_8000, values[1]);
- map.put(_9000, values[2]);
- map.put(_9500, values[3]);
- map.put(_9750, values[4]);
- map.put(_9900, values[5]);
- map.put(_9950, values[6]);
- map.put(_9990, values[7]);
- map.put(_9995, values[8]);
-
- map.put(_4000, -values[0]);
- map.put(_2000, -values[1]);
- map.put(_1000, -values[2]);
- map.put(_0500, -values[3]);
- map.put(_0250, -values[4]);
- map.put(_0100, -values[5]);
- map.put(_0050, -values[6]);
- map.put(_0010, -values[7]);
- map.put(_0005, -values[8]);
- tValues.put(n, map);
- }
-} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/math/statistics/TriangularKernelDensityFunction.java b/src/de/lmu/ifi/dbs/elki/math/statistics/TriangularKernelDensityFunction.java
index e3c23b2a..aee544de 100644
--- a/src/de/lmu/ifi/dbs/elki/math/statistics/TriangularKernelDensityFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/math/statistics/TriangularKernelDensityFunction.java
@@ -23,6 +23,7 @@ package de.lmu.ifi.dbs.elki.math.statistics;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
/**
* Triangular kernel density estimator.
@@ -49,4 +50,18 @@ public final class TriangularKernelDensityFunction implements KernelDensityFunct
* Static instance.
*/
public static final TriangularKernelDensityFunction KERNEL = new TriangularKernelDensityFunction();
+
+ /**
+ * Parameterization stub.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public static class Parameterizer extends AbstractParameterizer {
+ @Override
+ protected TriangularKernelDensityFunction makeInstance() {
+ return KERNEL;
+ }
+ }
}
diff --git a/src/de/lmu/ifi/dbs/elki/math/statistics/UniformKernelDensityFunction.java b/src/de/lmu/ifi/dbs/elki/math/statistics/UniformKernelDensityFunction.java
index 8d85528f..66fe7888 100644
--- a/src/de/lmu/ifi/dbs/elki/math/statistics/UniformKernelDensityFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/math/statistics/UniformKernelDensityFunction.java
@@ -23,6 +23,7 @@ package de.lmu.ifi.dbs.elki.math.statistics;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
/**
* Uniform / Rectangular kernel density estimator.
@@ -49,4 +50,18 @@ public final class UniformKernelDensityFunction implements KernelDensityFunction
* Static instance.
*/
public static final UniformKernelDensityFunction KERNEL = new UniformKernelDensityFunction();
+
+ /**
+ * Parameterization stub.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public static class Parameterizer extends AbstractParameterizer {
+ @Override
+ protected UniformKernelDensityFunction makeInstance() {
+ return KERNEL;
+ }
+ }
}
diff --git a/src/de/lmu/ifi/dbs/elki/math/statistics/distribution/ChiDistribution.java b/src/de/lmu/ifi/dbs/elki/math/statistics/distribution/ChiDistribution.java
index 84c86e98..5dc5b399 100644
--- a/src/de/lmu/ifi/dbs/elki/math/statistics/distribution/ChiDistribution.java
+++ b/src/de/lmu/ifi/dbs/elki/math/statistics/distribution/ChiDistribution.java
@@ -91,7 +91,7 @@ public class ChiDistribution implements DistributionWithRandom {
* @return CDF value
*/
public static double cdf(double val, double dof) {
- return GammaDistribution.regularizedGammaP(dof / 2, val * val / 2);
+ return GammaDistribution.regularizedGammaP(dof * .5, val * val * .5);
}
// FIXME: implement!
diff --git a/src/de/lmu/ifi/dbs/elki/math/statistics/distribution/ChiSquaredDistribution.java b/src/de/lmu/ifi/dbs/elki/math/statistics/distribution/ChiSquaredDistribution.java
index 8555afd3..efa24079 100644
--- a/src/de/lmu/ifi/dbs/elki/math/statistics/distribution/ChiSquaredDistribution.java
+++ b/src/de/lmu/ifi/dbs/elki/math/statistics/distribution/ChiSquaredDistribution.java
@@ -65,8 +65,8 @@ public class ChiSquaredDistribution extends GammaDistribution {
if(x == 0) {
return 0.0;
}
- final double k = dof / 2;
- if(k == 1.0) {
+ final double k = dof * .5;
+ if(Math.abs(k - 1.0) < Double.MIN_NORMAL) {
return Math.exp(-x * 2.0) * 2.0;
}
return Math.exp((k - 1.0) * Math.log(x * 2.0) - x * 2.0 - logGamma(k)) * 2.0;
diff --git a/src/de/lmu/ifi/dbs/elki/math/statistics/distribution/Distribution.java b/src/de/lmu/ifi/dbs/elki/math/statistics/distribution/Distribution.java
index 5b6cd286..ad4ef944 100644
--- a/src/de/lmu/ifi/dbs/elki/math/statistics/distribution/Distribution.java
+++ b/src/de/lmu/ifi/dbs/elki/math/statistics/distribution/Distribution.java
@@ -37,7 +37,7 @@ public interface Distribution {
* @param val existing value
* @return distribution density
*/
- public double pdf(double val);
+ double pdf(double val);
/**
* Return the cumulative density function at the given value.
@@ -45,7 +45,7 @@ public interface Distribution {
* @param val existing value
* @return cumulative density
*/
- public double cdf(double val);
+ double cdf(double val);
/**
* Quantile aka probit (for normal) aka inverse CDF (invcdf, cdf^-1) function.
@@ -53,7 +53,7 @@ public interface Distribution {
* @param val Quantile to find
* @return Quantile position
*/
- public double quantile(double val);
+ double quantile(double val);
/**
* Describe the distribution
@@ -61,5 +61,5 @@ public interface Distribution {
* @return description
*/
@Override
- public String toString();
-}
+ String toString();
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/math/statistics/distribution/DistributionWithRandom.java b/src/de/lmu/ifi/dbs/elki/math/statistics/distribution/DistributionWithRandom.java
index af272528..02f5002f 100644
--- a/src/de/lmu/ifi/dbs/elki/math/statistics/distribution/DistributionWithRandom.java
+++ b/src/de/lmu/ifi/dbs/elki/math/statistics/distribution/DistributionWithRandom.java
@@ -33,5 +33,5 @@ public interface DistributionWithRandom extends Distribution {
*
* @return new random value
*/
- public double nextRandom();
-}
+ double nextRandom();
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/math/statistics/distribution/ExponentialDistribution.java b/src/de/lmu/ifi/dbs/elki/math/statistics/distribution/ExponentialDistribution.java
new file mode 100644
index 00000000..866f40d6
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/math/statistics/distribution/ExponentialDistribution.java
@@ -0,0 +1,126 @@
+package de.lmu.ifi.dbs.elki.math.statistics.distribution;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.Random;
+
+/**
+ * Exponential distribution.
+ *
+ * @author Erich Schubert
+ */
+public class ExponentialDistribution implements DistributionWithRandom {
+ /**
+ * Random generator.
+ */
+ Random rnd;
+
+ /**
+ * Rate, inverse of mean
+ */
+ double rate;
+
+ /**
+ * Constructor.
+ *
+ * @param rate Rate parameter (1/scale)
+ */
+ public ExponentialDistribution(double rate) {
+ this(rate, new Random());
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param rate Rate parameter (1/scale)
+ * @param random Random generator
+ */
+ public ExponentialDistribution(double rate, Random random) {
+ super();
+ this.rate = rate;
+ this.rnd = random;
+ }
+
+ @Override
+ public double pdf(double val) {
+ return rate * Math.exp(-rate * val);
+ }
+
+ /**
+ * PDF, static version
+ *
+ * @param val Value to compute PDF at
+ * @param rate Rate parameter (1/scale)
+ * @return probability density
+ */
+ public static double pdf(double val, double rate) {
+ return rate * Math.exp(-rate * val);
+ }
+
+ @Override
+ public double cdf(double val) {
+ return 1 - Math.exp(-rate * val);
+ }
+
+ /**
+ * Cumulative density, static version
+ *
+ * @param val Value to compute CDF at
+ * @param rate Rate parameter (1/scale)
+ * @return cumulative density
+ */
+ public static double cdf(double val, double rate) {
+ return 1 - Math.exp(-rate * val);
+ }
+
+ @Override
+ public double quantile(double val) {
+ return -Math.log(1 - val) / rate;
+ }
+
+ /**
+ * Quantile function, static version
+ *
+ * @param val Value to compute quantile for
+ * @param rate Rate parameter
+ * @return Quantile
+ */
+ public static double quantile(double val, double rate) {
+ return -Math.log(1 - val) / rate;
+ }
+
+ /**
+ * This method currently uses the naive approach of returning
+ * <code>-log(uniform)</code>.
+ *
+ * TODO: there are variants that do not rely on the log method and are faster.
+ * We need to implement and evaluate these. For details: see
+ * "Computer methods for sampling from the exponential and normal distributions"
+ * J. H. Ahrens, U. Dieter, https://dl.acm.org/citation.cfm?id=361593
+ */
+ @Override
+ public double nextRandom() {
+ return -Math.log(rnd.nextDouble()) / rate;
+ }
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/math/statistics/distribution/HaltonUniformDistribution.java b/src/de/lmu/ifi/dbs/elki/math/statistics/distribution/HaltonUniformDistribution.java
new file mode 100644
index 00000000..df8fecda
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/math/statistics/distribution/HaltonUniformDistribution.java
@@ -0,0 +1,313 @@
+package de.lmu.ifi.dbs.elki.math.statistics.distribution;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.Random;
+
+import de.lmu.ifi.dbs.elki.math.MathUtil;
+import de.lmu.ifi.dbs.elki.math.Primes;
+import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
+
+/**
+ * Halton sequences are a pseudo-uniform distribution. The data is actually too
+ * regular for a true uniform distribution, but as such will of course often
+ * appear to be uniform.
+ *
+ * Technically, they are based on Van der Corput sequence and the Von Neumann
+ * Katutani transformation. These produce a series of integers which then are
+ * converted to floating point values.
+ *
+ * To randomize, we just choose a random starting position, as indicated by
+ *
+ * Reference:
+ * <p>
+ * Randomized halton sequences<br>
+ * Wang, X. and Hickernell, F.J.<br />
+ * Mathematical and Computer Modelling Vol. 32 (7)
+ * </p>
+ *
+ * <b>Important note: this code hasn't been double checked yet. While it
+ * probably works for some simple cases such as example data set generation, do
+ * <em>not</em> rely on it for e.g. quasi monte carlo methods without
+ * double-checking the quality, and looking at more advanced methods!</b>
+ *
+ * Let me repeat this: this code was written <b>to generate toy datasets</b>. It
+ * <b>may have deficits</b> for other uses! <b>There is a high chance it will
+ * produce correlated data when used for more than one dimension.</b> - for toy
+ * data sets, try different random seeds until you find one that works for you.
+ *
+ * TODO: find an improved algorithm that takes care of a better randomization,
+ * for example by adding scrambling.
+ *
+ * @author Erich Schubert
+ */
+@Reference(title = "Randomized halton sequences", authors = "Wang, X. and Hickernell, F.J.", booktitle = "Mathematical and Computer Modelling Vol. 32 (7)", url = "http://dx.doi.org/10.1016/S0895-7177(00)00178-3")
+public class HaltonUniformDistribution implements DistributionWithRandom {
+ /**
+ * Minimum
+ */
+ private double min;
+
+ /**
+ * Maximum
+ */
+ private double max;
+
+ /**
+ * Len := max - min
+ */
+ private double len;
+
+ /**
+ * Maximum number of iterations of fast variant
+ */
+ private static final int MAXFAST = 1000;
+
+ /**
+ * Threshold
+ */
+ private static final double ALMOST_ONE = 1.0 - 1e-10;
+
+ /**
+ * Base value
+ */
+ final short base;
+
+ /**
+ * Inverse of base, for faster division by multiplication.
+ */
+ final double invbase;
+
+ /**
+ * Logarithm of base.
+ */
+ final double logbase;
+
+ /**
+ * Maximum integer to use
+ */
+ final int maxi;
+
+ /**
+ * Counter, for max iterations of fast function.
+ */
+ int counter = 0;
+
+ /**
+ * Current value
+ */
+ double current;
+
+ /**
+ * Integer inverse
+ */
+ long inverse;
+
+ /**
+ * Constructor for a halton pseudo uniform distribution on the interval [min,
+ * max[
+ *
+ * @param min Minimum value
+ * @param max Maximum value
+ * @param base Base value
+ * @param seed Random seed (starting value)
+ */
+ public HaltonUniformDistribution(double min, double max, int base, double seed) {
+ super();
+ // Swap parameters if they were given incorrectly.
+ if (min > max) {
+ double tmp = min;
+ min = max;
+ max = tmp;
+ }
+ this.min = min;
+ this.max = max;
+ this.len = max - min;
+
+ this.base = (short) base;
+ this.invbase = 1.0 / base;
+ this.logbase = Math.log(base);
+ // 32 bit * log(2) / log(base)
+ this.maxi = (int) (32.0 * MathUtil.LOG2 / logbase);
+ this.current = seed;
+ this.inverse = inverse(seed);
+ }
+
+ /**
+ * Constructor for a halton pseudo uniform distribution on the interval [min,
+ * max[
+ *
+ * @param min Minimum value
+ * @param max Maximum value
+ */
+ public HaltonUniformDistribution(double min, double max) {
+ // TODO: use different starting primes?
+ this(min, max, new Random());
+ }
+
+ /**
+ * Constructor for a halton pseudo uniform distribution on the interval [min,
+ * max[
+ *
+ * @param min Minimum value
+ * @param max Maximum value
+ * @param rnd Random generator
+ */
+ public HaltonUniformDistribution(double min, double max, Random rnd) {
+ // TODO: use different starting primes?
+ this(min, max, choosePrime(rnd), rnd.nextDouble());
+ }
+
+ /**
+ * Choose a random prime. We try to avoid the later primes, as they are known
+ * to cause too correlated data.
+ *
+ * @param rnd Random generator
+ * @return Prime
+ */
+ private static int choosePrime(Random rnd) {
+ return Primes.FIRST_PRIMES[rnd.nextInt(10)];
+ }
+
+ @Override
+ public double pdf(double val) {
+ if (val < min || val >= max) {
+ return 0.0;
+ }
+ return 1.0 / len;
+ }
+
+ @Override
+ public double cdf(double val) {
+ if (val < min) {
+ return 0.0;
+ }
+ if (val > max) {
+ return 1.0;
+ }
+ return (val - min) / len;
+ }
+
+ @Override
+ public double quantile(double val) {
+ return min + len * val;
+ }
+
+ /**
+ * Compute the inverse with respect to the given base.
+ *
+ * @param current Current value
+ * @return Integer inverse
+ */
+ private long inverse(double current) {
+ // Represent to base b.
+ short[] digits = new short[maxi];
+ int j;
+ for (j = 0; j < maxi; j++) {
+ current *= base;
+ digits[j] = (short) current;
+ current -= digits[j];
+ if (current <= 1e-10) {
+ break;
+ }
+ }
+ long inv = 0;
+ for (j = maxi - 1; j >= 0; j--) {
+ inv = inv * base + digits[j];
+ }
+ return inv;
+ }
+
+ /**
+ * Compute the radical inverse of i.
+ *
+ * @param i Input long value
+ * @return Double radical inverse
+ */
+ private double radicalInverse(long i) {
+ double digit = 1.0 / (double) base;
+ double radical = digit;
+ double inverse = 0.0;
+ while (i > 0) {
+ inverse += digit * (double) (i % base);
+ digit *= radical;
+ i /= base;
+ }
+ return inverse;
+ }
+
+ /**
+ * Compute the next radical inverse.
+ *
+ * @return Next inverse
+ */
+ private double nextRadicalInverse() {
+ counter++;
+ // Do at most MAXFAST appromate steps
+ if (counter >= MAXFAST) {
+ counter = 0;
+ inverse += MAXFAST;
+ current = radicalInverse(inverse);
+ return current;
+ }
+ // Fast approximation:
+ double nextInverse = current + invbase;
+ if (nextInverse < ALMOST_ONE) {
+ current = nextInverse;
+ return current;
+ } else {
+ double digit1 = invbase, digit2 = invbase * invbase;
+ while (current + digit2 >= ALMOST_ONE) {
+ digit1 = digit2;
+ digit2 *= invbase;
+ }
+ current += (digit1 - 1.0) + digit2;
+ return current;
+ }
+ }
+
+ @Override
+ public double nextRandom() {
+ return min + nextRadicalInverse() * len;
+ }
+
+ @Override
+ public String toString() {
+ return "HaltonUniformDistribution(min=" + min + ", max=" + max + ")";
+ }
+
+ /**
+ * @return the minimum value
+ */
+ public double getMin() {
+ return min;
+ }
+
+ /**
+ * @return the maximum value
+ */
+ public double getMax() {
+ return max;
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/math/statistics/distribution/NormalDistribution.java b/src/de/lmu/ifi/dbs/elki/math/statistics/distribution/NormalDistribution.java
index 9180b59e..1845dec1 100644
--- a/src/de/lmu/ifi/dbs/elki/math/statistics/distribution/NormalDistribution.java
+++ b/src/de/lmu/ifi/dbs/elki/math/statistics/distribution/NormalDistribution.java
@@ -315,7 +315,7 @@ public class NormalDistribution implements DistributionWithRandom {
public static double pdf(double x, double mu, double sigma) {
final double x_mu = x - mu;
final double sigmasq = sigma * sigma;
- return 1 / (Math.sqrt(MathUtil.TWOPI * sigmasq)) * Math.exp(-1 * x_mu * x_mu / 2 / sigmasq);
+ return 1 / (Math.sqrt(MathUtil.TWOPI * sigmasq)) * Math.exp(-.5 * x_mu * x_mu / sigmasq);
}
/**
diff --git a/src/de/lmu/ifi/dbs/elki/math/statistics/distribution/PoissonDistribution.java b/src/de/lmu/ifi/dbs/elki/math/statistics/distribution/PoissonDistribution.java
index 53fb0dc8..a4ea9402 100644
--- a/src/de/lmu/ifi/dbs/elki/math/statistics/distribution/PoissonDistribution.java
+++ b/src/de/lmu/ifi/dbs/elki/math/statistics/distribution/PoissonDistribution.java
@@ -53,26 +53,26 @@ public class PoissonDistribution implements Distribution {
private double p;
/** Stirling error constants: 1./12 */
- private final static double S0 = 0.08333333333333333333333d;
+ private static final double S0 = 0.08333333333333333333333d;
/** Stirling error constants: 1./360 */
- private final static double S1 = 0.0027777777777777777777777777778d;
+ private static final double S1 = 0.0027777777777777777777777777778d;
/** Stirling error constants: 1./1260 */
- private final static double S2 = 0.00079365079365079365079365d;
+ private static final double S2 = 0.00079365079365079365079365d;
/** Stirling error constants: 1./1680 */
- private final static double S3 = 0.000595238095238095238095238095d;
+ private static final double S3 = 0.000595238095238095238095238095d;
/** Stirling error constants: 1./1188 */
- private final static double S4 = 0.00084175084175084175084175084175d;
+ private static final double S4 = 0.00084175084175084175084175084175d;
/**
* Exact table values for n <= 15 in steps of 0.5
*
* sfe[n] = ln( (n!*e^n)/((n^n)*sqrt(2*pi*n)) )
*/
- private final static double STIRLING_EXACT_ERROR[] = {//
+ private static final double STIRLING_EXACT_ERROR[] = {//
0.0, // 0.0
0.1534264097200273452913848, // 0.5
0.0810614667953272582196702, // 1.0
@@ -273,7 +273,7 @@ public class PoissonDistribution implements Distribution {
private static double stirlingError(int n) {
// Try to use a table value:
if(n < 16) {
- return STIRLING_EXACT_ERROR[n * 2];
+ return STIRLING_EXACT_ERROR[n << 1];
}
final double nn = n * n;
// Use the appropriate number of terms
diff --git a/src/de/lmu/ifi/dbs/elki/math/statistics/distribution/StudentsTDistribution.java b/src/de/lmu/ifi/dbs/elki/math/statistics/distribution/StudentsTDistribution.java
index 2e9e0d15..fcb96c12 100644
--- a/src/de/lmu/ifi/dbs/elki/math/statistics/distribution/StudentsTDistribution.java
+++ b/src/de/lmu/ifi/dbs/elki/math/statistics/distribution/StudentsTDistribution.java
@@ -73,7 +73,7 @@ public class StudentsTDistribution implements Distribution {
*/
public static double pdf(double val, int v) {
// TODO: improve precision by computing "exp" last?
- return Math.exp(GammaDistribution.logGamma((v + 1) / 2) - GammaDistribution.logGamma(v / 2)) * (1 / Math.sqrt(v * Math.PI)) * Math.pow(1 + (val * val) / v, -((v + 1) / 2));
+ return Math.exp(GammaDistribution.logGamma((v + 1) * .5) - GammaDistribution.logGamma(v * .5)) * (1 / Math.sqrt(v * Math.PI)) * Math.pow(1 + (val * val) / v, -((v + 1) * .5));
}
/**
@@ -85,6 +85,6 @@ public class StudentsTDistribution implements Distribution {
*/
public static double cdf(double val, int v) {
double x = v / (val * val + v);
- return 1 - (0.5 * BetaDistribution.regularizedIncBeta(x, v / 2, 0.5));
+ return 1 - (0.5 * BetaDistribution.regularizedIncBeta(x, v * .5, 0.5));
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/math/statistics/tests/GoodnessOfFitTest.java b/src/de/lmu/ifi/dbs/elki/math/statistics/tests/GoodnessOfFitTest.java
index f50f469f..363dbfea 100644
--- a/src/de/lmu/ifi/dbs/elki/math/statistics/tests/GoodnessOfFitTest.java
+++ b/src/de/lmu/ifi/dbs/elki/math/statistics/tests/GoodnessOfFitTest.java
@@ -38,12 +38,12 @@ public interface GoodnessOfFitTest extends Parameterizable {
/**
* Measure the deviation of a full sample from a conditional sample.
*
- * Sample arrays *may* be modified, e.g. sorted, by the test.
+ * Sample arrays <em>may</em> be modified, e.g. sorted, by the test.
*
* @param fullSample Full sample
* @param conditionalSample Conditional sample
*
* @return Deviation
*/
- public double deviation(double[] fullSample, double[] conditionalSample);
-}
+ double deviation(double[] fullSample, double[] conditionalSample);
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/persistent/ByteArrayUtil.java b/src/de/lmu/ifi/dbs/elki/persistent/ByteArrayUtil.java
index 42466558..ba9d108e 100644
--- a/src/de/lmu/ifi/dbs/elki/persistent/ByteArrayUtil.java
+++ b/src/de/lmu/ifi/dbs/elki/persistent/ByteArrayUtil.java
@@ -62,34 +62,41 @@ import de.lmu.ifi.dbs.elki.utilities.exceptions.AbortException;
*/
public final class ByteArrayUtil {
/**
+ * Fake constructor.
+ */
+ private ByteArrayUtil() {
+ // Do not instantiate
+ }
+
+ /**
* Size of a byte in bytes.
*/
- public final static int SIZE_BYTE = 1;
+ public static final int SIZE_BYTE = 1;
/**
* Size of a short in bytes.
*/
- public final static int SIZE_SHORT = 2;
+ public static final int SIZE_SHORT = 2;
/**
* Size of an integer in bytes.
*/
- public final static int SIZE_INT = 4;
+ public static final int SIZE_INT = 4;
/**
* Size of a long in bytes.
*/
- public final static int SIZE_LONG = 8;
+ public static final int SIZE_LONG = 8;
/**
* Size of a float in bytes.
*/
- public final static int SIZE_FLOAT = 4;
+ public static final int SIZE_FLOAT = 4;
/**
* Size of a double in bytes.
*/
- public final static int SIZE_DOUBLE = 8;
+ public static final int SIZE_DOUBLE = 8;
/**
* Write a short to the byte array at the given offset.
@@ -99,7 +106,7 @@ public final class ByteArrayUtil {
* @param v data
* @return number of bytes written
*/
- public final static int writeShort(byte[] array, int offset, int v) {
+ public static int writeShort(byte[] array, int offset, int v) {
array[offset + 0] = (byte) (v >>> 8);
array[offset + 1] = (byte) (v >>> 0);
return SIZE_SHORT;
@@ -113,7 +120,7 @@ public final class ByteArrayUtil {
* @param v data
* @return number of bytes written
*/
- public final static int writeInt(byte[] array, int offset, int v) {
+ public static int writeInt(byte[] array, int offset, int v) {
array[offset + 0] = (byte) (v >>> 24);
array[offset + 1] = (byte) (v >>> 16);
array[offset + 2] = (byte) (v >>> 8);
@@ -129,7 +136,7 @@ public final class ByteArrayUtil {
* @param v data
* @return number of bytes written
*/
- public final static int writeLong(byte[] array, int offset, long v) {
+ public static int writeLong(byte[] array, int offset, long v) {
array[offset + 0] = (byte) (v >>> 56);
array[offset + 1] = (byte) (v >>> 48);
array[offset + 2] = (byte) (v >>> 40);
@@ -149,7 +156,7 @@ public final class ByteArrayUtil {
* @param v data
* @return number of bytes written
*/
- public final static int writeFloat(byte[] array, int offset, float v) {
+ public static int writeFloat(byte[] array, int offset, float v) {
return writeInt(array, offset, Float.floatToIntBits(v));
}
@@ -161,7 +168,7 @@ public final class ByteArrayUtil {
* @param v data
* @return number of bytes written
*/
- public final static int writeDouble(byte[] array, int offset, double v) {
+ public static int writeDouble(byte[] array, int offset, double v) {
return writeLong(array, offset, Double.doubleToLongBits(v));
}
@@ -172,7 +179,7 @@ public final class ByteArrayUtil {
* @param offset Offset to read at
* @return (signed) short
*/
- public final static short readShort(byte[] array, int offset) {
+ public static short readShort(byte[] array, int offset) {
// First make integers to resolve signed vs. unsigned issues.
int b0 = array[offset + 0] & 0xFF;
int b1 = array[offset + 1] & 0xFF;
@@ -186,7 +193,7 @@ public final class ByteArrayUtil {
* @param offset Offset to read at
* @return short
*/
- public final static int readUnsignedShort(byte[] array, int offset) {
+ public static int readUnsignedShort(byte[] array, int offset) {
// First make integers to resolve signed vs. unsigned issues.
int b0 = array[offset + 0] & 0xFF;
int b1 = array[offset + 1] & 0xFF;
@@ -200,7 +207,7 @@ public final class ByteArrayUtil {
* @param offset Offset to read at
* @return data
*/
- public final static int readInt(byte[] array, int offset) {
+ public static int readInt(byte[] array, int offset) {
// First make integers to resolve signed vs. unsigned issues.
int b0 = array[offset + 0] & 0xFF;
int b1 = array[offset + 1] & 0xFF;
@@ -216,7 +223,7 @@ public final class ByteArrayUtil {
* @param offset Offset to read at
* @return data
*/
- public final static long readLong(byte[] array, int offset) {
+ public static long readLong(byte[] array, int offset) {
// First make integers to resolve signed vs. unsigned issues.
long b0 = array[offset + 0];
long b1 = array[offset + 1] & 0xFF;
@@ -236,7 +243,7 @@ public final class ByteArrayUtil {
* @param offset Offset to read at
* @return data
*/
- public final static float readFloat(byte[] array, int offset) {
+ public static float readFloat(byte[] array, int offset) {
return Float.intBitsToFloat(readInt(array, offset));
}
@@ -247,33 +254,36 @@ public final class ByteArrayUtil {
* @param offset Offset to read at
* @return data
*/
- public final static double readDouble(byte[] array, int offset) {
+ public static double readDouble(byte[] array, int offset) {
return Double.longBitsToDouble(readLong(array, offset));
}
/**
- * Serializer for byte objects
+ * Serializer for byte objects.
*
* @author Erich Schubert
*/
- public static class ByteSerializer implements FixedSizeByteBufferSerializer<Byte> {
+ public static final class ByteSerializer implements FixedSizeByteBufferSerializer<Byte> {
/**
* Constructor. Protected: use static instance!
*/
- protected ByteSerializer() {
+ private ByteSerializer() {
super();
}
+ @Deprecated
@Override
public Byte fromByteBuffer(ByteBuffer buffer) {
return buffer.get();
}
+ @Deprecated
@Override
public void toByteBuffer(ByteBuffer buffer, Byte obj) {
buffer.put(obj);
}
+ @Deprecated
@Override
public int getByteSize(Byte object) {
return getFixedByteSize();
@@ -286,28 +296,31 @@ public final class ByteArrayUtil {
}
/**
- * Serializer for short objects
+ * Serializer for short objects.
*
* @author Erich Schubert
*/
- public static class ShortSerializer implements FixedSizeByteBufferSerializer<Short> {
+ public static final class ShortSerializer implements FixedSizeByteBufferSerializer<Short> {
/**
* Constructor. Protected: use static instance!
*/
- protected ShortSerializer() {
+ private ShortSerializer() {
super();
}
+ @Deprecated
@Override
public Short fromByteBuffer(ByteBuffer buffer) {
return buffer.getShort();
}
+ @Deprecated
@Override
public void toByteBuffer(ByteBuffer buffer, Short obj) {
buffer.putShort(obj);
}
+ @Deprecated
@Override
public int getByteSize(Short object) {
return getFixedByteSize();
@@ -320,28 +333,31 @@ public final class ByteArrayUtil {
}
/**
- * Serializer for integer objects
+ * Serializer for integer objects.
*
* @author Erich Schubert
*/
- public static class IntegerSerializer implements FixedSizeByteBufferSerializer<Integer> {
+ public static final class IntegerSerializer implements FixedSizeByteBufferSerializer<Integer> {
/**
* Constructor. Protected: use static instance!
*/
- protected IntegerSerializer() {
+ private IntegerSerializer() {
super();
}
+ @Deprecated
@Override
public Integer fromByteBuffer(ByteBuffer buffer) {
return buffer.getInt();
}
+ @Deprecated
@Override
public void toByteBuffer(ByteBuffer buffer, Integer obj) {
buffer.putInt(obj);
}
+ @Deprecated
@Override
public int getByteSize(Integer object) {
return getFixedByteSize();
@@ -354,28 +370,31 @@ public final class ByteArrayUtil {
}
/**
- * Serializer for long objects
+ * Serializer for long objects.
*
* @author Erich Schubert
*/
- public static class LongSerializer implements FixedSizeByteBufferSerializer<Long> {
+ public static final class LongSerializer implements FixedSizeByteBufferSerializer<Long> {
/**
* Constructor. Protected: use static instance!
*/
- protected LongSerializer() {
+ private LongSerializer() {
super();
}
+ @Deprecated
@Override
public Long fromByteBuffer(ByteBuffer buffer) {
return buffer.getLong();
}
+ @Deprecated
@Override
public void toByteBuffer(ByteBuffer buffer, Long obj) {
buffer.putLong(obj);
}
+ @Deprecated
@Override
public int getByteSize(Long object) {
return getFixedByteSize();
@@ -388,28 +407,31 @@ public final class ByteArrayUtil {
}
/**
- * Serializer for float objects
+ * Serializer for float objects.
*
* @author Erich Schubert
*/
- public static class FloatSerializer implements FixedSizeByteBufferSerializer<Float> {
+ public static final class FloatSerializer implements FixedSizeByteBufferSerializer<Float> {
/**
* Constructor. Protected: use static instance!
*/
- protected FloatSerializer() {
+ private FloatSerializer() {
super();
}
+ @Deprecated
@Override
public Float fromByteBuffer(ByteBuffer buffer) {
return buffer.getFloat();
}
+ @Deprecated
@Override
public void toByteBuffer(ByteBuffer buffer, Float obj) {
buffer.putFloat(obj);
}
+ @Deprecated
@Override
public int getByteSize(Float object) {
return getFixedByteSize();
@@ -422,28 +444,31 @@ public final class ByteArrayUtil {
}
/**
- * Serializer for double objects
+ * Serializer for double objects.
*
* @author Erich Schubert
*/
- public static class DoubleSerializer implements FixedSizeByteBufferSerializer<Double> {
+ public static final class DoubleSerializer implements FixedSizeByteBufferSerializer<Double> {
/**
* Constructor. Protected: use static instance!
*/
- protected DoubleSerializer() {
+ private DoubleSerializer() {
super();
}
+ @Deprecated
@Override
public Double fromByteBuffer(ByteBuffer buffer) {
return buffer.getDouble();
}
+ @Deprecated
@Override
public void toByteBuffer(ByteBuffer buffer, Double obj) {
buffer.putDouble(obj);
}
+ @Deprecated
@Override
public int getByteSize(Double object) {
return getFixedByteSize();
@@ -456,44 +481,43 @@ public final class ByteArrayUtil {
}
/**
- * Serializer for String objects
+ * Serializer for String objects.
*
* @author Erich Schubert
*/
- public static class StringSerializer implements ByteBufferSerializer<String> {
+ public static final class StringSerializer implements ByteBufferSerializer<String> {
/**
- * Character set to use
+ * Character set to use.
*/
Charset charset = Charset.forName("UTF-8");
/**
- * Encoder
+ * Encoder.
*/
CharsetEncoder encoder = charset.newEncoder();
/**
- * Decoder
+ * Decoder.
*/
CharsetDecoder decoder = charset.newDecoder();
/**
* Constructor. Protected: use static instance!
*/
- protected StringSerializer() {
+ private StringSerializer() {
super();
}
@Override
public String fromByteBuffer(ByteBuffer buffer) {
- int len = buffer.getInt();
+ int len = readUnsignedVarint(buffer);
// Create and limit a view
ByteBuffer subbuffer = buffer.slice();
subbuffer.limit(len);
CharBuffer res;
try {
res = decoder.decode(subbuffer);
- }
- catch(CharacterCodingException e) {
+ } catch (CharacterCodingException e) {
throw new AbortException("String not representable as UTF-8.", e);
}
// TODO: assert that the decoding did not yet advance the buffer!
@@ -506,48 +530,50 @@ public final class ByteArrayUtil {
ByteBuffer data;
try {
data = encoder.encode(CharBuffer.wrap(obj));
- }
- catch(CharacterCodingException e) {
+ } catch (CharacterCodingException e) {
throw new AbortException("String not representable as UTF-8.", e);
}
- buffer.putInt(data.remaining());
+ writeUnsignedVarint(buffer, data.remaining());
buffer.put(data);
}
@Override
public int getByteSize(String object) {
try {
- return SIZE_INT + encoder.encode(CharBuffer.wrap(object)).remaining();
- }
- catch(CharacterCodingException e) {
+ final int len = encoder.encode(CharBuffer.wrap(object)).remaining();
+ return getUnsignedVarintSize(len) + len;
+ } catch (CharacterCodingException e) {
throw new AbortException("String not representable as UTF-8.", e);
}
}
}
/**
- * Serializer for Integer objects using a variable size encoding
+ * Serializer for Integer objects using a variable size encoding.
*
* @author Erich Schubert
*/
- public static class VarintSerializer implements ByteBufferSerializer<Integer> {
+ public static final class VarintSerializer implements ByteBufferSerializer<Integer> {
/**
* Constructor. Protected: use static instance!
*/
- protected VarintSerializer() {
+ private VarintSerializer() {
super();
}
+ @Deprecated
@Override
public Integer fromByteBuffer(ByteBuffer buffer) {
return readSignedVarint(buffer);
}
+ @Deprecated
@Override
public void toByteBuffer(ByteBuffer buffer, Integer obj) {
writeSignedVarint(buffer, obj);
}
+ @Deprecated
@Override
public int getByteSize(Integer object) {
return getSignedVarintSize(object);
@@ -605,7 +631,7 @@ public final class ByteArrayUtil {
* @param buffer Buffer to write to
* @param val number to write
*/
- public static final void writeSignedVarint(ByteBuffer buffer, int val) {
+ public static void writeSignedVarint(ByteBuffer buffer, int val) {
// Move sign to lowest bit
writeUnsignedVarint(buffer, (val << 1) ^ (val >> 31));
}
@@ -621,7 +647,7 @@ public final class ByteArrayUtil {
* @param buffer Buffer to write to
* @param val number to write
*/
- public static final void writeSignedVarintLong(ByteBuffer buffer, long val) {
+ public static void writeSignedVarintLong(ByteBuffer buffer, long val) {
// Move sign to lowest bit
writeUnsignedVarintLong(buffer, (val << 1) ^ (val >> 63));
}
@@ -635,9 +661,9 @@ public final class ByteArrayUtil {
* @param buffer Buffer to write to
* @param val number to write
*/
- public static final void writeUnsignedVarint(ByteBuffer buffer, int val) {
+ public static void writeUnsignedVarint(ByteBuffer buffer, int val) {
// Extra bytes have the high bit set
- while((val & 0x7F) != val) {
+ while ((val & 0x7F) != val) {
buffer.put((byte) ((val & 0x7F) | 0x80));
val >>>= 7;
}
@@ -657,9 +683,9 @@ public final class ByteArrayUtil {
* @param buffer Buffer to write to
* @param val number to write
*/
- public static final void writeUnsignedVarintLong(ByteBuffer buffer, long val) {
+ public static void writeUnsignedVarintLong(ByteBuffer buffer, long val) {
// Extra bytes have the high bit set
- while((val & 0x7F) != val) {
+ while ((val & 0x7F) != val) {
buffer.put((byte) ((val & 0x7F) | 0x80));
val >>>= 7;
}
@@ -668,26 +694,41 @@ public final class ByteArrayUtil {
}
/**
- * Compute the size of the varint encoding for this signed integer
+ * Write a string to the buffer.
+ *
+ * See {@link StringSerializer} for details.
+ *
+ * @param buffer Buffer to write to
+ * @param s String to write
+ */
+ public static void writeString(ByteBuffer buffer, String s) {
+ if (s == null) {
+ s = ""; // Which will be written as Varint 0 = single byte 0.
+ }
+ ByteArrayUtil.STRING_SERIALIZER.toByteBuffer(buffer, s);
+ }
+
+ /**
+ * Compute the size of the varint encoding for this signed integer.
*
* @param val integer to write
* @return Encoding size of this integer
*/
- public static final int getSignedVarintSize(int val) {
+ public static int getSignedVarintSize(int val) {
// Move sign to lowest bit
return getUnsignedVarintSize((val << 1) ^ (val >> 31));
}
/**
- * Compute the size of the varint encoding for this unsigned integer
+ * Compute the size of the varint encoding for this unsigned integer.
*
* @param obj integer to write
* @return Encoding size of this integer
*/
- public static final int getUnsignedVarintSize(int obj) {
+ public static int getUnsignedVarintSize(int obj) {
int bytes = 1;
// Extra bytes have the high bit set
- while((obj & 0x7F) != obj) {
+ while ((obj & 0x7F) != obj) {
bytes++;
obj >>>= 7;
}
@@ -695,26 +736,26 @@ public final class ByteArrayUtil {
}
/**
- * Compute the size of the varint encoding for this signed integer
+ * Compute the size of the varint encoding for this signed integer.
*
* @param val integer to write
* @return Encoding size of this integer
*/
- public static final int getSignedVarintLongSize(long val) {
+ public static int getSignedVarintLongSize(long val) {
// Move sign to lowest bit
return getUnsignedVarintLongSize((val << 1) ^ (val >> 31));
}
/**
- * Compute the size of the varint encoding for this unsigned integer
+ * Compute the size of the varint encoding for this unsigned integer.
*
* @param obj integer to write
* @return Encoding size of this integer
*/
- public static final int getUnsignedVarintLongSize(long obj) {
+ public static int getUnsignedVarintLongSize(long obj) {
int bytes = 1;
// Extra bytes have the high bit set
- while((obj & 0x7F) != obj) {
+ while ((obj & 0x7F) != obj) {
bytes++;
obj >>>= 7;
}
@@ -722,12 +763,22 @@ public final class ByteArrayUtil {
}
/**
+ * Compute the size of the string after encoding.
+ *
+ * @param s String to encode
+ * @return Byte size
+ */
+ public static int getStringSize(String s) {
+ return STRING_SERIALIZER.getByteSize(s);
+ }
+
+ /**
* Read a signed integer.
*
* @param buffer Buffer to read from
* @return Integer value
*/
- public static final int readSignedVarint(ByteBuffer buffer) {
+ public static int readSignedVarint(ByteBuffer buffer) {
final int raw = readUnsignedVarint(buffer);
return (raw >>> 1) ^ -(raw & 1);
}
@@ -738,17 +789,17 @@ public final class ByteArrayUtil {
* @param buffer Buffer to read from
* @return Integer value
*/
- public static final int readUnsignedVarint(ByteBuffer buffer) {
+ public static int readUnsignedVarint(ByteBuffer buffer) {
int val = 0;
int bits = 0;
- while(true) {
+ while (true) {
final int data = buffer.get();
val |= (data & 0x7F) << bits;
- if((data & 0x80) == 0) {
+ if ((data & 0x80) == 0) {
return val;
}
bits += 7;
- if(bits > 35) {
+ if (bits > 35) {
throw new AbortException("Variable length quantity is too long for expected integer.");
}
}
@@ -760,7 +811,7 @@ public final class ByteArrayUtil {
* @param buffer Buffer to read from
* @return long value
*/
- public static final long readSignedVarintLong(ByteBuffer buffer) {
+ public static long readSignedVarintLong(ByteBuffer buffer) {
final long raw = readUnsignedVarintLong(buffer);
return (raw >>> 1) ^ -(raw & 1);
}
@@ -771,29 +822,42 @@ public final class ByteArrayUtil {
* @param buffer Buffer to read from
* @return long value
*/
- public static final long readUnsignedVarintLong(ByteBuffer buffer) {
+ public static long readUnsignedVarintLong(ByteBuffer buffer) {
long val = 0;
int bits = 0;
- while(true) {
+ while (true) {
final int data = buffer.get();
val |= (data & 0x7F) << bits;
- if((data & 0x80) == 0) {
+ if ((data & 0x80) == 0) {
return val;
}
bits += 7;
- if(bits > 63) {
+ if (bits > 63) {
throw new AbortException("Variable length quantity is too long for expected integer.");
}
}
}
/**
+ * Read a string from the buffer.
+ *
+ * Note: this is not 100% symmetric to writeString, as a {@code null} value
+ * and the empty string are encoded the same way.
+ *
+ * @param buffer Buffer to read from.
+ * @return Deserialized string
+ */
+ public static String readString(ByteBuffer buffer) {
+ return STRING_SERIALIZER.fromByteBuffer(buffer);
+ }
+
+ /**
* Unmap a byte buffer.
*
* @param map Byte buffer to unmap.
*/
public static void unmapByteBuffer(final MappedByteBuffer map) {
- if(map == null) {
+ if (map == null) {
return;
}
map.force();
@@ -804,23 +868,22 @@ public final class ByteArrayUtil {
public Object run() {
try {
Method getCleanerMethod = map.getClass().getMethod("cleaner", new Class[0]);
- if(getCleanerMethod == null) {
+ if (getCleanerMethod == null) {
return null;
}
getCleanerMethod.setAccessible(true);
Object cleaner = getCleanerMethod.invoke(map, new Object[0]);
Method cleanMethod = cleaner.getClass().getMethod("clean");
- if(cleanMethod == null) {
+ if (cleanMethod == null) {
return null;
}
cleanMethod.invoke(cleaner);
- }
- catch(Exception e) {
+ } catch (Exception e) {
LoggingUtil.exception(e);
}
return null;
}
});
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/persistent/ByteBufferSerializer.java b/src/de/lmu/ifi/dbs/elki/persistent/ByteBufferSerializer.java
index e10a365d..93f99ba9 100644
--- a/src/de/lmu/ifi/dbs/elki/persistent/ByteBufferSerializer.java
+++ b/src/de/lmu/ifi/dbs/elki/persistent/ByteBufferSerializer.java
@@ -27,12 +27,12 @@ import java.io.IOException;
import java.nio.ByteBuffer;
/**
- * Class to convert from and to byte arrays (in index structures)
+ * Class to convert from and to byte arrays (in index structures).
*
* @author Erich Schubert
*
* @apiviz.uses ByteBuffer
- *
+ *
* @param <T> Object type processed
*/
public interface ByteBufferSerializer<T> {
@@ -41,22 +41,31 @@ public interface ByteBufferSerializer<T> {
*
* @param buffer Data array to process
* @return Deserialized object
+ * @throws IOException on IO errors
+ * @throws UnsupportedOperationException When functionality not implemented or
+ * available
*/
- public T fromByteBuffer(ByteBuffer buffer) throws IOException, UnsupportedOperationException;
+ T fromByteBuffer(ByteBuffer buffer) throws IOException, UnsupportedOperationException;
/**
* Serialize the object to a byte array (e.g. disk)
*
* @param buffer Buffer to serialize to
* @param object Object to serialize
+ * @throws IOException on IO errors
+ * @throws UnsupportedOperationException When functionality not implemented or
+ * available
*/
- public void toByteBuffer(ByteBuffer buffer, T object) throws IOException, UnsupportedOperationException;
+ void toByteBuffer(ByteBuffer buffer, T object) throws IOException, UnsupportedOperationException;
/**
* Get the size of the object in bytes.
*
* @param object Object to serialize
* @return maximum size in serialized form
+ * @throws IOException on IO errors
+ * @throws UnsupportedOperationException When functionality not implemented or
+ * available
*/
- public int getByteSize(T object) throws IOException, UnsupportedOperationException;
+ int getByteSize(T object) throws IOException, UnsupportedOperationException;
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/persistent/LRUCache.java b/src/de/lmu/ifi/dbs/elki/persistent/LRUCache.java
index 3b6f67dc..7e06e5ea 100644
--- a/src/de/lmu/ifi/dbs/elki/persistent/LRUCache.java
+++ b/src/de/lmu/ifi/dbs/elki/persistent/LRUCache.java
@@ -48,7 +48,7 @@ public class LRUCache<P extends Page> extends AbstractPageFile<P> {
/**
* Our logger
*/
- private static final Logging logger = Logging.getLogger(LRUCache.class);
+ private static final Logging LOG = Logging.getLogger(LRUCache.class);
/**
* Cache size in bytes.
@@ -96,13 +96,13 @@ public class LRUCache<P extends Page> extends AbstractPageFile<P> {
readAccess++;
P page = map.get(pageID);
if(page != null) {
- if(logger.isDebuggingFine()) {
- logger.debugFine("Read from cache: " + pageID);
+ if(LOG.isDebuggingFine()) {
+ LOG.debugFine("Read from cache: " + pageID);
}
}
else {
- if(logger.isDebuggingFine()) {
- logger.debugFine("Read from backing: " + pageID);
+ if(LOG.isDebuggingFine()) {
+ LOG.debugFine("Read from backing: " + pageID);
}
page = file.readPage(pageID);
map.put(pageID, page);
@@ -115,8 +115,8 @@ public class LRUCache<P extends Page> extends AbstractPageFile<P> {
writeAccess++;
page.setDirty(true);
map.put(pageID, page);
- if(logger.isDebuggingFine()) {
- logger.debugFine("Write to cache: " + pageID);
+ if(LOG.isDebuggingFine()) {
+ LOG.debugFine("Write to cache: " + pageID);
}
}
@@ -133,8 +133,8 @@ public class LRUCache<P extends Page> extends AbstractPageFile<P> {
* @param page page
*/
protected void expirePage(P page) {
- if(logger.isDebuggingFine()) {
- logger.debugFine("Write to backing:" + page.getPageID());
+ if(LOG.isDebuggingFine()) {
+ LOG.debugFine("Write to backing:" + page.getPageID());
}
if (page.isDirty()) {
file.writePage(page);
@@ -172,8 +172,8 @@ public class LRUCache<P extends Page> extends AbstractPageFile<P> {
throw new AbortException("Invalid cache size: " + cacheSizeBytes + " / " + header.getPageSize() + " = " + cacheSize);
}
- if(logger.isDebugging()) {
- logger.debug("LRU cache size is " + cacheSize + " pages.");
+ if(LOG.isDebugging()) {
+ LOG.debug("LRU cache size is " + cacheSize + " pages.");
}
float hashTableLoadFactor = 0.75f;
diff --git a/src/de/lmu/ifi/dbs/elki/persistent/OnDiskArray.java b/src/de/lmu/ifi/dbs/elki/persistent/OnDiskArray.java
index 7c62173d..a37dcbdb 100644
--- a/src/de/lmu/ifi/dbs/elki/persistent/OnDiskArray.java
+++ b/src/de/lmu/ifi/dbs/elki/persistent/OnDiskArray.java
@@ -105,12 +105,12 @@ public class OnDiskArray implements Serializable {
/**
* Size of the classes header size.
*/
- private final static int INTERNAL_HEADER_SIZE = 4 * ByteArrayUtil.SIZE_INT;
+ private static final int INTERNAL_HEADER_SIZE = 4 * ByteArrayUtil.SIZE_INT;
/**
* Position of file size (in records)
*/
- private final static int HEADER_POS_SIZE = 3 * ByteArrayUtil.SIZE_INT;
+ private static final int HEADER_POS_SIZE = 3 * ByteArrayUtil.SIZE_INT;
/**
* Constructor to write a new file.
diff --git a/src/de/lmu/ifi/dbs/elki/persistent/OnDiskArrayPageFile.java b/src/de/lmu/ifi/dbs/elki/persistent/OnDiskArrayPageFile.java
index 37a16384..1f8c2a1a 100644
--- a/src/de/lmu/ifi/dbs/elki/persistent/OnDiskArrayPageFile.java
+++ b/src/de/lmu/ifi/dbs/elki/persistent/OnDiskArrayPageFile.java
@@ -203,13 +203,11 @@ public class OnDiskArrayPageFile<P extends Page> extends AbstractStoringPageFile
}
}
catch(IOException e) {
- // TODO exception handling
- e.printStackTrace();
+ LoggingUtil.exception(e);
return null;
}
catch(ClassNotFoundException e) {
- // TODO exception handling
- e.printStackTrace();
+ LoggingUtil.exception(e);
return null;
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/persistent/OnDiskUpperTriangleMatrix.java b/src/de/lmu/ifi/dbs/elki/persistent/OnDiskUpperTriangleMatrix.java
index 638d85cd..044657c5 100644
--- a/src/de/lmu/ifi/dbs/elki/persistent/OnDiskUpperTriangleMatrix.java
+++ b/src/de/lmu/ifi/dbs/elki/persistent/OnDiskUpperTriangleMatrix.java
@@ -121,7 +121,7 @@ public class OnDiskUpperTriangleMatrix {
* @return size of the array
*/
private static int arraysize(int matrixsize) {
- return matrixsize * (matrixsize + 1) / 2;
+ return (matrixsize * (matrixsize + 1)) >> 1;
}
/**
@@ -135,7 +135,7 @@ public class OnDiskUpperTriangleMatrix {
if(y > x) {
return computeOffset(y, x);
}
- return (x * (x + 1)) / 2 + y;
+ return ((x * (x + 1)) >> 1) + y;
}
/**
diff --git a/src/de/lmu/ifi/dbs/elki/persistent/PageFileUtil.java b/src/de/lmu/ifi/dbs/elki/persistent/PageFileUtil.java
index 5fe11432..80a77fca 100644
--- a/src/de/lmu/ifi/dbs/elki/persistent/PageFileUtil.java
+++ b/src/de/lmu/ifi/dbs/elki/persistent/PageFileUtil.java
@@ -23,7 +23,6 @@ package de.lmu.ifi.dbs.elki.persistent;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-
/**
* Page file statistic utility functions.
*
@@ -39,11 +38,11 @@ public final class PageFileUtil {
*
* @param buffer Buffer to append to
*/
- public static void appendPageFileStatistics(StringBuffer buffer, PageFileStatistics statistics) {
+ public static void appendPageFileStatistics(StringBuilder buffer, PageFileStatistics statistics) {
if(statistics != null) {
- buffer.append("Page File Layer: ").append(statistics.getClass()).append("\n");
- buffer.append("Read Operations: ").append(statistics.getReadOperations()).append("\n");
- buffer.append("Write Operations: ").append(statistics.getWriteOperations()).append("\n");
+ buffer.append("Page File Layer: ").append(statistics.getClass()).append('\n');
+ buffer.append("Read Operations: ").append(statistics.getReadOperations()).append('\n');
+ buffer.append("Write Operations: ").append(statistics.getWriteOperations()).append('\n');
PageFileStatistics inner = statistics.getInnerStatistics();
if(inner != null) {
appendPageFileStatistics(buffer, inner);
diff --git a/src/de/lmu/ifi/dbs/elki/persistent/PersistentPageFile.java b/src/de/lmu/ifi/dbs/elki/persistent/PersistentPageFile.java
index 618707a5..750ed646 100644
--- a/src/de/lmu/ifi/dbs/elki/persistent/PersistentPageFile.java
+++ b/src/de/lmu/ifi/dbs/elki/persistent/PersistentPageFile.java
@@ -51,7 +51,7 @@ public class PersistentPageFile<P extends ExternalizablePage> extends AbstractSt
/**
* Our logger
*/
- private static final Logging logger = Logging.getLogger(PersistentPageFile.class);
+ private static final Logging LOG = Logging.getLogger(PersistentPageFile.class);
/**
* Indicates an empty page.
@@ -341,7 +341,7 @@ public class PersistentPageFile<P extends ExternalizablePage> extends AbstractSt
public boolean initialize(PageHeader header) {
try {
if(existed) {
- logger.debug("Initializing from an existing page file.");
+ LOG.debug("Initializing from an existing page file.");
// init the header
this.header = header;
@@ -384,7 +384,7 @@ public class PersistentPageFile<P extends ExternalizablePage> extends AbstractSt
}
// create new file
else {
- logger.debug("Initializing with a new page file.");
+ LOG.debug("Initializing with a new page file.");
// writing header
this.header = header;
diff --git a/src/de/lmu/ifi/dbs/elki/result/KMLOutputHandler.java b/src/de/lmu/ifi/dbs/elki/result/KMLOutputHandler.java
index 25734bdc..19a50b28 100644
--- a/src/de/lmu/ifi/dbs/elki/result/KMLOutputHandler.java
+++ b/src/de/lmu/ifi/dbs/elki/result/KMLOutputHandler.java
@@ -44,8 +44,8 @@ import de.lmu.ifi.dbs.elki.data.spatial.PolygonsObject;
import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
import de.lmu.ifi.dbs.elki.database.Database;
import de.lmu.ifi.dbs.elki.database.ids.ArrayModifiableDBIDs;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.logging.Logging;
@@ -84,7 +84,7 @@ public class KMLOutputHandler implements ResultHandler, Parameterizable {
/**
* Logger class to use.
*/
- public static final Logging logger = Logging.getLogger(KMLOutputHandler.class);
+ private static final Logging LOG = Logging.getLogger(KMLOutputHandler.class);
/**
* Number of styles to use (lower reduces rendering complexity a bit)
@@ -151,11 +151,11 @@ public class KMLOutputHandler implements ResultHandler, Parameterizable {
}
}
catch(XMLStreamException e) {
- logger.exception(e);
+ LOG.exception(e);
throw new AbortException("XML error in KML output.", e);
}
catch(IOException e) {
- logger.exception(e);
+ LOG.exception(e);
throw new AbortException("IO error in KML output.", e);
}
}
@@ -229,15 +229,14 @@ public class KMLOutputHandler implements ResultHandler, Parameterizable {
}
}
for (DBIDIter iter = outlierResult.getOrdering().iter(ids).iter(); iter.valid(); iter.advance()) {
- DBID id = iter.getDBID();
- Double score = scores.get(id);
- PolygonsObject poly = polys.get(id);
- String label = labels.get(id);
+ Double score = scores.get(iter);
+ PolygonsObject poly = polys.get(iter);
+ String label = labels.get(iter);
if(score == null) {
- logger.warning("No score for object " + id);
+ LOG.warning("No score for object " + DBIDUtil.toString(iter));
}
if(poly == null) {
- logger.warning("No polygon for object " + id + " - skipping.");
+ LOG.warning("No polygon for object " + DBIDUtil.toString(iter) + " - skipping.");
continue;
}
out.writeStartElement("Placemark");
@@ -245,7 +244,7 @@ public class KMLOutputHandler implements ResultHandler, Parameterizable {
out.writeStartElement("name");
out.writeCharacters(score + " " + label);
out.writeEndElement(); // name
- StringBuffer buf = makeDescription(otherrel, id);
+ StringBuilder buf = makeDescription(otherrel, iter);
out.writeStartElement("description");
out.writeCData("<div>" + buf.toString() + "</div>");
out.writeEndElement(); // description
@@ -310,8 +309,8 @@ public class KMLOutputHandler implements ResultHandler, Parameterizable {
* @param id Object ID
* @return Buffer
*/
- private StringBuffer makeDescription(Collection<Relation<?>> relations, DBID id) {
- StringBuffer buf = new StringBuffer();
+ private StringBuilder makeDescription(Collection<Relation<?>> relations, DBIDRef id) {
+ StringBuilder buf = new StringBuilder();
for(Relation<?> rel : relations) {
Object o = rel.get(id);
if(o == null) {
@@ -336,7 +335,7 @@ public class KMLOutputHandler implements ResultHandler, Parameterizable {
* @throws XMLStreamException
*/
private void writeNewlineOnDebug(XMLStreamWriter out) throws XMLStreamException {
- if(logger.isDebugging()) {
+ if(LOG.isDebugging()) {
out.writeCharacters("\n");
}
}
@@ -388,7 +387,7 @@ public class KMLOutputHandler implements ResultHandler, Parameterizable {
* Key: {@code -kml.scaling}
* </p>
*/
- public static final OptionID SCALING_ID = OptionID.getOrCreateOptionID("kml.scaling", "Additional scaling function for KML colorization.");
+ public static final OptionID SCALING_ID = new OptionID("kml.scaling", "Additional scaling function for KML colorization.");
/**
* Parameter for compatibility mode.
@@ -397,7 +396,7 @@ public class KMLOutputHandler implements ResultHandler, Parameterizable {
* Key: {@code -kml.compat}
* </p>
*/
- public static final OptionID COMPAT_ID = OptionID.getOrCreateOptionID("kml.compat", "Use simpler KML objects, compatibility mode.");
+ public static final OptionID COMPAT_ID = new OptionID("kml.compat", "Use simpler KML objects, compatibility mode.");
/**
* Parameter for automatically opening the output file.
@@ -406,7 +405,7 @@ public class KMLOutputHandler implements ResultHandler, Parameterizable {
* Key: {@code -kml.autoopen}
* </p>
*/
- public static final OptionID AUTOOPEN_ID = OptionID.getOrCreateOptionID("kml.autoopen", "Automatically open the result file.");
+ public static final OptionID AUTOOPEN_ID = new OptionID("kml.autoopen", "Automatically open the result file.");
/**
* Output file name
diff --git a/src/de/lmu/ifi/dbs/elki/result/LogResultStructureResultHandler.java b/src/de/lmu/ifi/dbs/elki/result/LogResultStructureResultHandler.java
new file mode 100644
index 00000000..61fae67a
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/result/LogResultStructureResultHandler.java
@@ -0,0 +1,94 @@
+package de.lmu.ifi.dbs.elki.result;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import de.lmu.ifi.dbs.elki.logging.Logging;
+import de.lmu.ifi.dbs.elki.utilities.datastructures.hierarchy.Hierarchy;
+import de.lmu.ifi.dbs.elki.utilities.documentation.Description;
+
+/**
+ * A result handler to help with ELKI development that will just show the
+ * structure of the result object.
+ *
+ * This class is mostly useful when you are implementing your own result
+ * handlers or evaluators, as it will print a simple representation of the
+ * result tree.
+ *
+ * For using this, make sure to set the logging level appropriately by using
+ * {@code -verbose}.
+ *
+ * TODO: transform this into an evaluator, then visualize, too?
+ *
+ * @author Erich Schubert
+ */
+@Description("Development result handler that merely logs the structure of the result tree.")
+public class LogResultStructureResultHandler implements ResultHandler {
+ /**
+ * Class logger
+ */
+ private static final Logging LOG = Logging.getLogger(LogResultStructureResultHandler.class);
+
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result newResult) {
+ if(LOG.isVerbose()) {
+ if(newResult instanceof HierarchicalResult) {
+ Hierarchy<Result> hier = ((HierarchicalResult) newResult).getHierarchy();
+ if(hier != null) {
+ StringBuilder buf = new StringBuilder();
+ recursiveLogResult(buf, hier, newResult, 0);
+ LOG.verbose(buf.toString());
+ }
+ }
+ }
+ }
+
+ /**
+ * Recursively walk through the result tree.
+ *
+ * @param buf Output buffer
+ * @param result Current result
+ * @param depth Depth
+ */
+ private void recursiveLogResult(StringBuilder buf, Hierarchy<Result> hier, Result result, int depth) {
+ if(result == null) {
+ buf.append("null");
+ LOG.warning("null result!");
+ return;
+ }
+ if(depth > 50) {
+ LOG.warning("Probably infinitely nested results, aborting!");
+ return;
+ }
+ for(int i = 0; i < depth; i++) {
+ buf.append(" ");
+ }
+ buf.append(result.getClass().getSimpleName()).append(": ").append(result.getLongName());
+ buf.append(" (").append(result.getShortName()).append(")\n");
+ if(hier.getChildren(result).size() > 0) {
+ for(Result r : hier.getChildren(result)) {
+ recursiveLogResult(buf, hier, r, depth + 1);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/result/OrderingFromDataStore.java b/src/de/lmu/ifi/dbs/elki/result/OrderingFromDataStore.java
index 7fa1a224..65713b11 100644
--- a/src/de/lmu/ifi/dbs/elki/result/OrderingFromDataStore.java
+++ b/src/de/lmu/ifi/dbs/elki/result/OrderingFromDataStore.java
@@ -27,7 +27,7 @@ import java.util.Comparator;
import de.lmu.ifi.dbs.elki.database.datastore.DataStore;
import de.lmu.ifi.dbs.elki.database.ids.ArrayModifiableDBIDs;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
@@ -134,9 +134,9 @@ public class OrderingFromDataStore<T extends Comparable<T>> extends BasicResult
*
* @apiviz.exclude
*/
- protected final class ImpliedComparator implements Comparator<DBID> {
+ protected final class ImpliedComparator implements Comparator<DBIDRef> {
@Override
- public int compare(DBID id1, DBID id2) {
+ public int compare(DBIDRef id1, DBIDRef id2) {
T k1 = map.get(id1);
T k2 = map.get(id2);
assert (k1 != null);
@@ -153,9 +153,9 @@ public class OrderingFromDataStore<T extends Comparable<T>> extends BasicResult
*
* @apiviz.exclude
*/
- protected final class DerivedComparator implements Comparator<DBID> {
+ protected final class DerivedComparator implements Comparator<DBIDRef> {
@Override
- public int compare(DBID id1, DBID id2) {
+ public int compare(DBIDRef id1, DBIDRef id2) {
T k1 = map.get(id1);
T k2 = map.get(id2);
assert (k1 != null);
diff --git a/src/de/lmu/ifi/dbs/elki/result/ResultHierarchy.java b/src/de/lmu/ifi/dbs/elki/result/ResultHierarchy.java
index bf2eefe6..2aba395b 100644
--- a/src/de/lmu/ifi/dbs/elki/result/ResultHierarchy.java
+++ b/src/de/lmu/ifi/dbs/elki/result/ResultHierarchy.java
@@ -41,7 +41,7 @@ public class ResultHierarchy extends HierarchyHashmapList<Result> {
/**
* Logger
*/
- private static final Logging logger = Logging.getLogger(ResultHierarchy.class);
+ private static final Logging LOG = Logging.getLogger(ResultHierarchy.class);
/**
* Holds the listener.
@@ -120,8 +120,8 @@ public class ResultHierarchy extends HierarchyHashmapList<Result> {
* @param parent Parent result that was added to
*/
private void fireResultAdded(Result child, Result parent) {
- if(logger.isDebugging()) {
- logger.debug("Result added: " + child + " <- " + parent);
+ if(LOG.isDebugging()) {
+ LOG.debug("Result added: " + child + " <- " + parent);
}
for(ResultListener l : listenerList.getListeners(ResultListener.class)) {
l.resultAdded(child, parent);
@@ -134,8 +134,8 @@ public class ResultHierarchy extends HierarchyHashmapList<Result> {
* @param current Result that has changed
*/
private void fireResultChanged(Result current) {
- if(logger.isDebugging()) {
- logger.debug("Result changed: " + current);
+ if(LOG.isDebugging()) {
+ LOG.debug("Result changed: " + current);
}
for(ResultListener l : listenerList.getListeners(ResultListener.class)) {
l.resultChanged(current);
@@ -150,8 +150,8 @@ public class ResultHierarchy extends HierarchyHashmapList<Result> {
* @param parent Parent result that has been removed
*/
private void fireResultRemoved(Result child, Result parent) {
- if(logger.isDebugging()) {
- logger.debug("Result removed: " + child + " <- " + parent);
+ if(LOG.isDebugging()) {
+ LOG.debug("Result removed: " + child + " <- " + parent);
}
for(ResultListener l : listenerList.getListeners(ResultListener.class)) {
l.resultRemoved(child, parent);
diff --git a/src/de/lmu/ifi/dbs/elki/result/ResultUtil.java b/src/de/lmu/ifi/dbs/elki/result/ResultUtil.java
index 921e7dd2..d081bc2e 100644
--- a/src/de/lmu/ifi/dbs/elki/result/ResultUtil.java
+++ b/src/de/lmu/ifi/dbs/elki/result/ResultUtil.java
@@ -252,7 +252,7 @@ public class ResultUtil {
* @param rel Relation
* @return associated scales result
*/
- public static ScalesResult getScalesResult(final Relation<? extends NumberVector<?, ?>> rel) {
+ public static ScalesResult getScalesResult(final Relation<? extends NumberVector<?>> rel) {
Collection<ScalesResult> scas = ResultUtil.filterResults(rel, ScalesResult.class);
if(scas.size() == 0) {
final ScalesResult newsca = new ScalesResult(rel);
diff --git a/src/de/lmu/ifi/dbs/elki/result/ResultWriter.java b/src/de/lmu/ifi/dbs/elki/result/ResultWriter.java
index ed8934a6..45475dd2 100644
--- a/src/de/lmu/ifi/dbs/elki/result/ResultWriter.java
+++ b/src/de/lmu/ifi/dbs/elki/result/ResultWriter.java
@@ -48,7 +48,7 @@ public class ResultWriter implements ResultHandler {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(ResultWriter.class);
+ private static final Logging LOG = Logging.getLogger(ResultWriter.class);
/**
* Flag to control GZIP compression.
@@ -56,7 +56,7 @@ public class ResultWriter implements ResultHandler {
* Key: {@code -out.gzip}
* </p>
*/
- public static final OptionID GZIP_OUTPUT_ID = OptionID.getOrCreateOptionID("out.gzip", "Enable gzip compression of output files.");
+ public static final OptionID GZIP_OUTPUT_ID = new OptionID("out.gzip", "Enable gzip compression of output files.");
/**
* Flag to suppress overwrite warning.
@@ -64,7 +64,7 @@ public class ResultWriter implements ResultHandler {
* Key: {@code -out.silentoverwrite}
* </p>
*/
- public static final OptionID OVERWRITE_OPTION_ID = OptionID.getOrCreateOptionID("out.silentoverwrite", "Silently overwrite output files.");
+ public static final OptionID OVERWRITE_OPTION_ID = new OptionID("out.silentoverwrite", "Silently overwrite output files.");
/**
* Holds the file to print results to.
@@ -107,13 +107,13 @@ public class ResultWriter implements ResultHandler {
else if(out.exists()) {
if(out.isDirectory()) {
if(warnoverwrite && out.listFiles().length > 0) {
- logger.warning("Output directory specified is not empty. Files will be overwritten and old files may be left over.");
+ LOG.warning("Output directory specified is not empty. Files will be overwritten and old files may be left over.");
}
output = new MultipleFilesOutput(out, gzip);
}
else {
if(warnoverwrite) {
- logger.warning("Output file exists and will be overwritten!");
+ LOG.warning("Output file exists and will be overwritten!");
}
output = new SingleStreamOutput(out, gzip);
}
diff --git a/src/de/lmu/ifi/dbs/elki/result/ScalesResult.java b/src/de/lmu/ifi/dbs/elki/result/ScalesResult.java
index 7def7b53..eca62474 100644
--- a/src/de/lmu/ifi/dbs/elki/result/ScalesResult.java
+++ b/src/de/lmu/ifi/dbs/elki/result/ScalesResult.java
@@ -46,7 +46,7 @@ public class ScalesResult extends BasicResult {
*
* @param relation Relation to use
*/
- public ScalesResult(Relation<? extends NumberVector<?, ?>> relation) {
+ public ScalesResult(Relation<? extends NumberVector<?>> relation) {
this(Scales.calcScales(relation));
}
diff --git a/src/de/lmu/ifi/dbs/elki/result/SettingsResult.java b/src/de/lmu/ifi/dbs/elki/result/SettingsResult.java
index 0d0dc34d..d8c24737 100644
--- a/src/de/lmu/ifi/dbs/elki/result/SettingsResult.java
+++ b/src/de/lmu/ifi/dbs/elki/result/SettingsResult.java
@@ -34,27 +34,28 @@ import de.lmu.ifi.dbs.elki.utilities.pairs.Pair;
*
* @author Erich Schubert
*/
-public class SettingsResult extends BasicResult {
+public class SettingsResult extends BasicResult {
/**
* Settings storage.
*/
- Collection<Pair<Object, Parameter<?,?>>> settings;
-
+ Collection<Pair<Object, Parameter<?>>> settings;
+
/**
* Constructor.
*
* @param settings Settings to store
*/
- public SettingsResult(Collection<Pair<Object, Parameter<?,?>>> settings) {
+ public SettingsResult(Collection<Pair<Object, Parameter<?>>> settings) {
super("Settings", "settings");
this.settings = settings;
}
-
+
/**
* Get the settings
+ *
* @return the settings
*/
- public Collection<Pair<Object, Parameter<?,?>>> getSettings() {
+ public Collection<Pair<Object, Parameter<?>>> getSettings() {
return settings;
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/result/optics/DoubleDistanceClusterOrderEntry.java b/src/de/lmu/ifi/dbs/elki/result/optics/DoubleDistanceClusterOrderEntry.java
index 533384aa..f2ebcce3 100644
--- a/src/de/lmu/ifi/dbs/elki/result/optics/DoubleDistanceClusterOrderEntry.java
+++ b/src/de/lmu/ifi/dbs/elki/result/optics/DoubleDistanceClusterOrderEntry.java
@@ -24,6 +24,7 @@ package de.lmu.ifi.dbs.elki.result.optics;
*/
import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
/**
@@ -74,13 +75,13 @@ public class DoubleDistanceClusterOrderEntry implements Comparable<ClusterOrderE
if(this == o) {
return true;
}
- if(o == null || !(o instanceof ClusterOrderEntry)) {
+ if(!(o instanceof ClusterOrderEntry)) {
return false;
}
final ClusterOrderEntry<?> that = (ClusterOrderEntry<?>) o;
// Compare by ID only, for UpdatableHeap!
- return objectID.sameDBID(that.getID());
+ return DBIDUtil.equal(objectID, that.getID());
}
/**
diff --git a/src/de/lmu/ifi/dbs/elki/result/optics/GenericClusterOrderEntry.java b/src/de/lmu/ifi/dbs/elki/result/optics/GenericClusterOrderEntry.java
index 47ee16e5..b04d35ea 100644
--- a/src/de/lmu/ifi/dbs/elki/result/optics/GenericClusterOrderEntry.java
+++ b/src/de/lmu/ifi/dbs/elki/result/optics/GenericClusterOrderEntry.java
@@ -24,6 +24,7 @@ package de.lmu.ifi.dbs.elki.result.optics;
*/
import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
/**
@@ -75,13 +76,13 @@ public class GenericClusterOrderEntry<D extends Distance<D>> implements Comparab
if(this == o) {
return true;
}
- if(o == null || !(o instanceof ClusterOrderEntry)) {
+ if(!(o instanceof ClusterOrderEntry)) {
return false;
}
final ClusterOrderEntry<?> that = (ClusterOrderEntry<?>) o;
// Compare by ID only, for UpdatableHeap!
- return objectID.sameDBID(that.getID());
+ return DBIDUtil.equal(objectID, that.getID());
}
/**
diff --git a/src/de/lmu/ifi/dbs/elki/result/outlier/OrderingFromRelation.java b/src/de/lmu/ifi/dbs/elki/result/outlier/OrderingFromRelation.java
index b272bd56..a60bda3d 100644
--- a/src/de/lmu/ifi/dbs/elki/result/outlier/OrderingFromRelation.java
+++ b/src/de/lmu/ifi/dbs/elki/result/outlier/OrderingFromRelation.java
@@ -26,7 +26,7 @@ package de.lmu.ifi.dbs.elki.result.outlier;
import java.util.Comparator;
import de.lmu.ifi.dbs.elki.database.ids.ArrayModifiableDBIDs;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
@@ -100,9 +100,9 @@ public class OrderingFromRelation implements OrderingResult {
*
* @apiviz.exclude
*/
- protected final class ImpliedComparator implements Comparator<DBID> {
+ protected final class ImpliedComparator implements Comparator<DBIDRef> {
@Override
- public int compare(DBID id1, DBID id2) {
+ public int compare(DBIDRef id1, DBIDRef id2) {
Double k1 = scores.get(id1);
Double k2 = scores.get(id2);
assert (k1 != null);
diff --git a/src/de/lmu/ifi/dbs/elki/result/textwriter/MultipleFilesOutput.java b/src/de/lmu/ifi/dbs/elki/result/textwriter/MultipleFilesOutput.java
index d891f1ee..2e089274 100644
--- a/src/de/lmu/ifi/dbs/elki/result/textwriter/MultipleFilesOutput.java
+++ b/src/de/lmu/ifi/dbs/elki/result/textwriter/MultipleFilesOutput.java
@@ -42,12 +42,12 @@ public class MultipleFilesOutput implements StreamFactory {
/**
* File name extension.
*/
- private final static String EXTENSION = ".txt";
+ private static final String EXTENSION = ".txt";
/**
* GZip extra file extension
*/
- private final static String GZIP_EXTENSION = ".gz";
+ private static final String GZIP_EXTENSION = ".gz";
/**
* Default stream to write to when no name is supplied.
@@ -72,7 +72,7 @@ public class MultipleFilesOutput implements StreamFactory {
/**
* Logger for debugging.
*/
- private final static Logging logger = Logging.getLogger(MultipleFilesOutput.class);
+ private static final Logging LOG = Logging.getLogger(MultipleFilesOutput.class);
/**
* Constructor
@@ -116,8 +116,8 @@ public class MultipleFilesOutput implements StreamFactory {
* @throws IOException
*/
private PrintStream newStream(String name) throws IOException {
- if (logger.isDebuggingFiner()) {
- logger.debugFiner("Requested stream: "+name);
+ if (LOG.isDebuggingFiner()) {
+ LOG.debugFiner("Requested stream: "+name);
}
PrintStream res = map.get(name);
if(res != null) {
@@ -138,8 +138,8 @@ public class MultipleFilesOutput implements StreamFactory {
os = new GZIPOutputStream(os);
}
res = new PrintStream(os);
- if (logger.isDebuggingFiner()) {
- logger.debugFiner("Opened new output stream:"+fn);
+ if (LOG.isDebuggingFiner()) {
+ LOG.debugFiner("Opened new output stream:"+fn);
}
// cache.
map.put(name, res);
diff --git a/src/de/lmu/ifi/dbs/elki/result/textwriter/TextWriter.java b/src/de/lmu/ifi/dbs/elki/result/textwriter/TextWriter.java
index 85afbc8e..e5b05883 100644
--- a/src/de/lmu/ifi/dbs/elki/result/textwriter/TextWriter.java
+++ b/src/de/lmu/ifi/dbs/elki/result/textwriter/TextWriter.java
@@ -46,6 +46,7 @@ import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
import de.lmu.ifi.dbs.elki.database.Database;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.datasource.bundle.SingleObjectBundle;
@@ -95,7 +96,7 @@ public class TextWriter {
/**
* Hash map for supported classes in writer.
*/
- public final static HandlerList<TextWriterWriterInterface<?>> writers = new HandlerList<TextWriterWriterInterface<?>>();
+ public static final HandlerList<TextWriterWriterInterface<?>> writers = new HandlerList<TextWriterWriterInterface<?>>();
/**
* Add some default handlers
@@ -173,7 +174,7 @@ public class TextWriter {
if(sr != null) {
for(SettingsResult settings : sr) {
Object last = null;
- for(Pair<Object, Parameter<?, ?>> setting : settings.getSettings()) {
+ for(Pair<Object, Parameter<?>> setting : settings.getSettings()) {
if(setting.first != last && setting.first != null) {
if(last != null) {
out.commentPrintLn("");
@@ -280,7 +281,7 @@ public class TextWriter {
}
}
- private void printObject(TextWriterStream out, Database db, final DBID objID, List<Relation<?>> ra) throws UnableToComplyException, IOException {
+ private void printObject(TextWriterStream out, Database db, final DBIDRef objID, List<Relation<?>> ra) throws UnableToComplyException, IOException {
SingleObjectBundle bundle = db.getBundle(objID);
// Write database element itself.
for(int i = 0; i < bundle.metaLength(); i++) {
@@ -359,7 +360,7 @@ public class TextWriter {
mwri.writeObject(out, null, model);
}
if(clus.getParents().size() > 0) {
- StringBuffer buf = new StringBuffer();
+ StringBuilder buf = new StringBuilder();
buf.append("Parents:");
for(Cluster<?> parent : clus.getParents()) {
buf.append(" ").append(naming.getNameFor(parent));
@@ -367,7 +368,7 @@ public class TextWriter {
out.commentPrintLn(buf.toString());
}
if(clus.getChildren().size() > 0) {
- StringBuffer buf = new StringBuffer();
+ StringBuilder buf = new StringBuilder();
buf.append("Children:");
for(Cluster<?> child : clus.getChildren()) {
buf.append(" ").append(naming.getNameFor(child));
@@ -379,7 +380,7 @@ public class TextWriter {
// print ids.
DBIDs ids = clus.getIDs();
for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
- printObject(out, db, iter.getDBID(), ra);
+ printObject(out, db, iter, ra);
}
out.commentPrintSeparator();
out.flush();
@@ -419,7 +420,7 @@ public class TextWriter {
printSettings(out, sr);
for (DBIDIter i = or.iter(or.getDBIDs()).iter(); i.valid(); i.advance()) {
- printObject(out, db, i.getDBID(), ra);
+ printObject(out, db, i, ra);
}
out.commentPrintSeparator();
out.flush();
diff --git a/src/de/lmu/ifi/dbs/elki/result/textwriter/TextWriterStream.java b/src/de/lmu/ifi/dbs/elki/result/textwriter/TextWriterStream.java
index ce60c856..7c905fb3 100644
--- a/src/de/lmu/ifi/dbs/elki/result/textwriter/TextWriterStream.java
+++ b/src/de/lmu/ifi/dbs/elki/result/textwriter/TextWriterStream.java
@@ -43,12 +43,12 @@ public class TextWriterStream {
/**
* Buffer for inline data to output.
*/
- private StringBuffer inline;
+ private StringBuilder inline;
/**
* Buffer for comment data to output.
*/
- private StringBuffer comment;
+ private StringBuilder comment;
/**
* Handlers for various object types.
@@ -74,12 +74,12 @@ public class TextWriterStream {
/**
* System newline character(s)
*/
- private final static String NEWLINE = System.getProperty("line.separator");
+ private static final String NEWLINE = System.getProperty("line.separator");
/**
* Marker used in text serialization (and re-parsing)
*/
- public final static String SER_MARKER = "Serialization class:";
+ public static final String SER_MARKER = "Serialization class:";
/**
* Force incomments flag
@@ -96,8 +96,8 @@ public class TextWriterStream {
public TextWriterStream(PrintStream out, HandlerList<TextWriterWriterInterface<?>> writers) {
this.outStream = out;
this.writers = writers;
- inline = new StringBuffer();
- comment = new StringBuffer();
+ inline = new StringBuilder();
+ comment = new StringBuilder();
}
/**
diff --git a/src/de/lmu/ifi/dbs/elki/result/textwriter/naming/SimpleEnumeratingScheme.java b/src/de/lmu/ifi/dbs/elki/result/textwriter/naming/SimpleEnumeratingScheme.java
index baff04e2..c1d0f8e5 100644
--- a/src/de/lmu/ifi/dbs/elki/result/textwriter/naming/SimpleEnumeratingScheme.java
+++ b/src/de/lmu/ifi/dbs/elki/result/textwriter/naming/SimpleEnumeratingScheme.java
@@ -58,7 +58,7 @@ public class SimpleEnumeratingScheme implements NamingScheme {
* This is the postfix added to the first cluster, which will be removed when
* there is only one cluster of this name.
*/
- private final static String nullpostfix = " " + Integer.toString(0);
+ private static final String nullpostfix = " " + Integer.toString(0);
/**
* Constructor.
diff --git a/src/de/lmu/ifi/dbs/elki/result/textwriter/writers/TextWriterObjectArray.java b/src/de/lmu/ifi/dbs/elki/result/textwriter/writers/TextWriterObjectArray.java
index 3a73d52a..be195648 100644
--- a/src/de/lmu/ifi/dbs/elki/result/textwriter/writers/TextWriterObjectArray.java
+++ b/src/de/lmu/ifi/dbs/elki/result/textwriter/writers/TextWriterObjectArray.java
@@ -38,7 +38,7 @@ public class TextWriterObjectArray<T> extends TextWriterWriterInterface<T[]> {
*/
@Override
public void write(TextWriterStream out, String label, T[] v) {
- StringBuffer buf = new StringBuffer();
+ StringBuilder buf = new StringBuilder();
if(label != null) {
buf.append(label).append("=");
}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/Base64.java b/src/de/lmu/ifi/dbs/elki/utilities/Base64.java
index 9ea9a024..bcd4c8ff 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/Base64.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/Base64.java
@@ -30,8 +30,8 @@ import java.lang.reflect.Method;
*
* This is a rather ugly hack; it would maybe have been sensible to just import
* one of the publicly available (and fast) Base64 encoders. The expectation was
- * that at some point, Oracle will acutally include a public and fast Base64
- * encoder in Java.
+ * that at some point, Oracle will actually include a public and fast Base64
+ * encoder in Java...
*
* @author Erich Schubert
*/
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/BitsUtil.java b/src/de/lmu/ifi/dbs/elki/utilities/BitsUtil.java
index d2dc6733..ed00dbab 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/BitsUtil.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/BitsUtil.java
@@ -112,7 +112,7 @@ public final class BitsUtil {
*/
public static long[] copy(long[] v, int mincap) {
int words = ((mincap - 1) >>> LONG_LOG2_SIZE) + 1;
- if(v.length == words) {
+ if (v.length == words) {
return Arrays.copyOf(v, v.length);
}
long[] ret = new long[words];
@@ -132,15 +132,15 @@ public final class BitsUtil {
*/
public static long[] copy(long[] v, int mincap, int shift) {
int words = ((mincap - 1) >>> LONG_LOG2_SIZE) + 1;
- if(v.length == words && shift == 0) {
+ if (v.length == words && shift == 0) {
return Arrays.copyOf(v, v.length);
}
long[] ret = new long[words];
final int shiftWords = shift >>> LONG_LOG2_SIZE;
final int shiftBits = shift & LONG_LOG2_MASK;
// Simple case - multiple of word size
- if(shiftBits == 0) {
- for(int i = shiftWords; i < ret.length; i++) {
+ if (shiftBits == 0) {
+ for (int i = shiftWords; i < ret.length; i++) {
ret[i] |= v[i - shiftWords];
}
return ret;
@@ -148,7 +148,7 @@ public final class BitsUtil {
// Overlapping case
final int unshiftBits = Long.SIZE - shiftBits;
final int end = Math.min(ret.length, v.length + shiftWords) - 1;
- for(int i = end; i > shiftWords; i--) {
+ for (int i = end; i > shiftWords; i--) {
final int src = i - shiftWords;
ret[i] |= (v[src] << shiftBits) | (v[src - 1] >>> unshiftBits);
}
@@ -206,15 +206,15 @@ public final class BitsUtil {
final int last = v.length - 1;
int o;
// Sub word level:
- for(o = 1; o < Long.SIZE; o <<= 1) {
- for(int i = 0; i < last; i++) {
+ for (o = 1; o < Long.SIZE; o <<= 1) {
+ for (int i = 0; i < last; i++) {
v[i] ^= (v[i] >>> o) ^ (v[i + 1] << (Long.SIZE - o));
}
v[last] ^= (v[last] >>> o);
}
// Word level:
- for(o = 1; o <= last; o <<= 1) {
- for(int i = o; i <= last; i++) {
+ for (o = 1; o <= last; o <<= 1) {
+ for (int i = o; i <= last; i++) {
v[i - o] ^= v[i];
}
}
@@ -228,8 +228,8 @@ public final class BitsUtil {
* @return true when all zero
*/
public static boolean isZero(long[] v) {
- for(int i = 0; i < v.length; i++) {
- if(v[i] != 0) {
+ for (int i = 0; i < v.length; i++) {
+ if (v[i] != 0) {
return false;
}
}
@@ -256,7 +256,7 @@ public final class BitsUtil {
*/
public static long cardinality(long[] v) {
int sum = 0;
- for(int i = 0; i < v.length; i++) {
+ for (int i = 0; i < v.length; i++) {
sum += Long.bitCount(v[i]);
}
return sum;
@@ -382,7 +382,7 @@ public final class BitsUtil {
*/
public static long[] xorI(long[] v, long[] o) {
assert (o.length <= v.length) : "Bit set sizes do not agree.";
- for(int i = 0; i < o.length; i++) {
+ for (int i = 0; i < o.length; i++) {
v[i] ^= o[i];
}
return v;
@@ -397,23 +397,23 @@ public final class BitsUtil {
* @return v
*/
public static long[] xorI(long[] v, long[] o, int off) {
- if(off == 0) {
+ if (off == 0) {
return xorI(v, o);
}
- if(off < 0) {
+ if (off < 0) {
throw new UnsupportedOperationException("Negative shifts are not supported.");
}
// Break shift into integers to shift and bits to shift
final int shiftWords = off >>> LONG_LOG2_SIZE;
final int shiftBits = off & LONG_LOG2_MASK;
- if(shiftWords >= v.length) {
+ if (shiftWords >= v.length) {
return v;
}
// Simple case - multiple of word size
- if(shiftBits == 0) {
+ if (shiftBits == 0) {
final int end = Math.min(v.length, o.length + shiftWords);
- for(int i = shiftWords; i < end; i++) {
+ for (int i = shiftWords; i < end; i++) {
v[i] ^= o[i - shiftWords];
}
return v;
@@ -421,7 +421,7 @@ public final class BitsUtil {
// Overlapping case
final int unshiftBits = Long.SIZE - shiftBits;
final int end = Math.min(v.length, o.length + shiftWords) - 1;
- for(int i = end; i > shiftWords; i--) {
+ for (int i = end; i > shiftWords; i--) {
final int src = i - shiftWords;
v[i] ^= (o[src] << shiftBits) | (o[src - 1] >>> unshiftBits);
}
@@ -439,7 +439,7 @@ public final class BitsUtil {
public static long[] orI(long[] v, long[] o) {
assert (o.length <= v.length) : "Bit set sizes do not agree.";
final int max = Math.min(v.length, o.length);
- for(int i = 0; i < max; i++) {
+ for (int i = 0; i < max; i++) {
v[i] |= o[i];
}
return v;
@@ -456,23 +456,23 @@ public final class BitsUtil {
* @return v
*/
public static long[] orI(long[] v, long[] o, int off) {
- if(off == 0) {
+ if (off == 0) {
return orI(v, o);
}
- if(off < 0) {
+ if (off < 0) {
throw new UnsupportedOperationException("Negative shifts are not supported.");
}
// Break shift into integers to shift and bits to shift
final int shiftWords = off >>> LONG_LOG2_SIZE;
final int shiftBits = off & LONG_LOG2_MASK;
- if(shiftWords >= v.length) {
+ if (shiftWords >= v.length) {
return v;
}
// Simple case - multiple of word size
- if(shiftBits == 0) {
+ if (shiftBits == 0) {
final int end = Math.min(v.length, o.length + shiftWords);
- for(int i = shiftWords; i < end; i++) {
+ for (int i = shiftWords; i < end; i++) {
v[i] |= o[i - shiftWords];
}
return v;
@@ -480,7 +480,7 @@ public final class BitsUtil {
// Overlapping case
final int unshiftBits = Long.SIZE - shiftBits;
final int end = Math.min(v.length, o.length + shiftWords) - 1;
- for(int i = end; i > shiftWords; i--) {
+ for (int i = end; i > shiftWords; i--) {
final int src = i - shiftWords;
v[i] |= (o[src] << shiftBits) | (o[src - 1] >>> unshiftBits);
}
@@ -497,7 +497,7 @@ public final class BitsUtil {
*/
public static long[] andI(long[] v, long[] o) {
int i = 0;
- for(; i < o.length; i++) {
+ for (; i < o.length; i++) {
v[i] |= o[i];
}
// Zero higher words
@@ -514,23 +514,23 @@ public final class BitsUtil {
* @return v
*/
public static long[] andI(long[] v, long[] o, int off) {
- if(off == 0) {
+ if (off == 0) {
return andI(v, o);
}
- if(off < 0) {
+ if (off < 0) {
throw new UnsupportedOperationException("Negative shifts are not supported.");
}
// Break shift into integers to shift and bits to shift
final int shiftWords = off >>> LONG_LOG2_SIZE;
final int shiftBits = off & LONG_LOG2_MASK;
- if(shiftWords >= v.length) {
+ if (shiftWords >= v.length) {
return v;
}
// Simple case - multiple of word size
- if(shiftBits == 0) {
+ if (shiftBits == 0) {
final int end = Math.min(v.length, o.length + shiftWords);
- for(int i = shiftWords; i < end; i++) {
+ for (int i = shiftWords; i < end; i++) {
v[i] &= o[i - shiftWords];
}
// Clear bottom words
@@ -541,7 +541,7 @@ public final class BitsUtil {
final int unshiftBits = Long.SIZE - shiftBits;
final int end = Math.min(v.length, o.length + shiftWords) - 1;
Arrays.fill(v, end + 1, v.length, 0);
- for(int i = end; i > shiftWords; i--) {
+ for (int i = end; i > shiftWords; i--) {
final int src = i - shiftWords;
v[i] &= (o[src] << shiftBits) | (o[src - 1] >>> unshiftBits);
}
@@ -558,7 +558,7 @@ public final class BitsUtil {
* @return v
*/
public static long[] invertI(long[] v) {
- for(int i = 0; i < v.length; i++) {
+ for (int i = 0; i < v.length; i++) {
v[i] = ~v[i];
}
return v;
@@ -574,21 +574,21 @@ public final class BitsUtil {
* @return Bitset
*/
public static long[] shiftRightI(long[] v, int off) {
- if(off == 0) {
+ if (off == 0) {
return v;
}
- if(off < 0) {
+ if (off < 0) {
return shiftLeftI(v, -off);
}
// Break shift into integers to shift and bits to shift
final int shiftWords = off >>> LONG_LOG2_SIZE;
final int shiftBits = off & LONG_LOG2_MASK;
- if(shiftWords >= v.length) {
+ if (shiftWords >= v.length) {
return zeroI(v);
}
// Simple case - multiple of word size
- if(shiftBits == 0) {
+ if (shiftBits == 0) {
// Move whole words down
System.arraycopy(v, shiftWords, v, 0, v.length - shiftWords);
// Fill top words with zeros
@@ -598,7 +598,7 @@ public final class BitsUtil {
// Overlapping case
final int unshiftBits = Long.SIZE - shiftBits;
// Bottom-up to not overlap the operations.
- for(int i = 0; i < v.length - shiftWords - 1; i++) {
+ for (int i = 0; i < v.length - shiftWords - 1; i++) {
final int src = i + shiftWords;
v[i] = (v[src + 1] << unshiftBits) | (v[src] >>> shiftBits);
}
@@ -619,21 +619,21 @@ public final class BitsUtil {
* @return Bitset
*/
public static long[] shiftLeftI(long[] v, int off) {
- if(off == 0) {
+ if (off == 0) {
return v;
}
- if(off < 0) {
+ if (off < 0) {
return shiftRightI(v, -off);
}
// Break shift into integers to shift and bits to shift
final int shiftWords = off >>> LONG_LOG2_SIZE;
final int shiftBits = off & LONG_LOG2_MASK;
- if(shiftWords >= v.length) {
+ if (shiftWords >= v.length) {
return zeroI(v);
}
// Simple case - multiple of word size
- if(shiftBits == 0) {
+ if (shiftBits == 0) {
// Move whole words up
System.arraycopy(v, 0, v, shiftWords, v.length - shiftWords);
// Fill the initial words with zeros
@@ -643,7 +643,7 @@ public final class BitsUtil {
// Overlapping case
final int unshiftBits = Long.SIZE - shiftBits;
// Top-Down to not overlap the operations.
- for(int i = v.length - 1; i > shiftWords; i--) {
+ for (int i = v.length - 1; i > shiftWords; i--) {
final int src = i - shiftWords;
v[i] = (v[src] << shiftBits) | (v[src - 1] >>> unshiftBits);
}
@@ -662,10 +662,10 @@ public final class BitsUtil {
* @return cycled bit set
*/
public static long cycleRightC(long v, int shift, int len) {
- if(shift == 0) {
+ if (shift == 0) {
return v;
}
- if(shift < 0) {
+ if (shift < 0) {
return cycleLeftC(v, -shift, len);
}
final long ones = (1 << len) - 1;
@@ -698,7 +698,7 @@ public final class BitsUtil {
final int zapWords = (zap >>> LONG_LOG2_SIZE);
final int zapbits = zap & LONG_LOG2_MASK;
Arrays.fill(v, v.length - zapWords, v.length, 0);
- if(zapbits > 0) {
+ if (zapbits > 0) {
v[v.length - zapWords - 1] &= (LONG_ALL_BITS >>> zapbits);
}
return v;
@@ -713,10 +713,10 @@ public final class BitsUtil {
* @return cycled bit set
*/
public static long cycleLeftC(long v, int shift, int len) {
- if(shift == 0) {
+ if (shift == 0) {
return v;
}
- if(shift < 0) {
+ if (shift < 0) {
return cycleRightC(v, -shift, len);
}
final long ones = (1 << len) - 1;
@@ -746,20 +746,20 @@ public final class BitsUtil {
*/
public static String toString(long[] v) {
final int mag = magnitude(v);
- if(v.length == 0 || mag == 0) {
+ if (v.length == 0 || mag == 0) {
return "0";
}
final int words = ((mag - 1) >>> LONG_LOG2_SIZE) + 1;
char[] digits = new char[mag];
int pos = mag - 1;
- for(int w = 0; w < words; w++) {
- long f = 1l;
- for(int i = 0; i < Long.SIZE; i++) {
+ for (int w = 0; w < words; w++) {
+ long f = 1L;
+ for (int i = 0; i < Long.SIZE; i++) {
digits[pos] = ((v[w] & f) == 0) ? '0' : '1';
pos--;
f <<= 1;
- if(pos < 0) {
+ if (pos < 0) {
break;
}
}
@@ -776,20 +776,20 @@ public final class BitsUtil {
*/
public static String toString(long[] v, int minw) {
final int mag = Math.max(magnitude(v), minw);
- if(v.length == 0 || mag == 0) {
+ if (v.length == 0 || mag == 0) {
return "0";
}
final int words = ((mag - 1) >>> LONG_LOG2_SIZE) + 1;
char[] digits = new char[mag];
int pos = mag - 1;
- for(int w = 0; w < words; w++) {
- long f = 1l;
- for(int i = 0; i < Long.SIZE; i++) {
+ for (int w = 0; w < words; w++) {
+ long f = 1L;
+ for (int i = 0; i < Long.SIZE; i++) {
digits[pos] = ((v[w] & f) == 0) ? '0' : '1';
pos--;
f <<= 1;
- if(pos < 0) {
+ if (pos < 0) {
break;
}
}
@@ -805,18 +805,18 @@ public final class BitsUtil {
*/
public static String toString(long v) {
final int mag = magnitude(v);
- if(mag == 0) {
+ if (mag == 0) {
return "0";
}
char[] digits = new char[mag];
int pos = mag - 1;
- long f = 1l;
- for(int i = 0; i < Long.SIZE; i++) {
+ long f = 1L;
+ for (int i = 0; i < Long.SIZE; i++) {
digits[pos] = ((v & f) == 0) ? '0' : '1';
pos--;
f <<= 1;
- if(pos < 0) {
+ if (pos < 0) {
break;
}
}
@@ -830,11 +830,11 @@ public final class BitsUtil {
* @return Position of first set bit, -1 if no set bit was found.
*/
public static int numberOfTrailingZerosSigned(long[] v) {
- for(int p = 0;; p++) {
- if(p == v.length) {
+ for (int p = 0;; p++) {
+ if (p == v.length) {
return -1;
}
- if(v[p] != 0) {
+ if (v[p] != 0) {
return Long.numberOfTrailingZeros(v[p]) + p * Long.SIZE;
}
}
@@ -847,11 +847,11 @@ public final class BitsUtil {
* @return Position of first set bit, v.length * 64 if no set bit was found.
*/
public static int numberOfTrailingZeros(long[] v) {
- for(int p = 0;; p++) {
- if(p == v.length) {
+ for (int p = 0;; p++) {
+ if (p == v.length) {
return p * Long.SIZE;
}
- if(v[p] != 0) {
+ if (v[p] != 0) {
return Long.numberOfTrailingZeros(v[p]) + p * Long.SIZE;
}
}
@@ -860,8 +860,8 @@ public final class BitsUtil {
/**
* Find the number of trailing zeros.
*
- * Note: this has different semantics to {@link Long#numberOfLeadingZeros}
- * when the number is 0.
+ * Note: this has different semantics to {@link Long#numberOfLeadingZeros}
+ * when the number is 0.
*
* @param v Long
* @return Position of first set bit, -1 if no set bit was found.
@@ -869,7 +869,7 @@ public final class BitsUtil {
public static int numberOfTrailingZerosSigned(long v) {
return Long.numberOfTrailingZeros(v);
}
-
+
/**
* Find the number of trailing zeros.
*
@@ -883,17 +883,29 @@ public final class BitsUtil {
}
/**
+ * Find the number of trailing zeros.
+ *
+ * Note: this is the same as {@link Long#numberOfTrailingZeros}
+ *
+ * @param v Long
+ * @return Position of first set bit, 64 if no set bit was found.
+ */
+ public static int numberOfTrailingZeros(int v) {
+ return Integer.numberOfTrailingZeros(v);
+ }
+
+ /**
* Find the number of leading zeros.
*
* @param v Bitset
* @return Position of first set bit, -1 if no set bit was found.
*/
public static int numberOfLeadingZerosSigned(long[] v) {
- for(int p = 0, ip = v.length - 1;; p++, ip--) {
- if(p == v.length) {
+ for (int p = 0, ip = v.length - 1;; p++, ip--) {
+ if (p == v.length) {
return -1;
}
- if(v[ip] != 0) {
+ if (v[ip] != 0) {
return Long.numberOfLeadingZeros(v[ip]) + p * Long.SIZE;
}
}
@@ -906,11 +918,11 @@ public final class BitsUtil {
* @return Position of first set bit, v.length * 64 if no set bit was found.
*/
public static int numberOfLeadingZeros(long[] v) {
- for(int p = 0, ip = v.length - 1;; p++, ip--) {
- if(p == v.length) {
+ for (int p = 0, ip = v.length - 1;; p++, ip--) {
+ if (p == v.length) {
return p * Long.SIZE;
}
- if(v[ip] != 0) {
+ if (v[ip] != 0) {
return Long.numberOfLeadingZeros(v[ip]) + p * Long.SIZE;
}
}
@@ -926,13 +938,29 @@ public final class BitsUtil {
* @return Position of first set bit, -1 if no set bit was found.
*/
public static int numberOfLeadingZerosSigned(long v) {
- if(v == 0) {
+ if (v == 0) {
return -1;
}
return Long.numberOfLeadingZeros(v);
}
/**
+ * Find the number of leading zeros; -1 if all zero
+ *
+ * Note: this has different semantics to {@link Long#numberOfLeadingZeros}
+ * when the number is 0.
+ *
+ * @param v Bitset
+ * @return Position of first set bit, -1 if no set bit was found.
+ */
+ public static int numberOfLeadingZerosSigned(int v) {
+ if (v == 0) {
+ return -1;
+ }
+ return Integer.numberOfLeadingZeros(v);
+ }
+
+ /**
* Find the number of leading zeros; 64 if all zero
*
* Note: this the same as {@link Long#numberOfLeadingZeros}.
@@ -941,7 +969,19 @@ public final class BitsUtil {
* @return Position of first set bit, 64 if no set bit was found.
*/
public static int numberOfLeadingZeros(long v) {
- return Long.numberOfLeadingZeros(v);
+ return Long.SIZE - magnitude(v);
+ }
+
+ /**
+ * Find the number of leading zeros; 64 if all zero
+ *
+ * Note: this the same as {@link Long#numberOfLeadingZeros}.
+ *
+ * @param v Bitset
+ * @return Position of first set bit, 64 if no set bit was found.
+ */
+ public static int numberOfLeadingZeros(int v) {
+ return Integer.SIZE - magnitude(v);
}
/**
@@ -952,21 +992,21 @@ public final class BitsUtil {
* @return Position of previous set bit, or -1.
*/
public static int previousSetBit(long[] v, int start) {
- if(start == -1) {
+ if (start == -1) {
return -1;
}
int wordindex = start >>> LONG_LOG2_SIZE;
- if(wordindex >= v.length) {
+ if (wordindex >= v.length) {
return magnitude(v) - 1;
}
// Initial word
final int off = Long.SIZE - 1 - (start & LONG_LOG2_MASK);
long cur = v[wordindex] & (LONG_ALL_BITS >>> off);
- for(;;) {
- if(cur != 0) {
+ for (;;) {
+ if (cur != 0) {
return (wordindex + 1) * Long.SIZE - 1 - Long.numberOfLeadingZeros(cur);
}
- if(wordindex == 0) {
+ if (wordindex == 0) {
return -1;
}
wordindex--;
@@ -982,21 +1022,21 @@ public final class BitsUtil {
* @return Position of previous clear bit, or -1.
*/
public static int previousClearBit(long[] v, int start) {
- if(start == -1) {
+ if (start == -1) {
return -1;
}
int wordindex = start >>> LONG_LOG2_SIZE;
- if(wordindex >= v.length) {
+ if (wordindex >= v.length) {
return magnitude(v);
}
final int off = Long.SIZE + 1 - (start & LONG_LOG2_MASK);
// Initial word
long cur = ~v[wordindex] & (LONG_ALL_BITS >>> off);
- for(;;) {
- if(cur != 0) {
+ for (;;) {
+ if (cur != 0) {
return (wordindex + 1) * Long.SIZE - 1 - Long.numberOfTrailingZeros(cur);
}
- if(wordindex == 0) {
+ if (wordindex == 0) {
return -1;
}
wordindex--;
@@ -1013,18 +1053,18 @@ public final class BitsUtil {
*/
public static int nextSetBit(long[] v, int start) {
int wordindex = start >>> LONG_LOG2_SIZE;
- if(wordindex >= v.length) {
+ if (wordindex >= v.length) {
return -1;
}
// Initial word
long cur = v[wordindex] & (LONG_ALL_BITS << start);
- for(;;) {
- if(cur != 0) {
+ for (;;) {
+ if (cur != 0) {
return (wordindex * Long.SIZE) + Long.numberOfTrailingZeros(cur);
}
wordindex++;
- if(wordindex == v.length) {
+ if (wordindex == v.length) {
return -1;
}
cur = v[wordindex];
@@ -1040,14 +1080,14 @@ public final class BitsUtil {
*/
public static int nextClearBit(long[] v, int start) {
int wordindex = start >>> LONG_LOG2_SIZE;
- if(wordindex >= v.length) {
+ if (wordindex >= v.length) {
return -1;
}
// Initial word
long cur = ~v[wordindex] & (LONG_ALL_BITS << start);
- for(; wordindex < v.length;) {
- if(cur != 0) {
+ for (; wordindex < v.length;) {
+ if (cur != 0) {
return (wordindex * Long.SIZE) + Long.numberOfTrailingZeros(cur);
}
wordindex++;
@@ -1063,10 +1103,7 @@ public final class BitsUtil {
* @return position of highest bit set, or 0.
*/
public static int magnitude(long[] v) {
- final int l = numberOfLeadingZerosSigned(v);
- if(l < 0) {
- return 0;
- }
+ final int l = numberOfLeadingZeros(v);
return capacity(v) - l;
}
@@ -1077,11 +1114,65 @@ public final class BitsUtil {
* @return position of highest bit set, or 0.
*/
public static int magnitude(long v) {
- final int l = numberOfLeadingZerosSigned(v);
- if(l < 0) {
- return 0;
+ int log = 0, t;
+ if ((v & 0xffffffff00000000L) != 0) {
+ t = (int) (v >>>= 32);
+ log = 32;
+ } else {
+ t = (int) v;
+ }
+ if ((t & 0xffff0000) != 0) {
+ t >>>= 16;
+ log += 16;
+ }
+ if (t >= 256) {
+ t >>>= 8;
+ log += 8;
+ }
+ if (t >= 16) {
+ t >>>= 4;
+ log += 4;
+ }
+ if (t >= 4) {
+ t >>>= 2;
+ log += 2;
+ }
+ if (t >= 2) {
+ t >>>= 1;
+ log += 1;
+ }
+ return log + t;
+ }
+
+ /**
+ * The magnitude is the position of the highest bit set
+ *
+ * @param v Vector v
+ * @return position of highest bit set, or 0.
+ */
+ public static int magnitude(int v) {
+ int log = 0;
+ if ((v & 0xffff0000) != 0) {
+ v >>>= 16;
+ log = 16;
+ }
+ if (v >= 256) {
+ v >>>= 8;
+ log += 8;
}
- return Long.SIZE - l;
+ if (v >= 16) {
+ v >>>= 4;
+ log += 4;
+ }
+ if (v >= 4) {
+ v >>>= 2;
+ log += 2;
+ }
+ if (v >= 2) {
+ v >>>= 1;
+ log += 1;
+ }
+ return log + v;
}
/**
@@ -1103,33 +1194,30 @@ public final class BitsUtil {
*/
public static int compare(long[] x, long[] y) {
int p = Math.min(x.length, y.length) - 1;
- for(int i = x.length - 1; i > p; i--) {
- if(x[i] != 0) {
+ for (int i = x.length - 1; i > p; i--) {
+ if (x[i] != 0) {
return +1;
}
}
- for(int i = y.length - 1; i > p; i--) {
- if(y[i] != 0) {
+ for (int i = y.length - 1; i > p; i--) {
+ if (y[i] != 0) {
return -1;
}
}
- for(; p >= 0; p--) {
+ for (; p >= 0; p--) {
final long xp = x[p];
final long yp = y[p];
- if(xp != yp) {
- if(xp < 0) {
- if(yp < 0) {
+ if (xp != yp) {
+ if (xp < 0) {
+ if (yp < 0) {
return (yp < xp) ? -1 : ((yp == xp) ? 0 : 1);
- }
- else {
+ } else {
return +1;
}
- }
- else {
- if(yp < 0) {
+ } else {
+ if (yp < 0) {
return -1;
- }
- else {
+ } else {
return (xp < yp) ? -1 : ((xp == yp) ? 0 : 1);
}
}
@@ -1137,4 +1225,4 @@ public final class BitsUtil {
}
return 0;
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/ClassGenericsUtil.java b/src/de/lmu/ifi/dbs/elki/utilities/ClassGenericsUtil.java
index bdd9924f..90b5ac62 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/ClassGenericsUtil.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/ClassGenericsUtil.java
@@ -65,7 +65,7 @@ public final class ClassGenericsUtil {
/**
* Static logger to use.
*/
- private static final Logging logger = Logging.getLogger(ClassGenericsUtil.class);
+ private static final Logging LOG = Logging.getLogger(ClassGenericsUtil.class);
/**
* Class loader.
@@ -78,6 +78,13 @@ public final class ClassGenericsUtil {
public static final String FACTORY_METHOD_NAME = "parameterize";
/**
+ * Fake Constructor. Use static methods.
+ */
+ private ClassGenericsUtil() {
+ // Do not instantiate
+ }
+
+ /**
* <p>
* Returns a new instance of the given type for the specified className.
* </p>
@@ -99,22 +106,17 @@ public final class ClassGenericsUtil {
try {
try {
instance = type.cast(loader.loadClass(className).newInstance());
- }
- catch(ClassNotFoundException e) {
+ } catch (ClassNotFoundException e) {
// try package of type
instance = type.cast(loader.loadClass(type.getPackage().getName() + "." + className).newInstance());
}
- }
- catch(InstantiationException e) {
+ } catch (InstantiationException e) {
throw new UnableToComplyException(e);
- }
- catch(IllegalAccessException e) {
+ } catch (IllegalAccessException e) {
throw new UnableToComplyException(e);
- }
- catch(ClassNotFoundException e) {
+ } catch (ClassNotFoundException e) {
throw new UnableToComplyException(e);
- }
- catch(ClassCastException e) {
+ } catch (ClassCastException e) {
throw new UnableToComplyException(e);
}
return instance;
@@ -150,22 +152,17 @@ public final class ClassGenericsUtil {
try {
try {
instance = ((Class<T>) type).cast(loader.loadClass(className).newInstance());
- }
- catch(ClassNotFoundException e) {
+ } catch (ClassNotFoundException e) {
// try package of type
instance = ((Class<T>) type).cast(loader.loadClass(type.getPackage().getName() + "." + className).newInstance());
}
- }
- catch(InstantiationException e) {
+ } catch (InstantiationException e) {
throw new UnableToComplyException(e);
- }
- catch(IllegalAccessException e) {
+ } catch (IllegalAccessException e) {
throw new UnableToComplyException(e);
- }
- catch(ClassNotFoundException e) {
+ } catch (ClassNotFoundException e) {
throw new UnableToComplyException(e);
- }
- catch(ClassCastException e) {
+ } catch (ClassCastException e) {
throw new UnableToComplyException(e);
}
return instance;
@@ -184,15 +181,15 @@ public final class ClassGenericsUtil {
* doesn't fit the constraints.
* @throws Exception On other errors such as security exceptions
*/
- public static <C> Method getParameterizationFactoryMethod(Class<C> c, Class<?> ret) throws NoSuchMethodException, Exception {
+ public static <C> Method getParameterizationFactoryMethod(Class<C> c, Class<?> ret) throws NoSuchMethodException {
Method m = c.getMethod(FACTORY_METHOD_NAME, Parameterization.class);
- if(m == null) {
+ if (m == null) {
throw new NoSuchMethodException("No parameterization method found.");
}
- if(!ret.isAssignableFrom(m.getReturnType())) {
+ if (!ret.isAssignableFrom(m.getReturnType())) {
throw new NoSuchMethodException("Return type doesn't match: " + m.getReturnType().getName() + ", expected: " + ret.getName());
}
- if(!java.lang.reflect.Modifier.isStatic(m.getModifiers())) {
+ if (!java.lang.reflect.Modifier.isStatic(m.getModifiers())) {
throw new NoSuchMethodException("Factory method is not static.");
}
return m;
@@ -205,13 +202,12 @@ public final class ClassGenericsUtil {
* @return Parameterizer or null.
*/
public static Parameterizer getParameterizer(Class<?> c) {
- for(Class<?> inner : c.getDeclaredClasses()) {
- if(Parameterizer.class.isAssignableFrom(inner)) {
+ for (Class<?> inner : c.getDeclaredClasses()) {
+ if (Parameterizer.class.isAssignableFrom(inner)) {
try {
return inner.asSubclass(Parameterizer.class).newInstance();
- }
- catch(Exception e) {
- logger.warning("Non-usable Parameterizer in class: " + c.getName());
+ } catch (Exception e) {
+ LOG.warning("Non-usable Parameterizer in class: " + c.getName());
}
}
}
@@ -233,14 +229,14 @@ public final class ClassGenericsUtil {
* @throws Exception when other instantiation errors occurred
*/
public static <C> C tryInstantiate(Class<C> r, Class<?> c, Parameterization config) throws InvocationTargetException, NoSuchMethodException, Exception {
- if(c == null) {
+ if (c == null) {
// TODO: better class? AbortException maybe?
throw new UnsupportedOperationException("Trying to instantiate 'null' class!");
}
// Try a V3 parameterization class
Parameterizer par = getParameterizer(c);
// TODO: API good?
- if(par != null && par instanceof AbstractParameterizer) {
+ if (par instanceof AbstractParameterizer) {
final Object instance = ((AbstractParameterizer) par).make(config);
return r.cast(instance);
}
@@ -249,8 +245,7 @@ public final class ClassGenericsUtil {
final Method factory = getParameterizationFactoryMethod(c, r);
final Object instance = factory.invoke(null, config);
return r.cast(instance);
- }
- catch(NoSuchMethodException e) {
+ } catch (NoSuchMethodException e) {
// continue.
}
// Try a regular "parameterization" constructor
@@ -258,8 +253,7 @@ public final class ClassGenericsUtil {
final Constructor<?> constructor = c.getConstructor(Parameterization.class);
final Object instance = constructor.newInstance(config);
return r.cast(instance);
- }
- catch(NoSuchMethodException e) {
+ } catch (NoSuchMethodException e) {
// continue
}
// Try a default constructor.
@@ -282,8 +276,7 @@ public final class ClassGenericsUtil {
public static <C> C parameterizeOrAbort(Class<?> c, Parameterization config) {
try {
return tryInstantiate((Class<C>) c, c, config);
- }
- catch(Exception e) {
+ } catch (Exception e) {
throw new AbortException("Instantiation failed", e);
}
}
@@ -328,10 +321,10 @@ public final class ClassGenericsUtil {
* @param len array size
* @return new array of ArrayLists
*/
- @SuppressWarnings("unchecked")
+ @SuppressWarnings({ "unchecked", "rawtypes" })
public static <T> ArrayList<T>[] newArrayOfEmptyArrayList(int len) {
- ArrayList<T>[] result = new ArrayList[len];
- for(int i = 0; i < len; i++) {
+ ArrayList[] result = new ArrayList[len];
+ for (int i = 0; i < len; i++) {
result[i] = new ArrayList<T>();
}
return result;
@@ -347,10 +340,10 @@ public final class ClassGenericsUtil {
* @param len array size
* @return new array of HashSets
*/
- @SuppressWarnings("unchecked")
+ @SuppressWarnings({ "unchecked", "rawtypes" })
public static <T> HashSet<T>[] newArrayOfEmptyHashSet(int len) {
- HashSet<T>[] result = new HashSet[len];
- for(int i = 0; i < len; i++) {
+ HashSet[] result = new HashSet[len];
+ for (int i = 0; i < len; i++) {
result[i] = new HashSet<T>();
}
return result;
@@ -405,8 +398,8 @@ public final class ClassGenericsUtil {
*/
@SuppressWarnings("unchecked")
public static <BASE, FROM extends BASE, TO extends BASE> Class<TO> uglyCrossCast(Class<FROM> cls, Class<BASE> base) {
- if(!base.isAssignableFrom(cls)) {
- if(cls == null) {
+ if (!base.isAssignableFrom(cls)) {
+ if (cls == null) {
throw new ClassCastException("Attempted to use 'null' as class.");
}
throw new ClassCastException(cls.getName() + " is not a superclass of " + base);
@@ -431,8 +424,7 @@ public final class ClassGenericsUtil {
public static <B, T extends B> T castWithGenericsOrNull(Class<B> base, Object obj) {
try {
return (T) base.cast(obj);
- }
- catch(ClassCastException e) {
+ } catch (ClassCastException e) {
return null;
}
}
@@ -453,8 +445,7 @@ public final class ClassGenericsUtil {
try {
Object n = obj.getClass().getConstructor().newInstance();
return (T) n;
- }
- catch(NullPointerException e) {
+ } catch (NullPointerException e) {
throw new IllegalArgumentException("Null pointer exception in newInstance()", e);
}
}
@@ -482,7 +473,7 @@ public final class ClassGenericsUtil {
*/
@SuppressWarnings("unchecked")
public static <T> T[] newArray(Class<? extends T> k, int size) {
- if(k.isPrimitive()) {
+ if (k.isPrimitive()) {
throw new IllegalArgumentException("Argument cannot be primitive: " + k);
}
Object a = java.lang.reflect.Array.newInstance(k, size);
@@ -514,17 +505,13 @@ public final class ClassGenericsUtil {
C copy = newInstance(coll);
copy.addAll(coll);
return copy;
- }
- catch(InstantiationException e) {
+ } catch (InstantiationException e) {
throw new RuntimeException(e);
- }
- catch(IllegalAccessException e) {
+ } catch (IllegalAccessException e) {
throw new RuntimeException(e);
- }
- catch(InvocationTargetException e) {
+ } catch (InvocationTargetException e) {
throw new RuntimeException(e);
- }
- catch(NoSuchMethodException e) {
+ } catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
@@ -538,15 +525,15 @@ public final class ClassGenericsUtil {
* @return new array containing the collection elements
*/
public static <T> T[] collectionToArray(Collection<T> c, T[] a) {
- if(a.length < c.size()) {
+ if (a.length < c.size()) {
a = newArray(a, c.size());
}
int i = 0;
- for(T x : c) {
+ for (T x : c) {
a[i] = x;
i++;
}
- if(i < a.length) {
+ if (i < a.length) {
a[i] = null;
}
return a;
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/ConstantObject.java b/src/de/lmu/ifi/dbs/elki/utilities/ConstantObject.java
deleted file mode 100644
index 9c24c500..00000000
--- a/src/de/lmu/ifi/dbs/elki/utilities/ConstantObject.java
+++ /dev/null
@@ -1,164 +0,0 @@
-package de.lmu.ifi.dbs.elki.utilities;
-
-/*
- This file is part of ELKI:
- Environment for Developing KDD-Applications Supported by Index-Structures
-
- Copyright (C) 2012
- Ludwig-Maximilians-Universität München
- Lehr- und Forschungseinheit für Datenbanksysteme
- ELKI Development Team
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * ConstantObject provides a parent class for constant objects, that are
- * immutable and unique by class and name.
- *
- * @author Arthur Zimek
- * @param <D> Class self reference for {@link Comparable} restriction
- */
-public abstract class ConstantObject<D extends ConstantObject<D>> implements Comparable<D> {
- /**
- * Index of constant objects.
- */
- private static final Map<Class<?>, Map<String, ConstantObject<?>>> CONSTANT_OBJECTS_INDEX = new HashMap<Class<?>, Map<String, ConstantObject<?>>>();
-
- /**
- * Holds the value of the property's name.
- */
- private final String name;
-
- /**
- * The cached hash code of this object.
- */
- private final int hashCode;
-
- /**
- * Provides a ConstantObject of the given name.
- *
- * @param name name of the ConstantObject
- */
- protected ConstantObject(final String name) {
- if(name == null) {
- throw new IllegalArgumentException("The name of a constant object must not be null.");
- }
- Map<String, ConstantObject<?>> index = CONSTANT_OBJECTS_INDEX.get(this.getClass());
- if(index == null) {
- index = new HashMap<String, ConstantObject<?>>();
- CONSTANT_OBJECTS_INDEX.put(this.getClass(), index);
- }
- if(index.containsKey(name)) {
- throw new IllegalArgumentException("A constant object of type \"" + this.getClass().getName() + "\" with value \"" + name + "\" is existing already.");
- }
- this.name = new String(name);
- index.put(name, this);
- this.hashCode = name.hashCode();
- }
-
- /**
- * Returns the name of the ConstantObject.
- *
- * @return the name of the ConstantObject
- */
- public String getName() {
- return new String(name);
- }
-
- /**
- * Provides a ConstantObject of specified class and name if it exists.
- *
- * @param <D> Type for compile time type checking
- * @param type the type of the desired ConstantObject
- * @param name the name of the desired ConstantObject
- * @return the ConstantObject of designated type and name if it exists, null
- * otherwise
- */
- @SuppressWarnings("unchecked")
- public static final <D extends ConstantObject<D>> D lookup(final Class<D> type, final String name) {
- Map<String, ConstantObject<?>> typeindex = CONSTANT_OBJECTS_INDEX.get(type);
- if (typeindex == null) {
- return null;
- }
- return (D) typeindex.get(name);
- }
-
- /**
- * Method for use by the serialization mechanism to ensure identity of
- * ConstantObjects.
- *
- * @return the ConstantObject that already exists in the virtual machine
- * rather than a new instance as created by the serialization
- * mechanism
- */
- @SuppressWarnings("unchecked")
- protected Object readResolve(){
- Object result = lookup(getClass(), getName());
- if(result == null) {
- throw new NullPointerException("No constant object of type \"" + getClass().getName() + "\" found for name \"" + getName() + "\".");
- }
- return result;
- }
-
- /**
- * @see Object#equals(Object)
- */
- @SuppressWarnings("unchecked")
- @Override
- public boolean equals(Object o) {
- if(this == o) {
- return true;
- }
- if(o == null || getClass() != o.getClass()) {
- return false;
- }
-
- final D that = (D) o;
-
- if(hashCode != that.hashCode()) {
- return false;
- }
- if (name == null) {
- return (that.getName() == null);
- }
- return name.equals(that.getName());
- }
-
- /**
- * @see Object#hashCode()
- */
- @Override
- public int hashCode() {
- return hashCode;
- }
-
- /**
- * Two constant objects are generally compared by their name. The result
- * reflects the lexicographical order of the names by
- * {@link String#compareTo(String) this.getName().compareTo(o.getName()}.
- * @param o Object to compare to.
- * @return comparison result
- *
- * @see java.lang.Comparable#compareTo(java.lang.Object)
- */
- @Override
- public int compareTo(D o) {
- return this.getName().compareTo(o.getName());
- }
-
-}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/DatabaseUtil.java b/src/de/lmu/ifi/dbs/elki/utilities/DatabaseUtil.java
index 365e3847..31e2431d 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/DatabaseUtil.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/DatabaseUtil.java
@@ -26,7 +26,6 @@ package de.lmu.ifi.dbs.elki.utilities;
import java.util.AbstractCollection;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.BitSet;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
@@ -40,7 +39,6 @@ import de.lmu.ifi.dbs.elki.data.LabelList;
import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.data.type.NoSupportedDataTypeException;
import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
-import de.lmu.ifi.dbs.elki.data.type.VectorFieldTypeInformation;
import de.lmu.ifi.dbs.elki.database.Database;
import de.lmu.ifi.dbs.elki.database.ids.ArrayDBIDs;
import de.lmu.ifi.dbs.elki.database.ids.ArrayModifiableDBIDs;
@@ -50,10 +48,7 @@ import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.relation.ConvertToStringView;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
-import de.lmu.ifi.dbs.elki.math.linearalgebra.Centroid;
-import de.lmu.ifi.dbs.elki.math.linearalgebra.CovarianceMatrix;
-import de.lmu.ifi.dbs.elki.math.linearalgebra.Matrix;
-import de.lmu.ifi.dbs.elki.math.linearalgebra.ProjectedCentroid;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.utilities.pairs.Pair;
/**
@@ -63,147 +58,26 @@ import de.lmu.ifi.dbs.elki.utilities.pairs.Pair;
* @author Erich Schubert
*
* @apiviz.landmark
- * @apiviz.uses Centroid
- * @apiviz.uses ProjectedCentroid
- * @apiviz.uses CovarianceMatrix
*/
public final class DatabaseUtil {
/**
- * Get the dimensionality of a database
- *
- * @param relation relation
- * @return Vector field type information
- */
- public static <V extends FeatureVector<?, ?>> VectorFieldTypeInformation<V> assumeVectorField(Relation<V> relation) {
- try {
- return ((VectorFieldTypeInformation<V>) relation.getDataTypeInformation());
- }
- catch(Exception e) {
- throw new UnsupportedOperationException("Expected a vector field, got type information: " + relation.getDataTypeInformation().toString());
- }
- }
-
- /**
- * Get the dimensionality of a database
- *
- * @param relation relation
- * @return Database dimensionality
- */
- public static int dimensionality(Relation<? extends FeatureVector<?, ?>> relation) {
- try {
- return ((VectorFieldTypeInformation<? extends FeatureVector<?, ?>>) relation.getDataTypeInformation()).dimensionality();
- }
- catch(Exception e) {
- return -1;
- }
- }
-
- /**
- * Returns the centroid as a NumberVector object of the specified database.
- * The objects must be instance of <code>NumberVector</code>.
- *
- * @param <V> Vector type
- * @param relation the Relation storing the objects
- * @return the centroid of the specified objects stored in the given database
- * @throws IllegalArgumentException if the database is empty
- */
- public static <V extends NumberVector<? extends V, ?>> V centroid(Relation<? extends V> relation) {
- return Centroid.make(relation).toVector(relation);
- }
-
- /**
- * Returns the centroid as a NumberVector object of the specified objects
- * stored in the given database. The objects belonging to the specified ids
- * must be instance of <code>NumberVector</code>.
- *
- * @param <V> Vector type
- * @param relation the relation
- * @param ids the ids of the objects
- * @return the centroid of the specified objects stored in the given database
- * @throws IllegalArgumentException if the id list is empty
- */
- public static <V extends NumberVector<? extends V, ?>> V centroid(Relation<? extends V> relation, DBIDs ids) {
- return Centroid.make(relation, ids).toVector(relation);
- }
-
- /**
- * Returns the centroid w.r.t. the dimensions specified by the given BitSet as
- * a NumberVector object of the specified objects stored in the given
- * database. The objects belonging to the specified IDs must be instance of
- * <code>NumberVector</code>.
- *
- * @param <V> Vector type
- * @param relation the database storing the objects
- * @param ids the identifiable objects
- * @param dimensions the BitSet representing the dimensions to be considered
- * @return the centroid of the specified objects stored in the given database
- * w.r.t. the specified subspace
- * @throws IllegalArgumentException if the id list is empty
- */
- public static <V extends NumberVector<? extends V, ?>> V centroid(Relation<? extends V> relation, DBIDs ids, BitSet dimensions) {
- return ProjectedCentroid.make(dimensions, relation, ids).toVector(relation);
- }
-
- /**
- * Determines the covariance matrix of the objects stored in the given
- * database.
- *
- * @param <V> Vector type
- * @param database the database storing the objects
- * @param ids the ids of the objects
- * @return the covariance matrix of the specified objects
- */
- public static <V extends NumberVector<? extends V, ?>> Matrix covarianceMatrix(Relation<? extends V> database, DBIDs ids) {
- return CovarianceMatrix.make(database, ids).destroyToNaiveMatrix();
- }
-
- /**
- * Determines the d x d covariance matrix of the given n x d data matrix.
- *
- * @param data the database storing the objects
- * @return the covariance matrix of the given data matrix.
+ * Fake constructor: Do not instantiate!
*/
- public static Matrix covarianceMatrix(Matrix data) {
- return CovarianceMatrix.make(data).destroyToNaiveMatrix();
+ private DatabaseUtil() {
+ // Do not instantiate!
}
-
+
/**
- * Determines the variances in each dimension of all objects stored in the
- * given database.
+ * Get the dimensionality of a relation.
*
- * @param database the database storing the objects
- * @return the variances in each dimension of all objects stored in the given
- * database
- */
- public static <V extends NumberVector<? extends V, ?>> double[] variances(Relation<V> database) {
- NumberVector<?, ?> centroid = centroid(database);
- double[] variances = new double[centroid.getDimensionality()];
-
- for(int d = 1; d <= centroid.getDimensionality(); d++) {
- double mu = centroid.doubleValue(d);
-
- for(DBIDIter it = database.iterDBIDs(); it.valid(); it.advance()) {
- NumberVector<?, ?> o = database.get(it);
- double diff = o.doubleValue(d) - mu;
- variances[d - 1] += diff * diff;
- }
-
- variances[d - 1] /= database.size();
- }
- return variances;
- }
-
- /**
- * Determines the variances in each dimension of the specified objects stored
- * in the given database. Returns
- * <code>variances(database, centroid(database, ids), ids)</code>
+ * @param relation Relation
+ * @return Dimensionality
*
- * @param database the database storing the objects
- * @param ids the ids of the objects
- * @return the variances in each dimension of the specified objects
+ * @deprecated Use {@link RelationUtil#dimensionality(Relation)} instead!
*/
- public static <V extends NumberVector<? extends V, ?>> double[] variances(Relation<V> database, DBIDs ids) {
- return variances(database, centroid(database, ids), ids);
+ @Deprecated
+ public static <V extends FeatureVector<?>> int dimensionality(Relation<V> relation) {
+ return RelationUtil.dimensionality(relation);
}
/**
@@ -215,19 +89,16 @@ public final class DatabaseUtil {
* @param centroid the centroid or reference vector of the ids
* @return the variances in each dimension of the specified objects
*/
- public static double[] variances(Relation<? extends NumberVector<?, ?>> database, NumberVector<?, ?> centroid, DBIDs ids) {
+ public static double[] variances(Relation<? extends NumberVector<?>> database, NumberVector<?> centroid, DBIDs ids) {
+ final int size = ids.size();
double[] variances = new double[centroid.getDimensionality()];
- for(int d = 1; d <= centroid.getDimensionality(); d++) {
- double mu = centroid.doubleValue(d);
-
- for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
- NumberVector<?, ?> o = database.get(iter);
- double diff = o.doubleValue(d) - mu;
- variances[d - 1] += diff * diff;
+ for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
+ NumberVector<?> o = database.get(iter);
+ for (int d = 0; d < centroid.getDimensionality(); d++) {
+ final double diff = o.doubleValue(d) - centroid.doubleValue(d);
+ variances[d ] += diff * diff / size;
}
-
- variances[d - 1] /= ids.size();
}
return variances;
}
@@ -237,26 +108,26 @@ public final class DatabaseUtil {
* stored in the given database.
*
* @param <NV> vector type
- * @param database the database storing the objects
+ * @param relation the database storing the objects
* @return Minimum and Maximum vector for the hyperrectangle
*/
- public static <NV extends NumberVector<NV, ?>> Pair<NV, NV> computeMinMax(Relation<NV> database) {
- int dim = dimensionality(database);
+ public static <NV extends NumberVector<?>> Pair<NV, NV> computeMinMax(Relation<NV> relation) {
+ int dim = RelationUtil.dimensionality(relation);
double[] mins = new double[dim];
double[] maxs = new double[dim];
- for(int i = 0; i < dim; i++) {
+ for (int i = 0; i < dim; i++) {
mins[i] = Double.MAX_VALUE;
maxs[i] = -Double.MAX_VALUE;
}
- for(DBIDIter iditer = database.iterDBIDs(); iditer.valid(); iditer.advance()) {
- final NV o = database.get(iditer);
- for(int d = 0; d < dim; d++) {
- final double v = o.doubleValue(d + 1);
+ for (DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
+ final NV o = relation.get(iditer);
+ for (int d = 0; d < dim; d++) {
+ final double v = o.doubleValue(d);
mins[d] = Math.min(mins[d], v);
maxs[d] = Math.max(maxs[d], v);
}
}
- NV factory = assumeVectorField(database).getFactory();
+ NumberVector.Factory<NV, ?> factory = RelationUtil.getNumberVectorFactory(relation);
NV min = factory.newNumberVector(mins);
NV max = factory.newNumberVector(maxs);
return new Pair<NV, NV>(min, max);
@@ -272,20 +143,19 @@ public final class DatabaseUtil {
* @param numberOfSamples Number of samples to draw
* @return Median value
*/
- public static <V extends NumberVector<?, ?>> double quickMedian(Relation<V> relation, ArrayDBIDs ids, int dimension, int numberOfSamples) {
+ public static <V extends NumberVector<?>> double quickMedian(Relation<V> relation, ArrayDBIDs ids, int dimension, int numberOfSamples) {
final int everyNthItem = (int) Math.max(1, Math.floor(ids.size() / (double) numberOfSamples));
final double[] vals = new double[numberOfSamples];
- for(int i = 0; i < numberOfSamples; i++) {
+ for (int i = 0; i < numberOfSamples; i++) {
final DBID id = ids.get(i * everyNthItem);
vals[i] = relation.get(id).doubleValue(dimension);
}
Arrays.sort(vals);
- if(vals.length % 2 == 1) {
- return vals[((vals.length + 1) / 2) - 1];
- }
- else {
- final double v1 = vals[vals.length / 2];
- final double v2 = vals[(vals.length / 2) - 1];
+ if (vals.length % 2 == 1) {
+ return vals[((vals.length + 1) >> 1) - 1];
+ } else {
+ final double v1 = vals[vals.length >> 1];
+ final double v2 = vals[(vals.length >> 1) - 1];
return (v1 + v2) / 2.0;
}
}
@@ -298,7 +168,7 @@ public final class DatabaseUtil {
* @param dimension Dimensionality
* @return Median value
*/
- public static <V extends NumberVector<?, ?>> double exactMedian(Relation<V> relation, DBIDs ids, int dimension) {
+ public static <V extends NumberVector<?>> double exactMedian(Relation<V> relation, DBIDs ids, int dimension) {
final double[] vals = new double[ids.size()];
int i = 0;
for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
@@ -306,12 +176,11 @@ public final class DatabaseUtil {
i++;
}
Arrays.sort(vals);
- if(vals.length % 2 == 1) {
- return vals[((vals.length + 1) / 2) - 1];
- }
- else {
- final double v1 = vals[vals.length / 2];
- final double v2 = vals[(vals.length / 2) - 1];
+ if (vals.length % 2 == 1) {
+ return vals[((vals.length + 1) >> 1) - 1];
+ } else {
+ final double v1 = vals[vals.length >> 1];
+ final double v2 = vals[(vals.length >> 1) - 1];
return (v1 + v2) / 2.0;
}
}
@@ -325,29 +194,26 @@ public final class DatabaseUtil {
public static Relation<String> guessLabelRepresentation(Database database) throws NoSupportedDataTypeException {
try {
Relation<? extends ClassLabel> classrep = database.getRelation(TypeUtil.CLASSLABEL);
- if(classrep != null) {
+ if (classrep != null) {
return new ConvertToStringView(classrep);
}
- }
- catch(NoSupportedDataTypeException e) {
+ } catch (NoSupportedDataTypeException e) {
// retry.
}
try {
Relation<? extends LabelList> labelsrep = database.getRelation(TypeUtil.LABELLIST);
- if(labelsrep != null) {
+ if (labelsrep != null) {
return new ConvertToStringView(labelsrep);
}
- }
- catch(NoSupportedDataTypeException e) {
+ } catch (NoSupportedDataTypeException e) {
// retry.
}
try {
Relation<String> stringrep = database.getRelation(TypeUtil.STRING);
- if(stringrep != null) {
+ if (stringrep != null) {
return stringrep;
}
- }
- catch(NoSupportedDataTypeException e) {
+ } catch (NoSupportedDataTypeException e) {
// retry.
}
throw new NoSupportedDataTypeException("No label-like representation was found.");
@@ -362,29 +228,26 @@ public final class DatabaseUtil {
public static Relation<String> guessObjectLabelRepresentation(Database database) throws NoSupportedDataTypeException {
try {
Relation<? extends LabelList> labelsrep = database.getRelation(TypeUtil.LABELLIST);
- if(labelsrep != null) {
+ if (labelsrep != null) {
return new ConvertToStringView(labelsrep);
}
- }
- catch(NoSupportedDataTypeException e) {
+ } catch (NoSupportedDataTypeException e) {
// retry.
}
try {
Relation<String> stringrep = database.getRelation(TypeUtil.STRING);
- if(stringrep != null) {
+ if (stringrep != null) {
return stringrep;
}
- }
- catch(NoSupportedDataTypeException e) {
+ } catch (NoSupportedDataTypeException e) {
// retry.
}
try {
Relation<? extends ClassLabel> classrep = database.getRelation(TypeUtil.CLASSLABEL);
- if(classrep != null) {
+ if (classrep != null) {
return new ConvertToStringView(classrep);
}
- }
- catch(NoSupportedDataTypeException e) {
+ } catch (NoSupportedDataTypeException e) {
// retry.
}
throw new NoSupportedDataTypeException("No label-like representation was found.");
@@ -399,7 +262,7 @@ public final class DatabaseUtil {
*/
public static SortedSet<ClassLabel> getClassLabels(Relation<? extends ClassLabel> database) {
SortedSet<ClassLabel> labels = new TreeSet<ClassLabel>();
- for(DBIDIter it = database.iterDBIDs(); it.valid(); it.advance()) {
+ for (DBIDIter it = database.iterDBIDs(); it.valid(); it.advance()) {
labels.add(database.get(it));
}
return labels;
@@ -445,25 +308,25 @@ public final class DatabaseUtil {
List<Class<?>> candidates = new ArrayList<Class<?>>();
DBIDIter iditer = database.iterDBIDs();
// empty database?!
- if(!iditer.valid()) {
+ if (!iditer.valid()) {
return null;
}
// put first class into result set.
candidates.add(database.get(iditer).getClass());
iditer.advance();
// other objects
- for(; iditer.valid(); iditer.advance()) {
+ for (; iditer.valid(); iditer.advance()) {
Class<?> newcls = database.get(iditer).getClass();
// validate all candidates
Iterator<Class<?>> ci = candidates.iterator();
- while(ci.hasNext()) {
+ while (ci.hasNext()) {
Class<?> cand = ci.next();
- if(cand.isAssignableFrom(newcls)) {
+ if (cand.isAssignableFrom(newcls)) {
continue;
}
// TODO: resolve conflicts by finding all superclasses!
// Does this code here work?
- for(Class<?> interf : cand.getInterfaces()) {
+ for (Class<?> interf : cand.getInterfaces()) {
candidates.add(interf);
}
candidates.add(cand.getSuperclass());
@@ -471,13 +334,13 @@ public final class DatabaseUtil {
}
}
// if we have any candidates left ...
- if(candidates != null && candidates.size() > 0) {
+ if (candidates.size() > 0) {
// remove subclasses
Iterator<Class<?>> ci = candidates.iterator();
- while(ci.hasNext()) {
+ while (ci.hasNext()) {
Class<?> cand = ci.next();
- for(Class<?> oc : candidates) {
- if(oc != cand && cand.isAssignableFrom(oc)) {
+ for (Class<?> oc : candidates) {
+ if (!oc.equals(cand) && cand.isAssignableFrom(oc)) {
ci.remove();
break;
}
@@ -486,8 +349,7 @@ public final class DatabaseUtil {
assert (candidates.size() > 0);
try {
return candidates.get(0);
- }
- catch(ClassCastException e) {
+ } catch (ClassCastException e) {
// ignore, and retry with next
}
}
@@ -504,12 +366,12 @@ public final class DatabaseUtil {
*/
public static ArrayModifiableDBIDs getObjectsByLabelMatch(Database database, Pattern name_pattern) {
Relation<String> relation = guessLabelRepresentation(database);
- if(name_pattern == null) {
+ if (name_pattern == null) {
return DBIDUtil.newArray();
}
ArrayModifiableDBIDs ret = DBIDUtil.newArray();
- for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
- if(name_pattern.matcher(relation.get(iditer)).find()) {
+ for (DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
+ if (name_pattern.matcher(relation.get(iditer)).find()) {
ret.add(iditer);
}
}
@@ -525,28 +387,11 @@ public final class DatabaseUtil {
* @return Database
*/
@SuppressWarnings("unchecked")
- public static <V extends NumberVector<?, ?>, T extends NumberVector<?, ?>> Relation<V> relationUglyVectorCast(Relation<T> database) {
+ public static <V extends NumberVector<?>, T extends NumberVector<?>> Relation<V> relationUglyVectorCast(Relation<T> database) {
return (Relation<V>) database;
}
/**
- * Get the column name or produce a generic label "Column XY".
- *
- * @param rel Relation
- * @param col Column
- * @return Label
- */
- public static <V extends FeatureVector<?, ?>> String getColumnLabel(Relation<? extends V> rel, int col) {
- String lbl = assumeVectorField(rel).getLabel(col);
- if(lbl != null) {
- return lbl;
- }
- else {
- return "Column " + col;
- }
- }
-
- /**
* Iterator class that retrieves the given objects from the database.
*
* @author Erich Schubert
@@ -558,7 +403,7 @@ public final class DatabaseUtil {
final DBIDIter iter;
/**
- * The database we use
+ * The database we use.
*/
final Relation<? extends O> database;
@@ -610,7 +455,7 @@ public final class DatabaseUtil {
*/
public static class CollectionFromRelation<O> extends AbstractCollection<O> implements Collection<O> {
/**
- * The database we query
+ * The database we query.
*/
Relation<? extends O> db;
@@ -634,4 +479,4 @@ public final class DatabaseUtil {
return db.size();
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/ELKIServiceLoader.java b/src/de/lmu/ifi/dbs/elki/utilities/ELKIServiceLoader.java
index b9a79d4a..129c6458 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/ELKIServiceLoader.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/ELKIServiceLoader.java
@@ -45,7 +45,7 @@ public class ELKIServiceLoader implements Iterator<Class<?>> {
/**
* Class logger.
*/
- private static final Logging logger = Logging.getLogger(ELKIServiceLoader.class);
+ private static final Logging LOG = Logging.getLogger(ELKIServiceLoader.class);
/**
* Prefix for the ELKI functionality discovery.
@@ -148,7 +148,7 @@ public class ELKIServiceLoader implements Iterator<Class<?>> {
return classes.iterator();
}
- private boolean parseLine(String line, ArrayList<Class<?>> classes, URL nextElement) throws IOException {
+ private boolean parseLine(String line, ArrayList<Class<?>> classes, URL nextElement) {
if(line == null) {
return false;
}
@@ -177,18 +177,17 @@ public class ELKIServiceLoader implements Iterator<Class<?>> {
Class<?> cls = cl.loadClass(line);
// Should not happen. Check anyway.
if(cls == null) {
- assert (cls != null);
return true;
}
if(parent.isAssignableFrom(cls)) {
classes.add(cls);
}
else {
- logger.warning("Class " + line + " does not implement " + parent + " but listed in service file " + nextElement);
+ LOG.warning("Class " + line + " does not implement " + parent + " but listed in service file " + nextElement);
}
}
catch(ClassNotFoundException e) {
- logger.warning("Class not found: " + line + "; listed in service file " + nextElement, e);
+ LOG.warning("Class not found: " + line + "; listed in service file " + nextElement, e);
}
return true;
}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/FileUtil.java b/src/de/lmu/ifi/dbs/elki/utilities/FileUtil.java
index 8c1c7fc7..ef01e084 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/FileUtil.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/FileUtil.java
@@ -38,6 +38,14 @@ import java.util.zip.GZIPInputStream;
*/
public final class FileUtil {
/**
+ * Fake Constructor. Use static methods.
+ *
+ */
+ private FileUtil() {
+ // Do not instantiate.
+ }
+
+ /**
* Returns the lower case extension of the selected file.
*
* If no file is selected, <code>null</code> is returned.
@@ -63,11 +71,11 @@ public final class FileUtil {
if(name == null) {
return null;
}
- int index = name.lastIndexOf(".");
+ int index = name.lastIndexOf('.');
if(index >= name.length() - 1) {
return null;
}
- return name.substring(name.lastIndexOf(".") + 1).toLowerCase();
+ return name.substring(name.lastIndexOf('.') + 1).toLowerCase();
}
/**
@@ -114,8 +122,7 @@ public final class FileUtil {
if (magic[0] == 31 && magic[1] == -117) {
in = new GZIPInputStream(pb);
}
- } else
- if (in.markSupported()) {
+ } else {
in.mark(16);
if (in.read() == 31 && in.read() == -117) {
in.reset();
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/FormatUtil.java b/src/de/lmu/ifi/dbs/elki/utilities/FormatUtil.java
index c61fdb10..e4431fd5 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/FormatUtil.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/FormatUtil.java
@@ -166,12 +166,11 @@ public final class FormatUtil {
* @return a String representing the double array d
*/
public static String format(double[] d, String sep) {
- StringBuffer buffer = new StringBuffer();
- for(int i = 0; i < d.length; i++) {
- if(i > 0) {
+ StringBuilder buffer = new StringBuilder();
+ for (int i = 0; i < d.length; i++) {
+ if (i > 0) {
buffer.append(sep).append(d[i]);
- }
- else {
+ } else {
buffer.append(d[i]);
}
}
@@ -189,12 +188,11 @@ public final class FormatUtil {
* @return a String representing the double array d
*/
public static String format(double[] d, String sep, int digits) {
- StringBuffer buffer = new StringBuffer();
- for(int i = 0; i < d.length; i++) {
- if(i < d.length - 1) {
+ StringBuilder buffer = new StringBuilder();
+ for (int i = 0; i < d.length; i++) {
+ if (i < d.length - 1) {
buffer.append(format(d[i], digits)).append(sep);
- }
- else {
+ } else {
buffer.append(format(d[i], digits));
}
}
@@ -222,12 +220,11 @@ public final class FormatUtil {
* @return a String representing the double array d
*/
public static String format(double[] d, String sep, NumberFormat nf) {
- StringBuffer buffer = new StringBuffer();
- for(int i = 0; i < d.length; i++) {
- if(i < d.length - 1) {
+ StringBuilder buffer = new StringBuilder();
+ for (int i = 0; i < d.length; i++) {
+ if (i < d.length - 1) {
buffer.append(format(d[i], nf)).append(sep);
- }
- else {
+ } else {
buffer.append(format(d[i], nf));
}
}
@@ -263,9 +260,9 @@ public final class FormatUtil {
* @return a String representing the double array d
*/
public static String format(double[][] d) {
- StringBuffer buffer = new StringBuffer();
- for(double[] array : d) {
- buffer.append(format(array, ", ", 2)).append("\n");
+ StringBuilder buffer = new StringBuilder();
+ for (double[] array : d) {
+ buffer.append(format(array, ", ", 2)).append('\n');
}
return buffer.toString();
}
@@ -281,13 +278,12 @@ public final class FormatUtil {
* @return a String representing the double array d
*/
public static String format(double[][] d, String sep1, String sep2, int digits) {
- StringBuffer buffer = new StringBuffer();
+ StringBuilder buffer = new StringBuilder();
- for(int i = 0; i < d.length; i++) {
- if(i < d.length - 1) {
+ for (int i = 0; i < d.length; i++) {
+ if (i < d.length - 1) {
buffer.append(format(d[i], sep2, digits)).append(sep1);
- }
- else {
+ } else {
buffer.append(format(d[i], sep2, digits));
}
}
@@ -306,13 +302,12 @@ public final class FormatUtil {
* @return a String representing the Double array f
*/
public static String format(Double[] f, String sep, int digits) {
- StringBuffer buffer = new StringBuffer();
- for(int i = 0; i < f.length; i++) {
- if(i < f.length - 1) {
- buffer.append(format(f[i], digits)).append(sep);
- }
- else {
- buffer.append(format(f[i], digits));
+ StringBuilder buffer = new StringBuilder();
+ for (int i = 0; i < f.length; i++) {
+ if (i < f.length - 1) {
+ buffer.append(format(f[i].doubleValue(), digits)).append(sep);
+ } else {
+ buffer.append(format(f[i].doubleValue(), digits));
}
}
return buffer.toString();
@@ -339,13 +334,12 @@ public final class FormatUtil {
* @return a String representing the Double array f
*/
public static String format(Double[] f, String sep, NumberFormat nf) {
- StringBuffer buffer = new StringBuffer();
- for(int i = 0; i < f.length; i++) {
- if(i < f.length - 1) {
- buffer.append(format(f[i], nf)).append(sep);
- }
- else {
- buffer.append(format(f[i], nf));
+ StringBuilder buffer = new StringBuilder();
+ for (int i = 0; i < f.length; i++) {
+ if (i < f.length - 1) {
+ buffer.append(format(f[i].doubleValue(), nf)).append(sep);
+ } else {
+ buffer.append(format(f[i].doubleValue(), nf));
}
}
return buffer.toString();
@@ -373,12 +367,11 @@ public final class FormatUtil {
* @return a String representing the float array f
*/
public static String format(float[] f, String sep, int digits) {
- StringBuffer buffer = new StringBuffer();
- for(int i = 0; i < f.length; i++) {
- if(i < f.length - 1) {
+ StringBuilder buffer = new StringBuilder();
+ for (int i = 0; i < f.length; i++) {
+ if (i < f.length - 1) {
buffer.append(format(f[i], digits)).append(sep);
- }
- else {
+ } else {
buffer.append(format(f[i], digits));
}
}
@@ -404,12 +397,11 @@ public final class FormatUtil {
* @return a String representing the int array a
*/
public static String format(int[] a, String sep) {
- StringBuffer buffer = new StringBuffer();
- for(int i = 0; i < a.length; i++) {
- if(i < a.length - 1) {
+ StringBuilder buffer = new StringBuilder();
+ for (int i = 0; i < a.length; i++) {
+ if (i < a.length - 1) {
buffer.append(a[i]).append(sep);
- }
- else {
+ } else {
buffer.append(a[i]);
}
}
@@ -435,12 +427,11 @@ public final class FormatUtil {
* @return a String representing the Integer array a
*/
public static String format(Integer[] a, String sep) {
- StringBuffer buffer = new StringBuffer();
- for(int i = 0; i < a.length; i++) {
- if(i < a.length - 1) {
+ StringBuilder buffer = new StringBuilder();
+ for (int i = 0; i < a.length; i++) {
+ if (i < a.length - 1) {
buffer.append(a[i]).append(sep);
- }
- else {
+ } else {
buffer.append(a[i]);
}
}
@@ -464,12 +455,11 @@ public final class FormatUtil {
* @return a String representing the long array a
*/
public static String format(long[] a) {
- StringBuffer buffer = new StringBuffer();
- for(int i = 0; i < a.length; i++) {
- if(i < a.length - 1) {
+ StringBuilder buffer = new StringBuilder();
+ for (int i = 0; i < a.length; i++) {
+ if (i < a.length - 1) {
buffer.append(a[i]).append(", ");
- }
- else {
+ } else {
buffer.append(a[i]);
}
}
@@ -483,12 +473,11 @@ public final class FormatUtil {
* @return a String representing the byte array a
*/
public static String format(byte[] a) {
- StringBuffer buffer = new StringBuffer();
- for(int i = 0; i < a.length; i++) {
- if(i < a.length - 1) {
+ StringBuilder buffer = new StringBuilder();
+ for (int i = 0; i < a.length; i++) {
+ if (i < a.length - 1) {
buffer.append(a[i]).append(", ");
- }
- else {
+ } else {
buffer.append(a[i]);
}
}
@@ -504,12 +493,11 @@ public final class FormatUtil {
* @return a String representing the boolean array b
*/
public static String format(boolean[] b, final String sep) {
- StringBuffer buffer = new StringBuffer();
- for(int i = 0; i < b.length; i++) {
- if(i < b.length - 1) {
+ StringBuilder buffer = new StringBuilder();
+ for (int i = 0; i < b.length; i++) {
+ if (i < b.length - 1) {
buffer.append(format(b[i])).append(sep);
- }
- else {
+ } else {
buffer.append(format(b[i]));
}
}
@@ -523,7 +511,7 @@ public final class FormatUtil {
* @return a String representing of the boolean b
*/
public static String format(final boolean b) {
- if(b) {
+ if (b) {
return "1";
}
return "0";
@@ -538,17 +526,16 @@ public final class FormatUtil {
* @return a string representation of the specified bit set.
*/
public static String format(BitSet bitSet, int dim, String sep) {
- StringBuffer msg = new StringBuffer();
+ StringBuilder msg = new StringBuilder();
- for(int d = 0; d < dim; d++) {
- if(d > 0) {
+ for (int d = 0; d < dim; d++) {
+ if (d > 0) {
msg.append(sep);
}
- if(bitSet.get(d)) {
- msg.append("1");
- }
- else {
- msg.append("0");
+ if (bitSet.get(d)) {
+ msg.append('1');
+ } else {
+ msg.append('0');
}
}
@@ -576,16 +563,16 @@ public final class FormatUtil {
* @return a String representing the String Collection d
*/
public static String format(Collection<String> d, String sep) {
- if(d.size() == 0) {
+ if (d.size() == 0) {
return "";
}
- if(d.size() == 1) {
+ if (d.size() == 1) {
return d.iterator().next();
}
- StringBuffer buffer = new StringBuffer();
+ StringBuilder buffer = new StringBuilder();
boolean first = true;
- for(String str : d) {
- if(!first) {
+ for (String str : d) {
+ if (!first) {
buffer.append(sep);
}
buffer.append(str);
@@ -611,19 +598,19 @@ public final class FormatUtil {
format.setGroupingUsed(false);
int width = w + 1;
- StringBuffer msg = new StringBuffer();
- msg.append("\n"); // start on new line.
- for(int i = 0; i < m.getRowDimensionality(); i++) {
- for(int j = 0; j < m.getColumnDimensionality(); j++) {
+ StringBuilder msg = new StringBuilder();
+ msg.append('\n'); // start on new line.
+ for (int i = 0; i < m.getRowDimensionality(); i++) {
+ for (int j = 0; j < m.getColumnDimensionality(); j++) {
String s = format.format(m.get(i, j)); // format the number
int padding = Math.max(1, width - s.length()); // At _least_ 1
// space
- for(int k = 0; k < padding; k++) {
+ for (int k = 0; k < padding; k++) {
msg.append(' ');
}
msg.append(s);
}
- msg.append("\n");
+ msg.append('\n');
}
// msg.append("\n");
@@ -647,13 +634,13 @@ public final class FormatUtil {
format.setGroupingUsed(false);
int width = w + 1;
- StringBuffer msg = new StringBuffer();
- msg.append("\n"); // start on new line.
- for(int i = 0; i < v.getDimensionality(); i++) {
+ StringBuilder msg = new StringBuilder();
+ msg.append('\n'); // start on new line.
+ for (int i = 0; i < v.getDimensionality(); i++) {
String s = format.format(v.get(i)); // format the number
int padding = Math.max(1, width - s.length()); // At _least_ 1
// space
- for(int k = 0; k < padding; k++) {
+ for (int k = 0; k < padding; k++) {
msg.append(' ');
}
msg.append(s);
@@ -671,14 +658,14 @@ public final class FormatUtil {
* @return a string representation of this matrix
*/
public static String format(Matrix m, String pre) {
- StringBuffer output = new StringBuffer();
+ StringBuilder output = new StringBuilder();
output.append(pre).append("[\n").append(pre);
- for(int i = 0; i < m.getRowDimensionality(); i++) {
+ for (int i = 0; i < m.getRowDimensionality(); i++) {
output.append(" [");
- for(int j = 0; j < m.getColumnDimensionality(); j++) {
- output.append(" ").append(m.get(i, j));
- if(j < m.getColumnDimensionality() - 1) {
- output.append(",");
+ for (int j = 0; j < m.getColumnDimensionality(); j++) {
+ output.append(' ').append(m.get(i, j));
+ if (j < m.getColumnDimensionality() - 1) {
+ output.append(',');
}
}
output.append(" ]\n").append(pre);
@@ -698,27 +685,27 @@ public final class FormatUtil {
public static String format(Matrix m, NumberFormat nf) {
int[] colMax = new int[m.getColumnDimensionality()];
String[][] entries = new String[m.getRowDimensionality()][m.getColumnDimensionality()];
- for(int i = 0; i < m.getRowDimensionality(); i++) {
- for(int j = 0; j < m.getColumnDimensionality(); j++) {
+ for (int i = 0; i < m.getRowDimensionality(); i++) {
+ for (int j = 0; j < m.getColumnDimensionality(); j++) {
entries[i][j] = nf.format(m.get(i, j));
- if(entries[i][j].length() > colMax[j]) {
+ if (entries[i][j].length() > colMax[j]) {
colMax[j] = entries[i][j].length();
}
}
}
- StringBuffer output = new StringBuffer();
+ StringBuilder output = new StringBuilder();
output.append("[\n");
- for(int i = 0; i < m.getRowDimensionality(); i++) {
+ for (int i = 0; i < m.getRowDimensionality(); i++) {
output.append(" [");
- for(int j = 0; j < m.getColumnDimensionality(); j++) {
- output.append(" ");
+ for (int j = 0; j < m.getColumnDimensionality(); j++) {
+ output.append(' ');
int space = colMax[j] - entries[i][j].length();
- for(int s = 0; s < space; s++) {
- output.append(" ");
+ for (int s = 0; s < space; s++) {
+ output.append(' ');
}
output.append(entries[i][j]);
- if(j < m.getColumnDimensionality() - 1) {
- output.append(",");
+ if (j < m.getColumnDimensionality() - 1) {
+ output.append(',');
}
}
output.append(" ]\n");
@@ -765,12 +752,12 @@ public final class FormatUtil {
* @return a string representation of this matrix
*/
public static String format(Vector v, String pre) {
- StringBuffer output = new StringBuffer();
+ StringBuilder output = new StringBuilder();
output.append(pre).append("[\n").append(pre);
- for(int j = 0; j < v.getDimensionality(); j++) {
- output.append(" ").append(v.get(j));
- if(j < v.getDimensionality() - 1) {
- output.append(",");
+ for (int j = 0; j < v.getDimensionality(); j++) {
+ output.append(' ').append(v.get(j));
+ if (j < v.getDimensionality() - 1) {
+ output.append(',');
}
}
output.append("]\n").append(pre);
@@ -787,33 +774,33 @@ public final class FormatUtil {
* @return a string representation of this matrix
*/
public static String format(Matrix m, String pre, NumberFormat nf) {
- if(nf == null) {
+ if (nf == null) {
return FormatUtil.format(m, pre);
}
int[] colMax = new int[m.getColumnDimensionality()];
String[][] entries = new String[m.getRowDimensionality()][m.getColumnDimensionality()];
- for(int i = 0; i < m.getRowDimensionality(); i++) {
- for(int j = 0; j < m.getColumnDimensionality(); j++) {
+ for (int i = 0; i < m.getRowDimensionality(); i++) {
+ for (int j = 0; j < m.getColumnDimensionality(); j++) {
entries[i][j] = nf.format(m.get(i, j));
- if(entries[i][j].length() > colMax[j]) {
+ if (entries[i][j].length() > colMax[j]) {
colMax[j] = entries[i][j].length();
}
}
}
- StringBuffer output = new StringBuffer();
+ StringBuilder output = new StringBuilder();
output.append(pre).append("[\n").append(pre);
- for(int i = 0; i < m.getRowDimensionality(); i++) {
+ for (int i = 0; i < m.getRowDimensionality(); i++) {
output.append(" [");
- for(int j = 0; j < m.getColumnDimensionality(); j++) {
- output.append(" ");
+ for (int j = 0; j < m.getColumnDimensionality(); j++) {
+ output.append(' ');
int space = colMax[j] - entries[i][j].length();
- for(int s = 0; s < space; s++) {
- output.append(" ");
+ for (int s = 0; s < space; s++) {
+ output.append(' ');
}
output.append(entries[i][j]);
- if(j < m.getColumnDimensionality() - 1) {
- output.append(",");
+ if (j < m.getColumnDimensionality() - 1) {
+ output.append(',');
}
}
output.append(" ]\n").append(pre);
@@ -834,22 +821,22 @@ public final class FormatUtil {
public static int findSplitpoint(String s, int width) {
// the newline (or EOS) is the fallback split position.
int in = s.indexOf(NEWLINE);
- if(in < 0) {
+ if (in < 0) {
in = s.length();
}
// Good enough?
- if(in < width) {
+ if (in < width) {
return in;
}
// otherwise, search for whitespace
int iw = s.lastIndexOf(' ', width);
// good whitespace found?
- if(iw >= 0 && iw < width) {
+ if (iw >= 0 && iw < width) {
return iw;
}
// sub-optimal splitpoint - retry AFTER the given position
int bp = nextPosition(s.indexOf(' ', width), s.indexOf(NEWLINE, width));
- if(bp >= 0) {
+ if (bp >= 0) {
return bp;
}
// even worse - can't split!
@@ -866,10 +853,10 @@ public final class FormatUtil {
* otherwise whichever is positive.
*/
private static int nextPosition(int a, int b) {
- if(a < 0) {
+ if (a < 0) {
return b;
}
- if(b < 0) {
+ if (b < 0) {
return a;
}
return Math.min(a, b);
@@ -887,19 +874,19 @@ public final class FormatUtil {
List<String> chunks = new ArrayList<String>();
String tmp = s;
- while(tmp.length() > 0) {
+ while (tmp.length() > 0) {
int index = findSplitpoint(tmp, width);
// store first part
chunks.add(tmp.substring(0, index));
// skip whitespace at beginning of line
- while(index < tmp.length() && tmp.charAt(index) == ' ') {
+ while (index < tmp.length() && tmp.charAt(index) == ' ') {
index += 1;
}
// remove a newline
- if(index < tmp.length() && tmp.regionMatches(index, NEWLINE, 0, NEWLINE.length())) {
+ if (index < tmp.length() && tmp.regionMatches(index, NEWLINE, 0, NEWLINE.length())) {
index += NEWLINE.length();
}
- if(index >= tmp.length()) {
+ if (index >= tmp.length()) {
break;
}
tmp = tmp.substring(index);
@@ -915,11 +902,11 @@ public final class FormatUtil {
* @return a string with the specified number of blanks
*/
public static String whitespace(int n) {
- if(n < WHITESPACE_BUFFER.length()) {
+ if (n < WHITESPACE_BUFFER.length()) {
return WHITESPACE_BUFFER.substring(0, n);
}
char[] buf = new char[n];
- for(int i = 0; i < n; i++) {
+ for (int i = 0; i < n; i++) {
buf[i] = WHITESPACE_BUFFER.charAt(0);
}
return new String(buf);
@@ -933,7 +920,7 @@ public final class FormatUtil {
* @return padded string of at least length len (and o otherwise)
*/
public static String pad(String o, int len) {
- if(o.length() >= len) {
+ if (o.length() >= len) {
return o;
}
return o + whitespace(len - o.length());
@@ -947,7 +934,7 @@ public final class FormatUtil {
* @return padded string of at least length len (and o otherwise)
*/
public static String padRightAligned(String o, int len) {
- if(o.length() >= len) {
+ if (o.length() >= len) {
return o;
}
return whitespace(len - o.length()) + o;
@@ -960,14 +947,14 @@ public final class FormatUtil {
* @return Terminal width
*/
public static int getConsoleWidth() {
- int termwidth = 78;
+ final int default_termwidth = 78;
try {
- termwidth = Integer.parseInt(System.getenv("COLUMNS")) - 1;
+ return Integer.parseInt(System.getenv("COLUMNS")) - 1;
+ } catch (SecurityException e) {
+ return default_termwidth;
+ } catch (NumberFormatException e) {
+ return default_termwidth;
}
- catch(Exception e) {
- // Do nothing, stick with default of 78.
- }
- return termwidth;
}
/**
@@ -980,21 +967,22 @@ public final class FormatUtil {
final StringBuilder sb = new StringBuilder();
final Formatter fmt = new Formatter(sb);
- for(int i = TIME_UNIT_SIZES.length - 1; i >= 0; --i) {
+ for (int i = TIME_UNIT_SIZES.length - 1; i >= 0; --i) {
// We do not include ms if we are in the order of minutes.
- if(i == 0 && sb.length() > 4) {
+ if (i == 0 && sb.length() > 4) {
continue;
}
// Separator
- if(sb.length() > 0) {
+ if (sb.length() > 0) {
sb.append(sep);
}
final long acValue = time / TIME_UNIT_SIZES[i];
time = time % TIME_UNIT_SIZES[i];
- if(!(acValue == 0 && sb.length() == 0)) {
- fmt.format("%0" + TIME_UNIT_DIGITS[i] + "d%s", acValue, TIME_UNIT_NAMES[i]);
+ if (!(acValue == 0 && sb.length() == 0)) {
+ fmt.format("%0" + TIME_UNIT_DIGITS[i] + "d%s", Long.valueOf(acValue), TIME_UNIT_NAMES[i]);
}
}
+ fmt.close();
return sb.toString();
}
@@ -1007,15 +995,14 @@ public final class FormatUtil {
* @return a String representing the string array d
*/
public static String format(String[] d, String sep) {
- StringBuffer buffer = new StringBuffer();
- for(int i = 0; i < d.length; i++) {
- if(i > 0) {
+ StringBuilder buffer = new StringBuilder();
+ for (int i = 0; i < d.length; i++) {
+ if (i > 0) {
buffer.append(sep).append(d[i]);
- }
- else {
+ } else {
buffer.append(d[i]);
}
}
return buffer.toString();
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/InspectionUtil.java b/src/de/lmu/ifi/dbs/elki/utilities/InspectionUtil.java
index f7bccc1a..aedc4e7c 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/InspectionUtil.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/InspectionUtil.java
@@ -55,7 +55,7 @@ public class InspectionUtil {
/**
* Class logger
*/
- private static final Logging logger = Logging.getLogger(InspectionUtil.class);
+ private static final Logging LOG = Logging.getLogger(InspectionUtil.class);
/**
* Default package ignores.
@@ -147,7 +147,7 @@ public class InspectionUtil {
}
if(!InspectionUtil.NONSTATIC_CLASSPATH) {
if(list.size() == 0) {
- logger.warning("No implementations for " + c.getName() + " were found using index files.");
+ LOG.warning("No implementations for " + c.getName() + " were found using index files.");
}
}
else {
@@ -203,7 +203,7 @@ public class InspectionUtil {
while(cps.hasMoreElements()) {
URL u = cps.nextElement();
// Scan file sources only.
- if(u.getProtocol() == "file") {
+ if("file".equals(u.getProtocol())) {
Iterator<String> it = new DirClassIterator(new File(u.getFile()), DEFAULT_IGNORES);
while(it.hasNext()) {
String classname = it.next();
@@ -236,7 +236,7 @@ public class InspectionUtil {
}
}
catch(IOException e) {
- logger.exception(e);
+ LOG.exception(e);
}
Collections.sort(res, new ClassSorter());
return res;
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/RandomFactory.java b/src/de/lmu/ifi/dbs/elki/utilities/RandomFactory.java
new file mode 100644
index 00000000..65a92d33
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/RandomFactory.java
@@ -0,0 +1,89 @@
+package de.lmu.ifi.dbs.elki.utilities;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.Random;
+
+/**
+ * RandomFactory is responsible for creating {@link Random} generator objects.
+ * It does not provide individual random numbers, but will create a random
+ * generator; either using a fixed seed or random seeded (default).
+ *
+ * TODO: allow global fixing of seed, to make whole experiments reproducible,
+ * without having to set every single seed.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.has Random
+ */
+public class RandomFactory {
+ /**
+ * Global default factory
+ */
+ public static RandomFactory DEFAULT = new RandomFactory(null);
+
+ /**
+ * Seed
+ */
+ private Long seed = null;
+
+ /**
+ * Factory method: Get a random factory for the given seed.
+ *
+ * @param seed Seed
+ * @return Instance
+ */
+ public static RandomFactory get(Long seed) {
+ if(seed == null) {
+ return DEFAULT;
+ }
+ else {
+ return new RandomFactory(seed);
+ }
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param seed Random seed
+ */
+ public RandomFactory(Long seed) {
+ super();
+ this.seed = seed;
+ }
+
+ /**
+ * Get a random generator.
+ *
+ * @return Random generator
+ */
+ public Random getRandom() {
+ if(seed != null) {
+ return new Random(seed.longValue());
+ }
+ else {
+ return new Random();
+ }
+ }
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/Util.java b/src/de/lmu/ifi/dbs/elki/utilities/Util.java
index 5faf69cc..42144958 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/Util.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/Util.java
@@ -23,20 +23,11 @@ package de.lmu.ifi.dbs.elki.utilities;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import gnu.trove.map.hash.TIntDoubleHashMap;
-import java.io.PrintStream;
-import java.util.ArrayList;
import java.util.BitSet;
import java.util.Comparator;
-import java.util.Iterator;
-import java.util.List;
import java.util.Random;
-import java.util.StringTokenizer;
-import de.lmu.ifi.dbs.elki.data.DoubleVector;
-import de.lmu.ifi.dbs.elki.data.SparseNumberVector;
-import de.lmu.ifi.dbs.elki.utilities.datastructures.arraylike.ArrayLikeUtil;
/**
* This class collects various static helper methods.
@@ -44,59 +35,14 @@ import de.lmu.ifi.dbs.elki.utilities.datastructures.arraylike.ArrayLikeUtil;
* For helper methods related to special application fields see other utilities
* classes.
*
- *
* @see de.lmu.ifi.dbs.elki.utilities
*/
public final class Util {
/**
- * Returns a new double array containing the same objects as are contained in
- * the given array.
- *
- * @param array an array to copy
- * @return the copied array
- */
- public static double[] copy(double[] array) {
- double[] copy = new double[array.length];
- System.arraycopy(array, 0, copy, 0, array.length);
- return copy;
- }
-
- /**
- * Returns a new <code>Double</code> array initialized to the values
- * represented by the specified <code>String</code> and separated by comma, as
- * performed by the <code>valueOf</code> method of class <code>Double</code>.
- *
- * @param s the string to be parsed.
- * @return a new <code>Double</code> array represented by s
- */
- public static double[] parseDoubles(String s) {
- List<Double> result = new ArrayList<Double>();
- StringTokenizer tokenizer = new StringTokenizer(s, ",");
- while(tokenizer.hasMoreTokens()) {
- String d = tokenizer.nextToken();
- result.add(Double.parseDouble(d));
- }
- return ArrayLikeUtil.toPrimitiveDoubleArray(result);
- }
-
- /**
- * Prints the given list to the specified PrintStream. The list entries are
- * separated by the specified separator. The last entry is not followed by a
- * separator. Thus, if a newline is used as separator, it might make sense to
- * print a newline to the PrintStream after calling this method.
- *
- * @param <O> object class
- * @param list the list to be printed
- * @param separator the separator to separate entries of the list
- * @param out the target PrintStream
+ * Fake constructor: do not instantiate.
*/
- public static <O> void print(List<O> list, String separator, PrintStream out) {
- for(Iterator<O> iter = list.iterator(); iter.hasNext();) {
- out.print(iter.next());
- if(iter.hasNext()) {
- out.print(separator);
- }
- }
+ private Util() {
+ // Do not instantiate.
}
/**
@@ -115,14 +61,13 @@ public final class Util {
assert (cardinality >= 0) : "Cannot set a negative number of bits!";
assert (cardinality < capacity) : "Cannot set " + cardinality + " of " + capacity + " bits!";
BitSet bitset = new BitSet(capacity);
- if(cardinality < capacity >>> 1) {
- while(bitset.cardinality() < cardinality) {
+ if (cardinality < capacity >>> 1) {
+ while (bitset.cardinality() < cardinality) {
bitset.set(random.nextInt(capacity));
}
- }
- else {
+ } else {
bitset.flip(0, capacity);
- while(bitset.cardinality() > cardinality) {
+ while (bitset.cardinality() > cardinality) {
bitset.clear(random.nextInt(capacity));
}
}
@@ -130,81 +75,25 @@ public final class Util {
}
/**
- * Provides a new DoubleVector as a projection on the specified attributes.
- *
- * If the given DoubleVector has already an ID not <code>null</code>, the same
- * ID is set in the returned new DoubleVector. Nevertheless, the returned
- * DoubleVector is not backed by the given DoubleVector, i.e., any changes
- * affecting <code>v</code> after calling this method will not affect the
- * newly returned DoubleVector.
- *
- * @param v a DoubleVector to project
- * @param selectedAttributes the attributes selected for projection
- * @return a new DoubleVector as a projection on the specified attributes
- * @throws IllegalArgumentException if the given selected attributes specify
- * an attribute as selected which is out of range for the given
- * DoubleVector.
- * @see DoubleVector#doubleValue(int)
- */
- public static DoubleVector project(DoubleVector v, BitSet selectedAttributes) {
- double[] newAttributes = new double[selectedAttributes.cardinality()];
- int i = 0;
- for(int d = selectedAttributes.nextSetBit(0); d >= 0; d = selectedAttributes.nextSetBit(d + 1)) {
- newAttributes[i] = v.doubleValue(d + 1);
- i++;
- }
- DoubleVector projectedVector = new DoubleVector(newAttributes);
- return projectedVector;
- }
-
- /**
- * Provides a new SparseFloatVector as a projection on the specified
- * attributes.
- *
- * If the given SparseFloatVector has already an ID not <code>null</code>, the
- * same ID is set in the returned new SparseFloatVector. Nevertheless, the
- * returned SparseFloatVector is not backed by the given SparseFloatVector,
- * i.e., any changes affecting <code>v</code> after calling this method will
- * not affect the newly returned SparseFloatVector.
- *
- * @param v a SparseFloatVector to project
- * @param selectedAttributes the attributes selected for projection
- * @return a new SparseFloatVector as a projection on the specified attributes
- * @throws IllegalArgumentException if the given selected attributes specify
- * an attribute as selected which is out of range for the given
- * SparseFloatVector.
- */
- public static <V extends SparseNumberVector<V, ?>> V project(V v, BitSet selectedAttributes) {
- TIntDoubleHashMap values = new TIntDoubleHashMap(selectedAttributes.cardinality(), 1);
- for(int d = selectedAttributes.nextSetBit(0); d >= 0; d = selectedAttributes.nextSetBit(d + 1)) {
- if(v.doubleValue(d + 1) != 0.0) {
- values.put(d, v.doubleValue(d + 1));
- }
- }
- V projectedVector = v.newNumberVector(values, selectedAttributes.cardinality());
- return projectedVector;
- }
-
- /**
* Mix multiple hashcodes into one.
*
* @param hash Hashcodes to mix
* @return Mixed hash code
*/
- public static final int mixHashCodes(int... hash) {
+ public static int mixHashCodes(int... hash) {
final long prime = 2654435761L;
- if(hash.length == 0) {
+ if (hash.length == 0) {
return 0;
}
long result = hash[0];
- for(int i = 1; i < hash.length; i++) {
+ for (int i = 1; i < hash.length; i++) {
result = result * prime + hash[i];
}
return (int) result;
}
-
+
/**
- * Static instance
+ * Static instance.
*/
private static final Comparator<?> FORWARD = new ForwardComparator();
@@ -227,10 +116,11 @@ public final class Util {
* Compare two objects, forward. See
* {@link java.util.Collections#reverseOrder()} for a reverse comparator.
*
+ * @param <T> Object type
* @return Forward comparator
*/
@SuppressWarnings("unchecked")
- public static final <T> Comparator<T> forwardOrder() {
+ public static <T> Comparator<T> forwardOrder() {
return (Comparator<T>) FORWARD;
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/QuickSelect.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/QuickSelect.java
index a191dde2..bfa7950d 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/QuickSelect.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/QuickSelect.java
@@ -5,6 +5,8 @@ import java.util.List;
import de.lmu.ifi.dbs.elki.database.ids.ArrayModifiableDBIDs;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDArrayIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
/*
This file is part of ELKI:
@@ -90,14 +92,14 @@ public class QuickSelect {
final int length = end - begin;
assert (length > 0);
// Integer division is "floor" since we are non-negative.
- final int left = begin + (length - 1) / 2;
+ final int left = begin + ((length - 1) >> 1);
quickSelect(data, begin, end, left);
if(length % 2 == 1) {
return data[left];
}
else {
quickSelect(data, begin, end, left + 1);
- return data[left] + (data[left + 1] - data[left]) / 2;
+ return data[left] + .5 * (data[left + 1] - data[left]);
}
}
@@ -155,59 +157,64 @@ public class QuickSelect {
* @param rank rank position we are interested in (starting at 0)
*/
public static void quickSelect(double[] data, int start, int end, int rank) {
- // Optimization for small arrays
- // This also ensures a minimum size below
- if(start + SMALL > end) {
- insertionSort(data, start, end);
- return;
- }
+ while(true) {
+ // Optimization for small arrays
+ // This also ensures a minimum size below
+ if(start + SMALL > end) {
+ insertionSort(data, start, end);
+ return;
+ }
- // Pick pivot from three candidates: start, middle, end
- // Since we compare them, we can also just "bubble sort" them.
- final int middle = (start + end) / 2;
- if(data[start] > data[middle]) {
- swap(data, start, middle);
- }
- if(data[start] > data[end - 1]) {
- swap(data, start, end - 1);
- }
- if(data[middle] > data[end - 1]) {
- swap(data, middle, end - 1);
- }
- // TODO: use more candidates for larger arrays?
+ // Pick pivot from three candidates: start, middle, end
+ // Since we compare them, we can also just "bubble sort" them.
+ final int middle = (start + end) >> 1;
+ if(data[start] > data[middle]) {
+ swap(data, start, middle);
+ }
+ if(data[start] > data[end - 1]) {
+ swap(data, start, end - 1);
+ }
+ if(data[middle] > data[end - 1]) {
+ swap(data, middle, end - 1);
+ }
+ // TODO: use more candidates for larger arrays?
+
+ final double pivot = data[middle];
+ // Move middle element out of the way, just before end
+ // (Since we already know that "end" is bigger)
+ swap(data, middle, end - 2);
+
+ // Begin partitioning
+ int i = start + 1, j = end - 3;
+ // This is classic quicksort stuff
+ while(true) {
+ while(data[i] <= pivot && i <= j) {
+ i++;
+ }
+ while(data[j] >= pivot && j >= i) {
+ j--;
+ }
+ if(i >= j) {
+ break;
+ }
+ swap(data, i, j);
+ }
- final double pivot = data[middle];
- // Move middle element out of the way, just before end
- // (Since we already know that "end" is bigger)
- swap(data, middle, end - 2);
+ // Move pivot (former middle element) back into the appropriate place
+ swap(data, i, end - 2);
- // Begin partitioning
- int i = start + 1, j = end - 3;
- // This is classic quicksort stuff
- while(true) {
- while(data[i] <= pivot && i <= j) {
- i++;
+ // In contrast to quicksort, we only need to recurse into the half we are
+ // interested in. Instead of recursion we now use iteration.
+ if(rank < i) {
+ end = i;
}
- while(data[j] >= pivot && j >= i) {
- j--;
+ else if(rank > i) {
+ start = i + 1;
}
- if(i >= j) {
+ else {
break;
}
- swap(data, i, j);
- }
-
- // Move pivot (former middle element) back into the appropriate place
- swap(data, i, end - 2);
-
- // In contrast to quicksort, we only need to recurse into the half we are
- // interested in.
- if(rank < i) {
- quickSelect(data, start, i, rank);
- }
- else if(rank > i) {
- quickSelect(data, i + 1, end, rank);
- }
+ } // Loop until rank==i
}
/**
@@ -283,7 +290,7 @@ public class QuickSelect {
final int length = end - begin;
assert (length > 0);
// Integer division is "floor" since we are non-negative.
- final int left = begin + (length - 1) / 2;
+ final int left = begin + ((length - 1) >> 1);
quickSelect(data, begin, end, left);
return data[left];
}
@@ -338,59 +345,64 @@ public class QuickSelect {
* @param rank rank position we are interested in (starting at 0)
*/
public static <T extends Comparable<? super T>> void quickSelect(T[] data, int start, int end, int rank) {
- // Optimization for small arrays
- // This also ensures a minimum size below
- if(start + SMALL > end) {
- insertionSort(data, start, end);
- return;
- }
+ while(true) {
+ // Optimization for small arrays
+ // This also ensures a minimum size below
+ if(start + SMALL > end) {
+ insertionSort(data, start, end);
+ return;
+ }
- // Pick pivot from three candidates: start, middle, end
- // Since we compare them, we can also just "bubble sort" them.
- final int middle = (start + end) / 2;
- if(data[start].compareTo(data[middle]) > 0) {
- swap(data, start, middle);
- }
- if(data[start].compareTo(data[end - 1]) > 0) {
- swap(data, start, end - 1);
- }
- if(data[middle].compareTo(data[end - 1]) > 0) {
- swap(data, middle, end - 1);
- }
- // TODO: use more candidates for larger arrays?
+ // Pick pivot from three candidates: start, middle, end
+ // Since we compare them, we can also just "bubble sort" them.
+ final int middle = (start + end) >> 1;
+ if(data[start].compareTo(data[middle]) > 0) {
+ swap(data, start, middle);
+ }
+ if(data[start].compareTo(data[end - 1]) > 0) {
+ swap(data, start, end - 1);
+ }
+ if(data[middle].compareTo(data[end - 1]) > 0) {
+ swap(data, middle, end - 1);
+ }
+ // TODO: use more candidates for larger arrays?
+
+ final T pivot = data[middle];
+ // Move middle element out of the way, just before end
+ // (Since we already know that "end" is bigger)
+ swap(data, middle, end - 2);
+
+ // Begin partitioning
+ int i = start + 1, j = end - 3;
+ // This is classic quicksort stuff
+ while(true) {
+ while(data[i].compareTo(pivot) <= 0 && i <= j) {
+ i++;
+ }
+ while(data[j].compareTo(pivot) >= 0 && j >= i) {
+ j--;
+ }
+ if(i >= j) {
+ break;
+ }
+ swap(data, i, j);
+ }
- final T pivot = data[middle];
- // Move middle element out of the way, just before end
- // (Since we already know that "end" is bigger)
- swap(data, middle, end - 2);
+ // Move pivot (former middle element) back into the appropriate place
+ swap(data, i, end - 2);
- // Begin partitioning
- int i = start + 1, j = end - 3;
- // This is classic quicksort stuff
- while(true) {
- while(data[i].compareTo(pivot) <= 0 && i <= j) {
- i++;
+ // In contrast to quicksort, we only need to recurse into the half we are
+ // interested in. Instead of recursion we now use iteration.
+ if(rank < i) {
+ end = i;
}
- while(data[j].compareTo(pivot) >= 0 && j >= i) {
- j--;
+ else if(rank > i) {
+ start = i + 1;
}
- if(i >= j) {
+ else {
break;
}
- swap(data, i, j);
- }
-
- // Move pivot (former middle element) back into the appropriate place
- swap(data, i, end - 2);
-
- // In contrast to quicksort, we only need to recurse into the half we are
- // interested in.
- if(rank < i) {
- quickSelect(data, start, i, rank);
- }
- else if(rank > i) {
- quickSelect(data, i + 1, end, rank);
- }
+ } // Loop until rank==i
}
/**
@@ -469,7 +481,7 @@ public class QuickSelect {
final int length = end - begin;
assert (length > 0);
// Integer division is "floor" since we are non-negative.
- final int left = begin + (length - 1) / 2;
+ final int left = begin + ((length - 1) >> 1);
quickSelect(data, begin, end, left);
return data.get(left);
}
@@ -524,59 +536,64 @@ public class QuickSelect {
* @param rank rank position we are interested in (starting at 0)
*/
public static <T extends Comparable<? super T>> void quickSelect(List<? extends T> data, int start, int end, int rank) {
- // Optimization for small arrays
- // This also ensures a minimum size below
- if(start + SMALL > end) {
- insertionSort(data, start, end);
- return;
- }
+ while(true) {
+ // Optimization for small arrays
+ // This also ensures a minimum size below
+ if(start + SMALL > end) {
+ insertionSort(data, start, end);
+ return;
+ }
- // Pick pivot from three candidates: start, middle, end
- // Since we compare them, we can also just "bubble sort" them.
- final int middle = (start + end) / 2;
- if(data.get(start).compareTo(data.get(middle)) > 0) {
- swap(data, start, middle);
- }
- if(data.get(start).compareTo(data.get(end - 1)) > 0) {
- swap(data, start, end - 1);
- }
- if(data.get(middle).compareTo(data.get(end - 1)) > 0) {
- swap(data, middle, end - 1);
- }
- // TODO: use more candidates for larger arrays?
+ // Pick pivot from three candidates: start, middle, end
+ // Since we compare them, we can also just "bubble sort" them.
+ final int middle = (start + end) >> 1;
+ if(data.get(start).compareTo(data.get(middle)) > 0) {
+ swap(data, start, middle);
+ }
+ if(data.get(start).compareTo(data.get(end - 1)) > 0) {
+ swap(data, start, end - 1);
+ }
+ if(data.get(middle).compareTo(data.get(end - 1)) > 0) {
+ swap(data, middle, end - 1);
+ }
+ // TODO: use more candidates for larger arrays?
+
+ final T pivot = data.get(middle);
+ // Move middle element out of the way, just before end
+ // (Since we already know that "end" is bigger)
+ swap(data, middle, end - 2);
+
+ // Begin partitioning
+ int i = start + 1, j = end - 3;
+ // This is classic quicksort stuff
+ while(true) {
+ while(data.get(i).compareTo(pivot) <= 0 && i <= j) {
+ i++;
+ }
+ while(data.get(j).compareTo(pivot) >= 0 && j >= i) {
+ j--;
+ }
+ if(i >= j) {
+ break;
+ }
+ swap(data, i, j);
+ }
- final T pivot = data.get(middle);
- // Move middle element out of the way, just before end
- // (Since we already know that "end" is bigger)
- swap(data, middle, end - 2);
+ // Move pivot (former middle element) back into the appropriate place
+ swap(data, i, end - 2);
- // Begin partitioning
- int i = start + 1, j = end - 3;
- // This is classic quicksort stuff
- while(true) {
- while(data.get(i).compareTo(pivot) <= 0 && i <= j) {
- i++;
+ // In contrast to quicksort, we only need to recurse into the half we are
+ // interested in. Instead of recursion we now use iteration.
+ if(rank < i) {
+ end = i;
}
- while(data.get(j).compareTo(pivot) >= 0 && j >= i) {
- j--;
+ else if(rank > i) {
+ start = i + 1;
}
- if(i >= j) {
+ else {
break;
}
- swap(data, i, j);
- }
-
- // Move pivot (former middle element) back into the appropriate place
- swap(data, i, end - 2);
-
- // In contrast to quicksort, we only need to recurse into the half we are
- // interested in.
- if(rank < i) {
- quickSelect(data, start, i, rank);
- }
- else if(rank > i) {
- quickSelect(data, i + 1, end, rank);
- }
+ } // Loop until rank==i
}
/**
@@ -656,7 +673,7 @@ public class QuickSelect {
final int length = end - begin;
assert (length > 0);
// Integer division is "floor" since we are non-negative.
- final int left = begin + (length - 1) / 2;
+ final int left = begin + ((length - 1) >> 1);
quickSelect(data, comparator, begin, end, left);
return data.get(left);
}
@@ -714,59 +731,64 @@ public class QuickSelect {
* @param rank rank position we are interested in (starting at 0)
*/
public static <T> void quickSelect(List<? extends T> data, Comparator<? super T> comparator, int start, int end, int rank) {
- // Optimization for small arrays
- // This also ensures a minimum size below
- if(start + SMALL > end) {
- insertionSort(data, comparator, start, end);
- return;
- }
+ while(true) {
+ // Optimization for small arrays
+ // This also ensures a minimum size below
+ if(start + SMALL > end) {
+ insertionSort(data, comparator, start, end);
+ return;
+ }
- // Pick pivot from three candidates: start, middle, end
- // Since we compare them, we can also just "bubble sort" them.
- final int middle = (start + end) / 2;
- if(comparator.compare(data.get(start), data.get(middle)) > 0) {
- swap(data, start, middle);
- }
- if(comparator.compare(data.get(start), data.get(end - 1)) > 0) {
- swap(data, start, end - 1);
- }
- if(comparator.compare(data.get(middle), data.get(end - 1)) > 0) {
- swap(data, middle, end - 1);
- }
- // TODO: use more candidates for larger arrays?
+ // Pick pivot from three candidates: start, middle, end
+ // Since we compare them, we can also just "bubble sort" them.
+ final int middle = (start + end) >> 1;
+ if(comparator.compare(data.get(start), data.get(middle)) > 0) {
+ swap(data, start, middle);
+ }
+ if(comparator.compare(data.get(start), data.get(end - 1)) > 0) {
+ swap(data, start, end - 1);
+ }
+ if(comparator.compare(data.get(middle), data.get(end - 1)) > 0) {
+ swap(data, middle, end - 1);
+ }
+ // TODO: use more candidates for larger arrays?
+
+ final T pivot = data.get(middle);
+ // Move middle element out of the way, just before end
+ // (Since we already know that "end" is bigger)
+ swap(data, middle, end - 2);
+
+ // Begin partitioning
+ int i = start + 1, j = end - 3;
+ // This is classic quicksort stuff
+ while(true) {
+ while(comparator.compare(data.get(i), pivot) <= 0 && i <= j) {
+ i++;
+ }
+ while(comparator.compare(data.get(j), pivot) >= 0 && j >= i) {
+ j--;
+ }
+ if(i >= j) {
+ break;
+ }
+ swap(data, i, j);
+ }
- final T pivot = data.get(middle);
- // Move middle element out of the way, just before end
- // (Since we already know that "end" is bigger)
- swap(data, middle, end - 2);
+ // Move pivot (former middle element) back into the appropriate place
+ swap(data, i, end - 2);
- // Begin partitioning
- int i = start + 1, j = end - 3;
- // This is classic quicksort stuff
- while(true) {
- while(comparator.compare(data.get(i), pivot) <= 0 && i <= j) {
- i++;
+ // In contrast to quicksort, we only need to recurse into the half we are
+ // interested in. Instead of recursion we now use iteration.
+ if(rank < i) {
+ end = i;
}
- while(comparator.compare(data.get(j), pivot) >= 0 && j >= i) {
- j--;
+ else if(rank > i) {
+ start = i + 1;
}
- if(i >= j) {
+ else {
break;
}
- swap(data, i, j);
- }
-
- // Move pivot (former middle element) back into the appropriate place
- swap(data, i, end - 2);
-
- // In contrast to quicksort, we only need to recurse into the half we are
- // interested in.
- if(rank < i) {
- quickSelect(data, comparator, start, i, rank);
- }
- else if(rank > i) {
- quickSelect(data, comparator, i + 1, end, rank);
- }
+ } // Loop until rank==i
}
/**
@@ -796,7 +818,7 @@ public class QuickSelect {
* @param rank Rank position that we are interested in (integer!)
* @return Value at the given rank
*/
- public static DBID quickSelect(ArrayModifiableDBIDs data, Comparator<? super DBID> comparator, int rank) {
+ public static DBID quickSelect(ArrayModifiableDBIDs data, Comparator<? super DBIDRef> comparator, int rank) {
quickSelect(data, comparator, 0, data.size(), rank);
return data.get(rank);
}
@@ -810,7 +832,7 @@ public class QuickSelect {
* @param comparator Comparator to use
* @return Median value
*/
- public static DBID median(ArrayModifiableDBIDs data, Comparator<? super DBID> comparator) {
+ public static DBID median(ArrayModifiableDBIDs data, Comparator<? super DBIDRef> comparator) {
return median(data, comparator, 0, data.size());
}
@@ -827,11 +849,11 @@ public class QuickSelect {
* @param end End of valid values (exclusive!)
* @return Median value
*/
- public static DBID median(ArrayModifiableDBIDs data, Comparator<? super DBID> comparator, int begin, int end) {
+ public static DBID median(ArrayModifiableDBIDs data, Comparator<? super DBIDRef> comparator, int begin, int end) {
final int length = end - begin;
assert (length > 0);
// Integer division is "floor" since we are non-negative.
- final int left = begin + (length - 1) / 2;
+ final int left = begin + ((length - 1) >> 1);
quickSelect(data, comparator, begin, end, left);
return data.get(left);
}
@@ -846,7 +868,7 @@ public class QuickSelect {
* @param quant Quantile to compute
* @return Value at quantile
*/
- public static DBID quantile(ArrayModifiableDBIDs data, Comparator<? super DBID> comparator, double quant) {
+ public static DBID quantile(ArrayModifiableDBIDs data, Comparator<? super DBIDRef> comparator, double quant) {
return quantile(data, comparator, 0, data.size(), quant);
}
@@ -864,7 +886,7 @@ public class QuickSelect {
* @param quant Quantile to compute
* @return Value at quantile
*/
- public static DBID quantile(ArrayModifiableDBIDs data, Comparator<? super DBID> comparator, int begin, int end, double quant) {
+ public static DBID quantile(ArrayModifiableDBIDs data, Comparator<? super DBIDRef> comparator, int begin, int end, double quant) {
final int length = end - begin;
assert (length > 0) : "Quantile on empty set?";
// Integer division is "floor" since we are non-negative.
@@ -885,60 +907,70 @@ public class QuickSelect {
* @param end Interval end (inclusive)
* @param rank rank position we are interested in (starting at 0)
*/
- public static void quickSelect(ArrayModifiableDBIDs data, Comparator<? super DBID> comparator, int start, int end, int rank) {
- // Optimization for small arrays
- // This also ensures a minimum size below
- if(start + SMALL > end) {
- insertionSort(data, comparator, start, end);
- return;
- }
+ public static void quickSelect(ArrayModifiableDBIDs data, Comparator<? super DBIDRef> comparator, int start, int end, int rank) {
+ while(true) {
+ // Optimization for small arrays
+ // This also ensures a minimum size below
+ if(start + SMALL > end) {
+ insertionSort(data, comparator, start, end);
+ return;
+ }
- // Pick pivot from three candidates: start, middle, end
- // Since we compare them, we can also just "bubble sort" them.
- final int middle = (start + end) / 2;
- if(comparator.compare(data.get(start), data.get(middle)) > 0) {
- data.swap(start, middle);
- }
- if(comparator.compare(data.get(start), data.get(end - 1)) > 0) {
- data.swap(start, end - 1);
- }
- if(comparator.compare(data.get(middle), data.get(end - 1)) > 0) {
- data.swap(middle, end - 1);
- }
- // TODO: use more candidates for larger arrays?
+ // Pick pivot from three candidates: start, middle, end
+ // Since we compare them, we can also just "bubble sort" them.
+ final int middle = (start + end) >> 1;
+ if(comparator.compare(data.get(start), data.get(middle)) > 0) {
+ data.swap(start, middle);
+ }
+ if(comparator.compare(data.get(start), data.get(end - 1)) > 0) {
+ data.swap(start, end - 1);
+ }
+ if(comparator.compare(data.get(middle), data.get(end - 1)) > 0) {
+ data.swap(middle, end - 1);
+ }
+ // TODO: use more candidates for larger arrays?
+
+ final DBID pivot = data.get(middle);
+ // Move middle element out of the way, just before end
+ // (Since we already know that "end" is bigger)
+ data.swap(middle, end - 2);
+
+ // Begin partitioning
+ int i = start + 1, j = end - 3;
+ DBIDArrayIter refi = data.iter(), refj = data.iter();
+ refi.seek(i);
+ refj.seek(j);
+ // This is classic quicksort stuff
+ while(true) {
+ while(comparator.compare(refi, pivot) <= 0 && i <= j) {
+ i++;
+ refi.advance();
+ }
+ while(comparator.compare(refj, pivot) >= 0 && j >= i) {
+ j--;
+ refj.retract();
+ }
+ if(i >= j) {
+ break;
+ }
+ data.swap(i, j);
+ }
- final DBID pivot = data.get(middle);
- // Move middle element out of the way, just before end
- // (Since we already know that "end" is bigger)
- data.swap(middle, end - 2);
+ // Move pivot (former middle element) back into the appropriate place
+ data.swap(i, end - 2);
- // Begin partitioning
- int i = start + 1, j = end - 3;
- // This is classic quicksort stuff
- while(true) {
- while(comparator.compare(data.get(i), pivot) <= 0 && i <= j) {
- i++;
+ // In contrast to quicksort, we only need to recurse into the half we are
+ // interested in. Instead of recursion we now use iteration.
+ if(rank < i) {
+ end = i;
}
- while(comparator.compare(data.get(j), pivot) >= 0 && j >= i) {
- j--;
+ else if(rank > i) {
+ start = i + 1;
}
- if(i >= j) {
+ else {
break;
}
- data.swap(i, j);
- }
-
- // Move pivot (former middle element) back into the appropriate place
- data.swap(i, end - 2);
-
- // In contrast to quicksort, we only need to recurse into the half we are
- // interested in.
- if(rank < i) {
- quickSelect(data, comparator, start, i, rank);
- }
- else if(rank > i) {
- quickSelect(data, comparator, i + 1, end, rank);
- }
+ } // Loop until rank==i
}
/**
@@ -948,9 +980,15 @@ public class QuickSelect {
* @param start Interval start
* @param end Interval end
*/
- private static void insertionSort(ArrayModifiableDBIDs data, Comparator<? super DBID> comparator, int start, int end) {
+ private static void insertionSort(ArrayModifiableDBIDs data, Comparator<? super DBIDRef> comparator, int start, int end) {
+ DBIDArrayIter iter1 = data.iter(), iter2 = data.iter();
for(int i = start + 1; i < end; i++) {
- for(int j = i; j > start && comparator.compare(data.get(j - 1), data.get(j)) > 0; j--) {
+ iter1.seek(i - 1);
+ iter2.seek(i);
+ for(int j = i; j > start; j--, iter1.retract(), iter2.retract()) {
+ if(comparator.compare(iter1, iter2) > 0) {
+ break;
+ }
data.swap(j, j - 1);
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/arraylike/ArrayLikeUtil.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/arraylike/ArrayLikeUtil.java
index f485e214..a08feb1a 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/arraylike/ArrayLikeUtil.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/arraylike/ArrayLikeUtil.java
@@ -40,29 +40,29 @@ import de.lmu.ifi.dbs.elki.data.NumberVector;
*/
public final class ArrayLikeUtil {
/**
- * Static instance for lists
+ * Static instance for lists.
*/
private static final ListArrayAdapter<Object> LISTADAPTER = new ListArrayAdapter<Object>();
/**
- * Static instance for lists of numbers
+ * Static instance for lists of numbers.
*/
private static final NumberListArrayAdapter<Number> NUMBERLISTADAPTER = new NumberListArrayAdapter<Number>();
/**
- * Static instance
+ * Static instance.
*/
- private final static IdentityArrayAdapter<?> IDENTITYADAPTER = new IdentityArrayAdapter<Object>();
+ private static final IdentityArrayAdapter<?> IDENTITYADAPTER = new IdentityArrayAdapter<Object>();
/**
- * Static instance
+ * Static instance.
*/
- private static final FeatureVectorAdapter<?> FEATUREVECTORADAPTER = new FeatureVectorAdapter<Number>();
+ public static final FeatureVectorAdapter<?> FEATUREVECTORADAPTER = new FeatureVectorAdapter<Number>();
/**
* Use a number vector in the array API.
*/
- private static final NumberVectorAdapter<?> NUMBERVECTORADAPTER = new NumberVectorAdapter<Double>();
+ public static final NumberVectorAdapter<?> NUMBERVECTORADAPTER = new NumberVectorAdapter<Double>();
/**
* Use a double array in the array API.
@@ -83,6 +83,13 @@ public final class ArrayLikeUtil {
* Use ArrayDBIDs as array.
*/
public static final ArrayDBIDsAdapter ARRAYDBIDADAPTER = new ArrayDBIDsAdapter();
+
+ /**
+ * Fake constructor. Do not instantiate!
+ */
+ private ArrayLikeUtil() {
+ // Do not instantiate
+ }
/**
* Cast the static instance.
@@ -124,7 +131,7 @@ public final class ArrayLikeUtil {
* @return Instance
*/
@SuppressWarnings("unchecked")
- public static <F> FeatureVectorAdapter<F> featureVectorAdapter(FeatureVector<?, F> prototype) {
+ public static <F> FeatureVectorAdapter<F> featureVectorAdapter(FeatureVector<F> prototype) {
return (FeatureVectorAdapter<F>) FEATUREVECTORADAPTER;
}
@@ -135,7 +142,7 @@ public final class ArrayLikeUtil {
* @return Instance
*/
@SuppressWarnings("unchecked")
- public static <N extends Number> NumberVectorAdapter<N> numberVectorAdapter(NumberVector<?, N> prototype) {
+ public static <N extends Number> NumberVectorAdapter<N> numberVectorAdapter(NumberVector<N> prototype) {
return (NumberVectorAdapter<N>) NUMBERVECTORADAPTER;
}
@@ -185,7 +192,7 @@ public final class ArrayLikeUtil {
}
/**
- * Convert a numeric array-like to a <code>double[]</code>
+ * Convert a numeric array-like to a <code>double[]</code>.
*
* @param array Array-like
* @param adapter Adapter
@@ -215,12 +222,12 @@ public final class ArrayLikeUtil {
* @param obj Object to convert
* @return primitive double array
*/
- public static <N extends Number> double[] toPrimitiveDoubleArray(NumberVector<?, N> obj) {
+ public static <N extends Number> double[] toPrimitiveDoubleArray(NumberVector<N> obj) {
return toPrimitiveDoubleArray(obj, numberVectorAdapter(obj));
}
/**
- * Convert a numeric array-like to a <code>float[]</code>
+ * Convert a numeric array-like to a <code>float[]</code>.
*
* @param array Array-like
* @param adapter Adapter
@@ -250,7 +257,7 @@ public final class ArrayLikeUtil {
* @param obj Object to convert
* @return primitive float array
*/
- public static <N extends Number> float[] toPrimitiveFloatArray(NumberVector<?, N> obj) {
+ public static <N extends Number> float[] toPrimitiveFloatArray(NumberVector<N> obj) {
return toPrimitiveFloatArray(obj, numberVectorAdapter(obj));
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/arraylike/DoubleArrayAdapter.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/arraylike/DoubleArrayAdapter.java
index 43419103..117f3845 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/arraylike/DoubleArrayAdapter.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/arraylike/DoubleArrayAdapter.java
@@ -45,8 +45,9 @@ class DoubleArrayAdapter implements NumberArrayAdapter<Double, double[]> {
}
@Override
+ @Deprecated
public Double get(double[] array, int off) throws IndexOutOfBoundsException {
- return array[off];
+ return Double.valueOf(array[off]);
}
@Override
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/arraylike/ExtendedArray.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/arraylike/ExtendedArray.java
index a195360b..491c4f95 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/arraylike/ExtendedArray.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/arraylike/ExtendedArray.java
@@ -68,13 +68,13 @@ public class ExtendedArray<T> implements ArrayAdapter<T, ExtendedArray<T>> {
@Override
public int size(ExtendedArray<T> array) {
- assert (array == this);
+ assert (this == array);
return size;
}
@Override
public T get(ExtendedArray<T> array, int off) throws IndexOutOfBoundsException {
- assert (array == this);
+ assert (this == array);
if(off == size - 1) {
return extra;
}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/arraylike/FeatureVectorAdapter.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/arraylike/FeatureVectorAdapter.java
index 19c2ec19..38b662e8 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/arraylike/FeatureVectorAdapter.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/arraylike/FeatureVectorAdapter.java
@@ -34,7 +34,7 @@ import de.lmu.ifi.dbs.elki.data.FeatureVector;
*
* @param <F> Feature type
*/
-public class FeatureVectorAdapter<F> implements ArrayAdapter<F, FeatureVector<?, F>> {
+public class FeatureVectorAdapter<F> implements ArrayAdapter<F, FeatureVector<F>> {
/**
* Constructor.
*
@@ -45,12 +45,12 @@ public class FeatureVectorAdapter<F> implements ArrayAdapter<F, FeatureVector<?,
}
@Override
- public int size(FeatureVector<?, F> array) {
+ public int size(FeatureVector<F> array) {
return array.getDimensionality();
}
@Override
- public F get(FeatureVector<?, F> array, int off) throws IndexOutOfBoundsException {
+ public F get(FeatureVector<F> array, int off) throws IndexOutOfBoundsException {
return array.getValue(off);
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/arraylike/FloatArrayAdapter.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/arraylike/FloatArrayAdapter.java
index 14d42dc5..ae501039 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/arraylike/FloatArrayAdapter.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/arraylike/FloatArrayAdapter.java
@@ -46,8 +46,9 @@ class FloatArrayAdapter implements NumberArrayAdapter<Float, float[]> {
}
@Override
+ @Deprecated
public Float get(float[] array, int off) throws IndexOutOfBoundsException {
- return array[off];
+ return Float.valueOf(array[off]);
}
@Override
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/arraylike/ListArrayAdapter.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/arraylike/ListArrayAdapter.java
index 792fad0b..cba1e706 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/arraylike/ListArrayAdapter.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/arraylike/ListArrayAdapter.java
@@ -1,4 +1,26 @@
package de.lmu.ifi.dbs.elki.utilities.datastructures.arraylike;
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
import java.util.List;
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/arraylike/NumberVectorAdapter.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/arraylike/NumberVectorAdapter.java
index c75acd64..fd1e6636 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/arraylike/NumberVectorAdapter.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/arraylike/NumberVectorAdapter.java
@@ -1,7 +1,5 @@
package de.lmu.ifi.dbs.elki.utilities.datastructures.arraylike;
-import de.lmu.ifi.dbs.elki.data.NumberVector;
-
/*
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
@@ -25,6 +23,8 @@ import de.lmu.ifi.dbs.elki.data.NumberVector;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+import de.lmu.ifi.dbs.elki.data.NumberVector;
+
/**
* Adapter to use a feature vector as an array of features.
*
@@ -34,7 +34,7 @@ import de.lmu.ifi.dbs.elki.data.NumberVector;
*
* @param <N> Number type
*/
-public class NumberVectorAdapter<N extends Number> implements NumberArrayAdapter<N, NumberVector<?, N>> {
+public class NumberVectorAdapter<N extends Number> implements NumberArrayAdapter<N, NumberVector<N>> {
/**
* Constructor.
*
@@ -45,42 +45,43 @@ public class NumberVectorAdapter<N extends Number> implements NumberArrayAdapter
}
@Override
- public int size(NumberVector<?, N> array) {
+ public int size(NumberVector<N> array) {
return array.getDimensionality();
}
@Override
- public N get(NumberVector<?, N> array, int off) throws IndexOutOfBoundsException {
+ @Deprecated
+ public N get(NumberVector<N> array, int off) throws IndexOutOfBoundsException {
return array.getValue(off + 1);
}
@Override
- public double getDouble(NumberVector<?, N> array, int off) throws IndexOutOfBoundsException {
- return array.doubleValue(off + 1);
+ public double getDouble(NumberVector<N> array, int off) throws IndexOutOfBoundsException {
+ return array.doubleValue(off);
}
@Override
- public float getFloat(NumberVector<?, N> array, int off) throws IndexOutOfBoundsException {
- return array.floatValue(off + 1);
+ public float getFloat(NumberVector<N> array, int off) throws IndexOutOfBoundsException {
+ return array.floatValue(off);
}
@Override
- public int getInteger(NumberVector<?, N> array, int off) throws IndexOutOfBoundsException {
- return array.intValue(off + 1);
+ public int getInteger(NumberVector<N> array, int off) throws IndexOutOfBoundsException {
+ return array.intValue(off);
}
@Override
- public short getShort(NumberVector<?, N> array, int off) throws IndexOutOfBoundsException {
- return array.shortValue(off + 1);
+ public short getShort(NumberVector<N> array, int off) throws IndexOutOfBoundsException {
+ return array.shortValue(off);
}
@Override
- public long getLong(NumberVector<?, N> array, int off) throws IndexOutOfBoundsException {
- return array.longValue(off + 1);
+ public long getLong(NumberVector<N> array, int off) throws IndexOutOfBoundsException {
+ return array.longValue(off);
}
@Override
- public byte getByte(NumberVector<?, N> array, int off) throws IndexOutOfBoundsException {
- return array.byteValue(off + 1);
+ public byte getByte(NumberVector<N> array, int off) throws IndexOutOfBoundsException {
+ return array.byteValue(off);
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/arraylike/SubsetArrayAdapter.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/arraylike/SubsetArrayAdapter.java
index b2958358..c607759f 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/arraylike/SubsetArrayAdapter.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/arraylike/SubsetArrayAdapter.java
@@ -23,7 +23,7 @@ package de.lmu.ifi.dbs.elki.utilities.datastructures.arraylike;
*/
/**
- * Subset array adapter (allows reordering and projection)
+ * Subset array adapter (allows reordering and projection).
*
* @author Erich Schubert
*
@@ -32,12 +32,12 @@ package de.lmu.ifi.dbs.elki.utilities.datastructures.arraylike;
*/
public class SubsetArrayAdapter<T, A> implements ArrayAdapter<T, A> {
/**
- * Wrapped adapter
+ * Wrapped adapter.
*/
ArrayAdapter<T, ? super A> wrapped;
/**
- * Offsets to return
+ * Offsets to return.
*/
int[] offs;
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/arraylike/TDoubleListAdapter.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/arraylike/TDoubleListAdapter.java
index 889c64e8..cee393ac 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/arraylike/TDoubleListAdapter.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/arraylike/TDoubleListAdapter.java
@@ -45,8 +45,9 @@ public class TDoubleListAdapter implements NumberArrayAdapter<Double, TDoubleLis
}
@Override
+ @Deprecated
public Double get(TDoubleList array, int off) throws IndexOutOfBoundsException {
- return array.get(off);
+ return Double.valueOf(array.get(off));
}
@Override
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/arrays/IntegerArrayQuickSort.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/arrays/IntegerArrayQuickSort.java
new file mode 100644
index 00000000..a0c93997
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/arrays/IntegerArrayQuickSort.java
@@ -0,0 +1,225 @@
+package de.lmu.ifi.dbs.elki.utilities.datastructures.arrays;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
+
+/**
+ * Class to sort an int array, using a modified quicksort.
+ *
+ * The implementation is closely based on:
+ * <p>
+ * Dual-Pivot Quicksort<br />
+ * Vladimir Yaroslavskiy
+ * </p>
+ *
+ * and differs mostly in that we sort different kinds of arrays,
+ * and allow the use of comparators - useful in particular when
+ * the array references external objects.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.uses IntegerComparator
+ */
+@Reference(authors = "Vladimir Yaroslavskiy", title = "Dual-Pivot Quicksort", booktitle = "http://iaroslavski.narod.ru/quicksort/", url = "http://iaroslavski.narod.ru/quicksort/")
+public class IntegerArrayQuickSort {
+ /**
+ * Threshold for using insertion sort. Value taken from Javas QuickSort,
+ * assuming that it will be similar for our data sets.
+ */
+ private static final int INSERTION_THRESHOLD = 47;
+
+ /**
+ * Sort the full array using the given comparator.
+ *
+ * @param data Data to sort
+ * @param comp Comparator
+ */
+ public static void sort(int[] data, IntegerComparator comp) {
+ sort(data, 0, data.length, comp);
+ }
+
+ /**
+ * Sort the array using the given comparator.
+ *
+ * @param data Data to sort
+ * @param start First index
+ * @param end Last index (exclusive)
+ * @param comp Comparator
+ */
+ public static void sort(int[] data, int start, int end, IntegerComparator comp) {
+ quickSort(data, start, end - 1, comp);
+ }
+
+ /**
+ * Actual recursive sort function.
+ *
+ * @param data Data to sort
+ * @param start First index
+ * @param end Last index (inclusive!)
+ * @param comp Comparator
+ */
+ private static void quickSort(int[] data, final int start, final int end, IntegerComparator comp) {
+ final int len = end - start;
+ if (len < INSERTION_THRESHOLD) {
+ // Classic insertion sort.
+ for (int i = start + 1; i <= end; i++) {
+ for (int j = i; j > start; j--) {
+ if (comp.compare(data[j], data[j - 1]) < 0) {
+ final int tmp = data[j - 1];
+ data[j - 1] = data[j];
+ data[j] = tmp;
+ } else {
+ break;
+ }
+ }
+ }
+ return;
+ }
+
+ // Choose pivots by looking at five candidates.
+ final int seventh = (len >> 3) + (len >> 6) + 1;
+ final int m3 = (start + end) >> 1; // middle
+ final int m2 = m3 - seventh;
+ final int m1 = m2 - seventh;
+ final int m4 = m3 + seventh;
+ final int m5 = m4 + seventh;
+
+ // Explicit (and optimal) sorting network for 5 elements
+ // See Knuth for details.
+ if (comp.compare(data[m1], data[m2]) > 0) {
+ final int tmp = data[m2];
+ data[m2] = data[m1];
+ data[m1] = tmp;
+ }
+ if (comp.compare(data[m1], data[m3]) > 0) {
+ final int tmp = data[m3];
+ data[m3] = data[m1];
+ data[m1] = tmp;
+ }
+ if (comp.compare(data[m2], data[m3]) > 0) {
+ final int tmp = data[m3];
+ data[m3] = data[m2];
+ data[m2] = tmp;
+ }
+ if (comp.compare(data[m4], data[m5]) > 0) {
+ final int tmp = data[m5];
+ data[m5] = data[m4];
+ data[m4] = tmp;
+ }
+ if (comp.compare(data[m1], data[m4]) > 0) {
+ final int tmp = data[m4];
+ data[m4] = data[m1];
+ data[m1] = tmp;
+ }
+ if (comp.compare(data[m3], data[m4]) > 0) {
+ final int tmp = data[m4];
+ data[m4] = data[m3];
+ data[m3] = tmp;
+ }
+ if (comp.compare(data[m2], data[m5]) > 0) {
+ final int tmp = data[m5];
+ data[m5] = data[m2];
+ data[m2] = tmp;
+ }
+ if (comp.compare(data[m2], data[m3]) > 0) {
+ final int tmp = data[m3];
+ data[m3] = data[m2];
+ data[m2] = tmp;
+ }
+ if (comp.compare(data[m4], data[m5]) > 0) {
+ final int tmp = data[m5];
+ data[m5] = data[m4];
+ data[m4] = tmp;
+ }
+
+ // Choose the 2 and 4th as pivots, as we want to get three parts
+ // Copy to variables v1 and v3, replace them with the start and end
+ // Note: do not modify v1 or v3 until we put them back!
+ final int lpivot = data[m2];
+ final int rpivot = data[m4];
+ data[m2] = data[start];
+ data[m4] = data[end];
+
+ // A tie is when the two chosen pivots are the same
+ final boolean tied = comp.compare(lpivot, rpivot) == 0;
+
+ // Insertion points for pivot areas.
+ int left = start + 1;
+ int right = end - 1;
+
+ // Note: we merged the ties and no ties cases.
+ // This likely is marginally slower, but not at a macro level
+ // And you never know with hotspot.
+ for (int k = left; k <= right; k++) {
+ final int tmp = data[k];
+ final int c = comp.compare(tmp, lpivot);
+ if (c == 0) {
+ continue;
+ } else if (c < 0) {
+ // Traditional quicksort
+ data[k] = data[left];
+ data[left] = tmp;
+ left++;
+ } else if (tied || comp.compare(tmp, rpivot) > 0) {
+ // Now look at the right. First skip correct entries there, too
+ while (true) {
+ final int tmp2 = data[right];
+ if (comp.compare(tmp2, rpivot) > 0 && k < right) {
+ right--;
+ } else {
+ break;
+ }
+ }
+ // Now move tmp from k to the right.
+ data[k] = data[right];
+ data[right] = tmp;
+ right--;
+ // Test the element we just inserted: left or center?
+ if (comp.compare(data[k], lpivot) < 0) {
+ final int tmp2 = data[k];
+ data[k] = data[left];
+ data[left] = tmp2;
+ left++;
+ } // else: center. cannot be on right.
+ }
+ }
+ // Put the pivot elements back in.
+ // Remember: we must not modify v1 and v3 above.
+ data[start] = data[left - 1];
+ data[left - 1] = lpivot;
+ data[end] = data[right + 1];
+ data[right + 1] = rpivot;
+ // v1 and v3 are now safe to modify again. Perform recursion:
+ quickSort(data, start, left - 2, comp);
+ // Handle the middle part - if necessary:
+ if (!tied) {
+ // TODO: the original publication had a special tie handling here.
+ // It shouldn't affect correctness, but probably improves situations
+ // with a lot of tied elements.
+ quickSort(data, left, right, comp);
+ }
+ quickSort(data, right + 2, end, comp);
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/arrays/IntegerComparator.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/arrays/IntegerComparator.java
new file mode 100644
index 00000000..51164425
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/arrays/IntegerComparator.java
@@ -0,0 +1,40 @@
+package de.lmu.ifi.dbs.elki.utilities.datastructures.arrays;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * Interface for comparing two integers.
+ *
+ * @author Erich Schubert
+ */
+public interface IntegerComparator {
+ /**
+ * Compare two integers.
+ *
+ * @param x First int
+ * @param y Second int
+ * @return Comparison result
+ */
+ int compare(int x, int y);
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/arrays/package-info.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/arrays/package-info.java
new file mode 100644
index 00000000..3db78032
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/arrays/package-info.java
@@ -0,0 +1,26 @@
+/**
+ * <p>Utilities for arrays: advanced sorting for primitvie arrays.</p>
+ */
+/*
+This file is part of ELKI:
+Environment for Developing KDD-Applications Supported by Index-Structures
+
+Copyright (C) 2012
+Ludwig-Maximilians-Universität München
+Lehr- und Forschungseinheit für Datenbanksysteme
+ELKI Development Team
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+package de.lmu.ifi.dbs.elki.utilities.datastructures.arrays; \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/AbstractHeap.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/AbstractHeap.java
new file mode 100644
index 00000000..b6f098e6
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/AbstractHeap.java
@@ -0,0 +1,103 @@
+package de.lmu.ifi.dbs.elki.utilities.datastructures.heap;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * Abstract base class for heaps.
+ *
+ * @author Erich Schubert
+ */
+public class AbstractHeap {
+ /**
+ * Default initial capacity
+ */
+ public static final int DEFAULT_INITIAL_CAPACITY = 11;
+
+ /**
+ * Current number of objects
+ */
+ public int size = 0;
+
+ /**
+ * Indicate up to where the heap is valid
+ */
+ public int validSize = 0;
+
+ /**
+ * (Structural) modification counter. Used to invalidate iterators.
+ */
+ public int modCount = 0;
+
+ /**
+ * Constructor.
+ */
+ public AbstractHeap() {
+ super();
+ }
+
+ /**
+ * Query the size
+ *
+ * @return Size
+ */
+ public int size() {
+ return this.size;
+ }
+
+ /**
+ * Delete all elements from the heap.
+ */
+ public void clear() {
+ this.size = 0;
+ this.validSize = -1;
+ heapModified();
+ }
+
+ /**
+ * Test whether we need to resize to have the requested capacity.
+ *
+ * @param requiredSize required capacity
+ * @param capacity Current capacity
+ * @return new capacity
+ */
+ protected final int desiredSize(int requiredSize, int capacity) {
+ // Double until 64, then increase by 50% each time.
+ int newCapacity = ((capacity < 64) ? ((capacity + 1) * 2) : ((capacity / 2) * 3));
+ // overflow?
+ if (newCapacity < 0) {
+ throw new OutOfMemoryError();
+ }
+ if (requiredSize > newCapacity) {
+ newCapacity = requiredSize;
+ }
+ return newCapacity;
+ }
+
+ /**
+ * Called at the end of each heap modification.
+ */
+ protected void heapModified() {
+ modCount++;
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/ComparableMaxHeap.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/ComparableMaxHeap.java
new file mode 100644
index 00000000..ab8ef1bb
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/ComparableMaxHeap.java
@@ -0,0 +1,63 @@
+package de.lmu.ifi.dbs.elki.utilities.datastructures.heap;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * Basic in-memory heap structure.
+ *
+ * This heap is built lazily: if you first add many elements, then poll the
+ * heap, it will be bulk-loaded in O(n) instead of iteratively built in O(n log
+ * n). This is implemented via a simple validTo counter.
+ *
+ * @author Erich Schubert
+ */
+public class ComparableMaxHeap<K extends Comparable<K>> extends ObjectHeap<K> {
+ /**
+ * Constructor with default capacity.
+ */
+ public ComparableMaxHeap() {
+ super(DEFAULT_INITIAL_CAPACITY);
+ }
+
+ /**
+ * Constructor with initial capacity.
+ *
+ * @param size initial capacity
+ */
+ public ComparableMaxHeap(int size) {
+ super(size);
+ }
+
+ /**
+ * Compare two objects
+ *
+ * @param o1 First object
+ * @param o2 Second object
+ */
+ @Override
+ @SuppressWarnings("unchecked")
+ protected boolean comp(Object o1, Object o2) {
+ return ((K) o1).compareTo((K) o2) < 0;
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/ComparableMinHeap.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/ComparableMinHeap.java
new file mode 100644
index 00000000..06d2cb32
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/ComparableMinHeap.java
@@ -0,0 +1,63 @@
+package de.lmu.ifi.dbs.elki.utilities.datastructures.heap;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * Basic in-memory heap structure.
+ *
+ * This heap is built lazily: if you first add many elements, then poll the
+ * heap, it will be bulk-loaded in O(n) instead of iteratively built in O(n log
+ * n). This is implemented via a simple validTo counter.
+ *
+ * @author Erich Schubert
+ */
+public class ComparableMinHeap<K extends Comparable<K>> extends ObjectHeap<K> {
+ /**
+ * Constructor with default capacity.
+ */
+ public ComparableMinHeap() {
+ super(DEFAULT_INITIAL_CAPACITY);
+ }
+
+ /**
+ * Constructor with initial capacity.
+ *
+ * @param size initial capacity
+ */
+ public ComparableMinHeap(int size) {
+ super(size);
+ }
+
+ /**
+ * Compare two objects
+ *
+ * @param o1 First object
+ * @param o2 Second object
+ */
+ @Override
+ @SuppressWarnings("unchecked")
+ protected boolean comp(Object o1, Object o2) {
+ return ((K) o1).compareTo((K) o2) > 0;
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/DoubleHeap.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/DoubleHeap.java
new file mode 100644
index 00000000..f9f928bd
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/DoubleHeap.java
@@ -0,0 +1,264 @@
+package de.lmu.ifi.dbs.elki.utilities.datastructures.heap;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.Arrays;
+
+import de.lmu.ifi.dbs.elki.math.MathUtil;
+
+/**
+ * Basic in-memory heap structure.
+ *
+ * This heap is built lazily: if you first add many elements, then poll the
+ * heap, it will be bulk-loaded in O(n) instead of iteratively built in O(n log
+ * n). This is implemented via a simple validTo counter.
+ *
+ * @author Erich Schubert
+ */
+public abstract class DoubleHeap extends AbstractHeap {
+ /**
+ * Heap storage: queue
+ */
+ protected transient double[] queue;
+
+ /**
+ * Constructor with initial capacity.
+ *
+ * @param size initial capacity
+ */
+ public DoubleHeap(int size) {
+ super();
+ this.size = 0;
+ this.queue = new double[size];
+ }
+
+ /**
+ * Add a key-value pair to the heap
+ *
+ * @param key Key
+ */
+ public void add(double key) {
+ // resize when needed
+ if (size + 1 > queue.length) {
+ resize(size + 1);
+ }
+ // final int pos = size;
+ this.queue[size] = key;
+ this.size += 1;
+ heapifyUp(size - 1, key);
+ validSize += 1;
+ heapModified();
+ }
+
+ /**
+ * Add a key-value pair to the heap, except if the new element is larger than
+ * the top, and we are at design size (overflow)
+ *
+ * @param key Key
+ * @param max Maximum size of heap
+ */
+ public void add(double key, int max) {
+ if (size < max) {
+ add(key);
+ } else if (comp(key, peek())) {
+ replaceTopElement(key);
+ }
+ }
+
+ /**
+ * Combined operation that removes the top element, and inserts a new element
+ * instead.
+ *
+ * @param e New element to insert
+ * @return Previous top element of the heap
+ */
+ public double replaceTopElement(double e) {
+ ensureValid();
+ double oldroot = queue[0];
+ heapifyDown(0, e);
+ heapModified();
+ return oldroot;
+ }
+
+ /**
+ * Get the current top key
+ *
+ * @return Top key
+ */
+ public double peek() {
+ if (size == 0) {
+ throw new ArrayIndexOutOfBoundsException("Peek() on an empty heap!");
+ }
+ ensureValid();
+ return queue[0];
+ }
+
+ /**
+ * Remove the first element
+ *
+ * @return Top element
+ */
+ public double poll() {
+ return removeAt(0);
+ }
+
+ /**
+ * Repair the heap
+ */
+ protected void ensureValid() {
+ if (validSize != size) {
+ if (size > 1) {
+ // Parent of first invalid
+ int nextmin = validSize > 0 ? ((validSize - 1) >>> 1) : 0;
+ int curmin = MathUtil.nextAllOnesInt(nextmin); // Next line
+ int nextmax = curmin - 1; // End of valid line
+ int pos = (size - 2) >>> 1; // Parent of last element
+ // System.err.println(validSize+"<="+size+" iter:"+pos+"->"+curmin+", "+nextmin);
+ while (pos >= nextmin) {
+ // System.err.println(validSize+"<="+size+" iter:"+pos+"->"+curmin);
+ while (pos >= curmin) {
+ if (!heapifyDown(pos, queue[pos])) {
+ final int parent = (pos - 1) >>> 1;
+ if (parent < curmin) {
+ nextmin = Math.min(nextmin, parent);
+ nextmax = Math.max(nextmax, parent);
+ }
+ }
+ pos--;
+ }
+ curmin = nextmin;
+ pos = Math.min(pos, nextmax);
+ nextmax = -1;
+ }
+ }
+ validSize = size;
+ }
+ }
+
+ /**
+ * Remove the element at the given position.
+ *
+ * @param pos Element position.
+ * @return Removed element
+ */
+ protected double removeAt(int pos) {
+ if (pos < 0 || pos >= size) {
+ return 0.0;
+ }
+ final double top = queue[0];
+ // Replacement object:
+ final double reinkey = queue[size - 1];
+ // Keep heap in sync
+ if (validSize == size) {
+ size -= 1;
+ validSize -= 1;
+ heapifyDown(pos, reinkey);
+ } else {
+ size -= 1;
+ validSize = Math.min(pos >>> 1, validSize);
+ queue[pos] = reinkey;
+ }
+ heapModified();
+ return top;
+ }
+
+ /**
+ * Execute a "Heapify Upwards" aka "SiftUp". Used in insertions.
+ *
+ * @param pos insertion position
+ * @param curkey Current key
+ */
+ protected void heapifyUp(int pos, double curkey) {
+ while (pos > 0) {
+ final int parent = (pos - 1) >>> 1;
+ double parkey = queue[parent];
+
+ if (comp(curkey, parkey)) { // Compare
+ break;
+ }
+ queue[pos] = parkey;
+ pos = parent;
+ }
+ queue[pos] = curkey;
+ }
+
+ /**
+ * Execute a "Heapify Downwards" aka "SiftDown". Used in deletions.
+ *
+ * @param ipos re-insertion position
+ * @param curkey Current key
+ * @return true when the order was changed
+ */
+ protected boolean heapifyDown(final int ipos, double curkey) {
+ int pos = ipos;
+ final int half = size >>> 1;
+ while (pos < half) {
+ // Get left child (must exist!)
+ int cpos = (pos << 1) + 1;
+ double chikey = queue[cpos];
+ // Test right child, if present
+ final int rchild = cpos + 1;
+ if (rchild < size) {
+ double right = queue[rchild];
+ if (comp(chikey, right)) { // Compare
+ cpos = rchild;
+ chikey = right;
+ }
+ }
+
+ if (comp(chikey, curkey)) { // Compare
+ break;
+ }
+ queue[pos] = chikey;
+ pos = cpos;
+ }
+ queue[pos] = curkey;
+ return (pos == ipos);
+ }
+
+ /**
+ * Test whether we need to resize to have the requested capacity.
+ *
+ * @param requiredSize required capacity
+ */
+ protected final void resize(int requiredSize) {
+ queue = Arrays.copyOf(queue, desiredSize(requiredSize, queue.length));
+ }
+
+ /**
+ * Delete all elements from the heap.
+ */
+ @Override
+ public void clear() {
+ super.clear();
+ for (int i = 0; i < size; i++) {
+ queue[i] = 0.0;
+ }
+ }
+
+ /**
+ * Compare two objects
+ */
+ abstract protected boolean comp(double o1, double o2);
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/DoubleMaxHeap.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/DoubleMaxHeap.java
new file mode 100644
index 00000000..1b7d6037
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/DoubleMaxHeap.java
@@ -0,0 +1,62 @@
+package de.lmu.ifi.dbs.elki.utilities.datastructures.heap;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * Basic in-memory heap structure.
+ *
+ * This heap is built lazily: if you first add many elements, then poll the
+ * heap, it will be bulk-loaded in O(n) instead of iteratively built in O(n log
+ * n). This is implemented via a simple validTo counter.
+ *
+ * @author Erich Schubert
+ */
+public class DoubleMaxHeap extends DoubleHeap {
+ /**
+ * Constructor with default capacity.
+ */
+ public DoubleMaxHeap() {
+ super(DEFAULT_INITIAL_CAPACITY);
+ }
+
+ /**
+ * Constructor with initial capacity.
+ *
+ * @param size initial capacity
+ */
+ public DoubleMaxHeap(int size) {
+ super(size);
+ }
+
+ /**
+ * Compare two objects
+ *
+ * @param o1 First object
+ * @param o2 Second object
+ */
+ @Override
+ protected boolean comp(double o1, double o2) {
+ return o1 < o2;
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/DoubleMinHeap.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/DoubleMinHeap.java
new file mode 100644
index 00000000..2ce05ff9
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/DoubleMinHeap.java
@@ -0,0 +1,62 @@
+package de.lmu.ifi.dbs.elki.utilities.datastructures.heap;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * Basic in-memory heap structure.
+ *
+ * This heap is built lazily: if you first add many elements, then poll the
+ * heap, it will be bulk-loaded in O(n) instead of iteratively built in O(n log
+ * n). This is implemented via a simple validTo counter.
+ *
+ * @author Erich Schubert
+ */
+public class DoubleMinHeap extends DoubleHeap {
+ /**
+ * Constructor with default capacity.
+ */
+ public DoubleMinHeap() {
+ super(DEFAULT_INITIAL_CAPACITY);
+ }
+
+ /**
+ * Constructor with initial capacity.
+ *
+ * @param size initial capacity
+ */
+ public DoubleMinHeap(int size) {
+ super(size);
+ }
+
+ /**
+ * Compare two objects
+ *
+ * @param o1 First object
+ * @param o2 Second object
+ */
+ @Override
+ protected boolean comp(double o1, double o2) {
+ return o1 > o2;
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/DoubleObjMaxHeap.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/DoubleObjMaxHeap.java
new file mode 100644
index 00000000..8417309a
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/DoubleObjMaxHeap.java
@@ -0,0 +1,328 @@
+package de.lmu.ifi.dbs.elki.utilities.datastructures.heap;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.Arrays;
+import java.util.Comparator;
+
+import de.lmu.ifi.dbs.elki.math.MathUtil;
+
+/**
+ * Basic in-memory heap structure.
+ *
+ * This heap is built lazily: if you first add many elements, then poll the
+ * heap, it will be bulk-loaded in O(n) instead of iteratively built in O(n log
+ * n). This is implemented via a simple validTo counter.
+ *
+ * @author Erich Schubert
+ *
+ * @param <V> value type
+ */
+public class DoubleObjMaxHeap<V> {
+ /**
+ * Heap storage: keys
+ */
+ protected double[] keys;
+
+ /**
+ * Heap storage: values
+ */
+ protected Object[] values;
+
+ /**
+ * Current number of objects
+ */
+ protected int size = 0;
+
+ /**
+ * Indicate up to where the heap is valid
+ */
+ protected int validSize = 0;
+
+ /**
+ * (Structural) modification counter. Used to invalidate iterators.
+ */
+ public transient int modCount = 0;
+
+ /**
+ * Default initial capacity
+ */
+ private static final int DEFAULT_INITIAL_CAPACITY = 11;
+
+ /**
+ * Default constructor: default capacity, natural ordering.
+ */
+ public DoubleObjMaxHeap() {
+ this(DEFAULT_INITIAL_CAPACITY);
+ }
+
+ /**
+ * Constructor with initial capacity and {@link Comparator}.
+ *
+ * @param size initial capacity
+ */
+ public DoubleObjMaxHeap(int size) {
+ super();
+ this.size = 0;
+ this.keys = new double[size];
+ this.values = new Object[size];
+ }
+
+ /**
+ * Add a key-value pair to the heap
+ *
+ * @param key Key
+ * @param val Value
+ * @return Success code
+ */
+ public boolean add(double key, V val) {
+ // resize when needed
+ if(size + 1 > keys.length) {
+ resize(size + 1);
+ }
+ // final int pos = size;
+ this.keys[size] = key;
+ this.values[size] = val;
+ this.size += 1;
+ heapifyUp(size - 1, key, val);
+ validSize += 1;
+ // We have changed - return true according to {@link Collection#put}
+ modCount++;
+ return true;
+ }
+
+ /**
+ * Get the current top key
+ *
+ * @return Top key
+ */
+ public double peekKey() {
+ if(size == 0) {
+ throw new ArrayIndexOutOfBoundsException("Peek() on an empty heap!");
+ }
+ ensureValid();
+ return keys[0];
+ }
+
+ /**
+ * Get the current top value
+ *
+ * @return Value
+ */
+ @SuppressWarnings("unchecked")
+ public V peekValue() {
+ if(size == 0) {
+ throw new ArrayIndexOutOfBoundsException("Peek() on an empty heap!");
+ }
+ ensureValid();
+ return (V) values[0];
+ }
+
+ /**
+ * Remove the first element
+ */
+ public void poll() {
+ removeAt(0);
+ }
+
+ /**
+ * Repair the heap
+ */
+ protected void ensureValid() {
+ if(validSize != size) {
+ if(size > 1) {
+ // Parent of first invalid
+ int nextmin = validSize > 0 ? ((validSize - 1) >>> 1) : 0;
+ int curmin = MathUtil.nextAllOnesInt(nextmin); // Next line
+ int nextmax = curmin - 1; // End of valid line
+ int pos = (size - 2) >>> 1; // Parent of last element
+ // System.err.println(validSize+"<="+size+" iter:"+pos+"->"+curmin+", "+nextmin);
+ while(pos >= nextmin) {
+ // System.err.println(validSize+"<="+size+" iter:"+pos+"->"+curmin);
+ while(pos >= curmin) {
+ if(!heapifyDown(pos, keys[pos], values[pos])) {
+ final int parent = (pos - 1) >>> 1;
+ if(parent < curmin) {
+ nextmin = Math.min(nextmin, parent);
+ nextmax = Math.max(nextmax, parent);
+ }
+ }
+ pos--;
+ }
+ curmin = nextmin;
+ pos = Math.min(pos, nextmax);
+ nextmax = -1;
+ }
+ }
+ validSize = size;
+ }
+ }
+
+ /**
+ * Remove the element at the given position.
+ *
+ * @param pos Element position.
+ */
+ protected void removeAt(int pos) {
+ if(pos < 0 || pos >= size) {
+ return;
+ }
+ // Replacement object:
+ final double reinkey = keys[size - 1];
+ final Object reinval = values[size - 1];
+ values[size - 1] = null;
+ // Keep heap in sync
+ if(validSize == size) {
+ size -= 1;
+ validSize -= 1;
+ heapifyDown(pos, reinkey, reinval);
+ }
+ else {
+ size -= 1;
+ validSize = Math.min(pos >>> 1, validSize);
+ keys[pos] = reinkey;
+ values[pos] = reinval;
+ }
+ modCount++;
+ }
+
+ /**
+ * Execute a "Heapify Upwards" aka "SiftUp". Used in insertions.
+ *
+ * @param pos insertion position
+ * @param curkey Current key
+ * @param curval Current value
+ */
+ protected void heapifyUp(int pos, double curkey, Object curval) {
+ while(pos > 0) {
+ final int parent = (pos - 1) >>> 1;
+ double parkey = keys[parent];
+
+ if(curkey <= parkey) { // Compare
+ break;
+ }
+ keys[pos] = parkey;
+ values[pos] = values[parent];
+ pos = parent;
+ }
+ keys[pos] = curkey;
+ values[pos] = curval;
+ }
+
+ /**
+ * Execute a "Heapify Downwards" aka "SiftDown". Used in deletions.
+ *
+ * @param ipos re-insertion position
+ * @param curkey Current key
+ * @param curval Current value
+ * @return true when the order was changed
+ */
+ protected boolean heapifyDown(final int ipos, double curkey, Object curval) {
+ int pos = ipos;
+ final int half = size >>> 1;
+ while(pos < half) {
+ // Get left child (must exist!)
+ int cpos = (pos << 1) + 1;
+ double chikey = keys[cpos];
+ Object chival = values[cpos];
+ // Test right child, if present
+ final int rchild = cpos + 1;
+ if(rchild < size) {
+ double right = keys[rchild];
+ if(chikey < right) { // Compare
+ cpos = rchild;
+ chikey = right;
+ chival = values[rchild];
+ }
+ }
+
+ if(curkey >= chikey) { // Compare
+ break;
+ }
+ keys[pos] = chikey;
+ values[pos] = chival;
+ pos = cpos;
+ }
+ keys[pos] = curkey;
+ values[pos] = curval;
+ return (pos == ipos);
+ }
+
+ /**
+ * Query the size
+ *
+ * @return Size
+ */
+ public int size() {
+ return this.size;
+ }
+
+ /**
+ * Test whether we need to resize to have the requested capacity.
+ *
+ * @param requiredSize required capacity
+ */
+ protected final void resize(int requiredSize) {
+ // Double until 64, then increase by 50% each time.
+ int newCapacity = ((keys.length < 64) ? ((keys.length + 1) << 1) : ((keys.length >> 1) * 3));
+ // overflow?
+ if(newCapacity < 0) {
+ throw new OutOfMemoryError();
+ }
+ if(requiredSize > newCapacity) {
+ newCapacity = requiredSize;
+ }
+ keys = Arrays.copyOf(keys, newCapacity);
+ values = Arrays.copyOf(values, newCapacity);
+ }
+
+ /**
+ * Delete all elements from the heap.
+ */
+ public void clear() {
+ // clean up references in the array for memory management
+ Arrays.fill(values, null);
+ this.size = 0;
+ this.validSize = -1;
+ modCount++;
+ }
+
+ /**
+ * Test whether the heap is still valid.
+ *
+ * Debug method.
+ *
+ * @return {@code null} when the heap is correct
+ */
+ protected String checkHeap() {
+ ensureValid();
+ for(int i = 1; i < size; i++) {
+ final int parent = (i - 1) >>> 1;
+ if(keys[parent] < keys[i]) { // Compare
+ return "@" + parent + ": " + keys[parent] + " < @" + i + ": " + keys[i];
+ }
+ }
+ return null;
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/DoubleObjMinHeap.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/DoubleObjMinHeap.java
new file mode 100644
index 00000000..244277e8
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/DoubleObjMinHeap.java
@@ -0,0 +1,328 @@
+package de.lmu.ifi.dbs.elki.utilities.datastructures.heap;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.Arrays;
+import java.util.Comparator;
+
+import de.lmu.ifi.dbs.elki.math.MathUtil;
+
+/**
+ * Basic in-memory heap structure.
+ *
+ * This heap is built lazily: if you first add many elements, then poll the
+ * heap, it will be bulk-loaded in O(n) instead of iteratively built in O(n log
+ * n). This is implemented via a simple validTo counter.
+ *
+ * @author Erich Schubert
+ *
+ * @param <V> value type
+ */
+public class DoubleObjMinHeap<V> {
+ /**
+ * Heap storage: keys
+ */
+ protected double[] keys;
+
+ /**
+ * Heap storage: values
+ */
+ protected Object[] values;
+
+ /**
+ * Current number of objects
+ */
+ protected int size = 0;
+
+ /**
+ * Indicate up to where the heap is valid
+ */
+ protected int validSize = 0;
+
+ /**
+ * (Structural) modification counter. Used to invalidate iterators.
+ */
+ public transient int modCount = 0;
+
+ /**
+ * Default initial capacity
+ */
+ private static final int DEFAULT_INITIAL_CAPACITY = 11;
+
+ /**
+ * Default constructor: default capacity, natural ordering.
+ */
+ public DoubleObjMinHeap() {
+ this(DEFAULT_INITIAL_CAPACITY);
+ }
+
+ /**
+ * Constructor with initial capacity and {@link Comparator}.
+ *
+ * @param size initial capacity
+ */
+ public DoubleObjMinHeap(int size) {
+ super();
+ this.size = 0;
+ this.keys = new double[size];
+ this.values = new Object[size];
+ }
+
+ /**
+ * Add a key-value pair to the heap
+ *
+ * @param key Key
+ * @param val Value
+ * @return Success code
+ */
+ public boolean add(double key, V val) {
+ // resize when needed
+ if(size + 1 > keys.length) {
+ resize(size + 1);
+ }
+ // final int pos = size;
+ this.keys[size] = key;
+ this.values[size] = val;
+ this.size += 1;
+ heapifyUp(size - 1, key, val);
+ validSize += 1;
+ // We have changed - return true according to {@link Collection#put}
+ modCount++;
+ return true;
+ }
+
+ /**
+ * Get the current top key
+ *
+ * @return Top key
+ */
+ public double peekKey() {
+ if(size == 0) {
+ throw new ArrayIndexOutOfBoundsException("Peek() on an empty heap!");
+ }
+ ensureValid();
+ return keys[0];
+ }
+
+ /**
+ * Get the current top value
+ *
+ * @return Value
+ */
+ @SuppressWarnings("unchecked")
+ public V peekValue() {
+ if(size == 0) {
+ throw new ArrayIndexOutOfBoundsException("Peek() on an empty heap!");
+ }
+ ensureValid();
+ return (V) values[0];
+ }
+
+ /**
+ * Remove the first element
+ */
+ public void poll() {
+ removeAt(0);
+ }
+
+ /**
+ * Repair the heap
+ */
+ protected void ensureValid() {
+ if(validSize != size) {
+ if(size > 1) {
+ // Parent of first invalid
+ int nextmin = validSize > 0 ? ((validSize - 1) >>> 1) : 0;
+ int curmin = MathUtil.nextAllOnesInt(nextmin); // Next line
+ int nextmax = curmin - 1; // End of valid line
+ int pos = (size - 2) >>> 1; // Parent of last element
+ // System.err.println(validSize+"<="+size+" iter:"+pos+"->"+curmin+", "+nextmin);
+ while(pos >= nextmin) {
+ // System.err.println(validSize+"<="+size+" iter:"+pos+"->"+curmin);
+ while(pos >= curmin) {
+ if(!heapifyDown(pos, keys[pos], values[pos])) {
+ final int parent = (pos - 1) >>> 1;
+ if(parent < curmin) {
+ nextmin = Math.min(nextmin, parent);
+ nextmax = Math.max(nextmax, parent);
+ }
+ }
+ pos--;
+ }
+ curmin = nextmin;
+ pos = Math.min(pos, nextmax);
+ nextmax = -1;
+ }
+ }
+ validSize = size;
+ }
+ }
+
+ /**
+ * Remove the element at the given position.
+ *
+ * @param pos Element position.
+ */
+ protected void removeAt(int pos) {
+ if(pos < 0 || pos >= size) {
+ return;
+ }
+ // Replacement object:
+ final double reinkey = keys[size - 1];
+ final Object reinval = values[size - 1];
+ values[size - 1] = null;
+ // Keep heap in sync
+ if(validSize == size) {
+ size -= 1;
+ validSize -= 1;
+ heapifyDown(pos, reinkey, reinval);
+ }
+ else {
+ size -= 1;
+ validSize = Math.min(pos >>> 1, validSize);
+ keys[pos] = reinkey;
+ values[pos] = reinval;
+ }
+ modCount++;
+ }
+
+ /**
+ * Execute a "Heapify Upwards" aka "SiftUp". Used in insertions.
+ *
+ * @param pos insertion position
+ * @param curkey Current key
+ * @param curval Current value
+ */
+ protected void heapifyUp(int pos, double curkey, Object curval) {
+ while(pos > 0) {
+ final int parent = (pos - 1) >>> 1;
+ double parkey = keys[parent];
+
+ if(curkey >= parkey) { // Compare
+ break;
+ }
+ keys[pos] = parkey;
+ values[pos] = values[parent];
+ pos = parent;
+ }
+ keys[pos] = curkey;
+ values[pos] = curval;
+ }
+
+ /**
+ * Execute a "Heapify Downwards" aka "SiftDown". Used in deletions.
+ *
+ * @param ipos re-insertion position
+ * @param curkey Current key
+ * @param curval Current value
+ * @return true when the order was changed
+ */
+ protected boolean heapifyDown(final int ipos, double curkey, Object curval) {
+ int pos = ipos;
+ final int half = size >>> 1;
+ while(pos < half) {
+ // Get left child (must exist!)
+ int cpos = (pos << 1) + 1;
+ double chikey = keys[cpos];
+ Object chival = values[cpos];
+ // Test right child, if present
+ final int rchild = cpos + 1;
+ if(rchild < size) {
+ double right = keys[rchild];
+ if(chikey > right) { // Compare
+ cpos = rchild;
+ chikey = right;
+ chival = values[rchild];
+ }
+ }
+
+ if(curkey <= chikey) { // Compare
+ break;
+ }
+ keys[pos] = chikey;
+ values[pos] = chival;
+ pos = cpos;
+ }
+ keys[pos] = curkey;
+ values[pos] = curval;
+ return (pos == ipos);
+ }
+
+ /**
+ * Query the size
+ *
+ * @return Size
+ */
+ public int size() {
+ return this.size;
+ }
+
+ /**
+ * Test whether we need to resize to have the requested capacity.
+ *
+ * @param requiredSize required capacity
+ */
+ protected final void resize(int requiredSize) {
+ // Double until 64, then increase by 50% each time.
+ int newCapacity = ((keys.length < 64) ? ((keys.length + 1) << 1) : ((keys.length >> 1) * 3));
+ // overflow?
+ if(newCapacity < 0) {
+ throw new OutOfMemoryError();
+ }
+ if(requiredSize > newCapacity) {
+ newCapacity = requiredSize;
+ }
+ keys = Arrays.copyOf(keys, newCapacity);
+ values = Arrays.copyOf(values, newCapacity);
+ }
+
+ /**
+ * Delete all elements from the heap.
+ */
+ public void clear() {
+ // clean up references in the array for memory management
+ Arrays.fill(values, null);
+ this.size = 0;
+ this.validSize = -1;
+ modCount++;
+ }
+
+ /**
+ * Test whether the heap is still valid.
+ *
+ * Debug method.
+ *
+ * @return {@code null} when the heap is correct
+ */
+ protected String checkHeap() {
+ ensureValid();
+ for(int i = 1; i < size; i++) {
+ final int parent = (i - 1) >>> 1;
+ if(keys[parent] > keys[i]) { // Compare
+ return "@" + parent + ": " + keys[parent] + " < @" + i + ": " + keys[i];
+ }
+ }
+ return null;
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/DoublePriorityObject.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/DoublePriorityObject.java
index 976a4d0c..92d548cb 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/DoublePriorityObject.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/DoublePriorityObject.java
@@ -79,8 +79,9 @@ public class DoublePriorityObject<O> implements PairInterface<Double, O>, Compar
}
@Override
+ @Deprecated
public Double getFirst() {
- return priority;
+ return Double.valueOf(priority);
}
@Override
@@ -120,8 +121,8 @@ public class DoublePriorityObject<O> implements PairInterface<Double, O>, Compar
@Override
public String toString() {
- StringBuffer buf = new StringBuffer();
- buf.append(priority).append(":").append(object.toString());
+ StringBuilder buf = new StringBuilder();
+ buf.append(priority).append(':').append(object.toString());
return buf.toString();
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/Heap.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/Heap.java
index 307a0807..86d3ae08 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/Heap.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/Heap.java
@@ -23,11 +23,7 @@ package de.lmu.ifi.dbs.elki.utilities.datastructures.heap;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.io.Serializable;
-import java.util.AbstractQueue;
-import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collection;
import java.util.Comparator;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
@@ -49,39 +45,34 @@ import de.lmu.ifi.dbs.elki.math.MathUtil;
* @param <E> Element type. Should be {@link java.lang.Comparable} or a
* {@link java.util.Comparator} needs to be given.
*/
-public class Heap<E> extends AbstractQueue<E> implements Serializable {
+public class Heap<E> implements Iterable<E> {
/**
- * Serial version
- */
- private static final long serialVersionUID = 1L;
-
- /**
- * Heap storage
+ * Heap storage.
*/
protected transient Object[] queue;
/**
- * Current number of objects
+ * Current number of objects.
*/
protected int size = 0;
/**
- * Indicate up to where the heap is valid
+ * Indicate up to where the heap is valid.
*/
protected int validSize = 0;
/**
- * The comparator or {@code null}
+ * The comparator or {@code null}.
*/
protected final Comparator<Object> comparator;
/**
* (Structural) modification counter. Used to invalidate iterators.
*/
- public transient int modCount = 0;
+ private transient int modCount = 0;
/**
- * Default initial capacity
+ * Default initial capacity.
*/
private static final int DEFAULT_INITIAL_CAPACITY = 11;
@@ -124,16 +115,14 @@ public class Heap<E> extends AbstractQueue<E> implements Serializable {
this.comparator = (Comparator<Object>) comparator;
}
- @Override
- public boolean add(E e) {
- // Never full - overriding probably slightly faster
- return offer(e);
- }
-
- @Override
- public boolean offer(E e) {
+ /**
+ * Add an element to the heap.
+ *
+ * @param e Element to add
+ */
+ public void add(E e) {
// resize when needed
- if(size + 1 > queue.length) {
+ if (size + 1 > queue.length) {
resize(size + 1);
}
// final int pos = size;
@@ -141,46 +130,69 @@ public class Heap<E> extends AbstractQueue<E> implements Serializable {
this.size += 1;
heapifyUp(size - 1, e);
validSize += 1;
- // We have changed - return true according to {@link Collection#put}
- modCount++;
- return true;
+ heapModified();
}
- @Override
+ /**
+ * Combined operation that removes the top element, and inserts a new element
+ * instead.
+ *
+ * @param e New element to insert
+ * @return Previous top element of the heap
+ */
+ @SuppressWarnings("unchecked")
+ public E replaceTopElement(E e) {
+ ensureValid();
+ E oldroot = (E) queue[0];
+ heapifyDown(0, e);
+ heapModified();
+ return oldroot;
+ }
+
+ /**
+ * Peek at the top element.
+ *
+ * @return Top element.
+ */
+ @SuppressWarnings("unchecked")
public E peek() {
- if(size == 0) {
+ if (size == 0) {
return null;
}
ensureValid();
- return castQueueElement(0);
+ return (E) queue[0];
}
- @Override
+ /**
+ * Remove the top element.
+ *
+ * @return Top element.
+ */
public E poll() {
ensureValid();
return removeAt(0);
}
/**
- * Repair the heap
+ * Perform pending heap repair operations in a single bulk operation.
*/
protected void ensureValid() {
- if(validSize != size) {
- if(size > 1) {
+ if (validSize != size) {
+ if (size > 1) {
// Bottom up heap update.
- if(comparator != null) {
+ if (comparator != null) {
// Parent of first invalid
int nextmin = validSize > 0 ? ((validSize - 1) >>> 1) : 0;
int curmin = MathUtil.nextAllOnesInt(nextmin); // Next line
int nextmax = curmin - 1; // End of valid line
int pos = (size - 2) >>> 1; // Parent of last element
// System.err.println(validSize+"<="+size+" iter:"+pos+"->"+curmin+", "+nextmin);
- while(pos >= nextmin) {
+ while (pos >= nextmin) {
// System.err.println(validSize+"<="+size+" iter:"+pos+"->"+curmin);
- while(pos >= curmin) {
- if(!heapifyDownComparator(pos, queue[pos])) {
+ while (pos >= curmin) {
+ if (!heapifyDownComparator(pos, queue[pos])) {
final int parent = (pos - 1) >>> 1;
- if(parent < curmin) {
+ if (parent < curmin) {
nextmin = Math.min(nextmin, parent);
nextmax = Math.max(nextmax, parent);
}
@@ -191,20 +203,19 @@ public class Heap<E> extends AbstractQueue<E> implements Serializable {
pos = Math.min(pos, nextmax);
nextmax = -1;
}
- }
- else {
+ } else {
// Parent of first invalid
int nextmin = validSize > 0 ? ((validSize - 1) >>> 1) : 0;
int curmin = MathUtil.nextAllOnesInt(nextmin); // Next line
int nextmax = curmin - 1; // End of valid line
int pos = (size - 2) >>> 1; // Parent of last element
// System.err.println(validSize+"<="+size+" iter:"+pos+"->"+curmin+", "+nextmin);
- while(pos >= nextmin) {
+ while (pos >= nextmin) {
// System.err.println(validSize+"<="+size+" iter:"+pos+"->"+curmin);
- while(pos >= curmin) {
- if(!heapifyDownComparable(pos, queue[pos])) {
+ while (pos >= curmin) {
+ if (!heapifyDownComparable(pos, queue[pos])) {
final int parent = (pos - 1) >>> 1;
- if(parent < curmin) {
+ if (parent < curmin) {
nextmin = Math.min(nextmin, parent);
nextmax = Math.max(nextmax, parent);
}
@@ -225,27 +236,28 @@ public class Heap<E> extends AbstractQueue<E> implements Serializable {
* Remove the element at the given position.
*
* @param pos Element position.
+ * @return Element that was at this position.
*/
+ @SuppressWarnings("unchecked")
protected E removeAt(int pos) {
- if(pos < 0 || pos >= size) {
+ if (pos < 0 || pos >= size) {
return null;
}
- final E ret = castQueueElement(pos);
+ final E ret = (E) queue[pos];
// Replacement object:
final Object reinsert = queue[size - 1];
queue[size - 1] = null;
// Keep heap in sync
- if(validSize == size) {
+ if (validSize == size) {
size -= 1;
validSize -= 1;
heapifyDown(pos, reinsert);
- }
- else {
+ } else {
size -= 1;
validSize = Math.min(pos >>> 1, validSize);
queue[pos] = reinsert;
}
- modCount++;
+ heapModified();
return ret;
}
@@ -257,10 +269,9 @@ public class Heap<E> extends AbstractQueue<E> implements Serializable {
*/
protected void heapifyUp(int pos, E elem) {
assert (pos < size && pos >= 0);
- if(comparator != null) {
+ if (comparator != null) {
heapifyUpComparator(pos, elem);
- }
- else {
+ } else {
heapifyUpComparable(pos, elem);
}
}
@@ -274,11 +285,11 @@ public class Heap<E> extends AbstractQueue<E> implements Serializable {
@SuppressWarnings("unchecked")
protected void heapifyUpComparable(int pos, Object elem) {
final Comparable<Object> cur = (Comparable<Object>) elem; // queue[pos];
- while(pos > 0) {
+ while (pos > 0) {
final int parent = (pos - 1) >>> 1;
Object par = queue[parent];
- if(cur.compareTo(par) >= 0) {
+ if (cur.compareTo(par) >= 0) {
break;
}
queue[pos] = par;
@@ -294,11 +305,11 @@ public class Heap<E> extends AbstractQueue<E> implements Serializable {
* @param cur Element to insert
*/
protected void heapifyUpComparator(int pos, Object cur) {
- while(pos > 0) {
+ while (pos > 0) {
final int parent = (pos - 1) >>> 1;
Object par = queue[parent];
- if(comparator.compare(cur, par) >= 0) {
+ if (comparator.compare(cur, par) >= 0) {
break;
}
queue[pos] = par;
@@ -316,10 +327,9 @@ public class Heap<E> extends AbstractQueue<E> implements Serializable {
*/
protected boolean heapifyDown(int pos, Object reinsert) {
assert (pos >= 0);
- if(comparator != null) {
+ if (comparator != null) {
return heapifyDownComparator(pos, reinsert);
- }
- else {
+ } else {
return heapifyDownComparable(pos, reinsert);
}
}
@@ -328,6 +338,7 @@ public class Heap<E> extends AbstractQueue<E> implements Serializable {
* Execute a "Heapify Downwards" aka "SiftDown". Used in deletions.
*
* @param ipos re-insertion position
+ * @param reinsert Object to reinsert
* @return true when the order was changed
*/
@SuppressWarnings("unchecked")
@@ -335,21 +346,21 @@ public class Heap<E> extends AbstractQueue<E> implements Serializable {
Comparable<Object> cur = (Comparable<Object>) reinsert;
int pos = ipos;
final int half = size >>> 1;
- while(pos < half) {
+ while (pos < half) {
// Get left child (must exist!)
int cpos = (pos << 1) + 1;
Object child = queue[cpos];
// Test right child, if present
final int rchild = cpos + 1;
- if(rchild < size) {
+ if (rchild < size) {
Object right = queue[rchild];
- if(((Comparable<Object>) child).compareTo(right) > 0) {
+ if (((Comparable<Object>) child).compareTo(right) > 0) {
cpos = rchild;
child = right;
}
}
- if(cur.compareTo(child) <= 0) {
+ if (cur.compareTo(child) <= 0) {
break;
}
queue[pos] = child;
@@ -363,30 +374,31 @@ public class Heap<E> extends AbstractQueue<E> implements Serializable {
* Execute a "Heapify Downwards" aka "SiftDown". Used in deletions.
*
* @param ipos re-insertion position
+ * @param cur Object to reinsert
* @return true when the order was changed
*/
protected boolean heapifyDownComparator(final int ipos, Object cur) {
int pos = ipos;
final int half = size >>> 1;
- while(pos < half) {
+ while (pos < half) {
int min = pos;
Object best = cur;
final int lchild = (pos << 1) + 1;
Object left = queue[lchild];
- if(comparator.compare(best, left) > 0) {
+ if (comparator.compare(best, left) > 0) {
min = lchild;
best = left;
}
final int rchild = lchild + 1;
- if(rchild < size) {
+ if (rchild < size) {
Object right = queue[rchild];
- if(comparator.compare(best, right) > 0) {
+ if (comparator.compare(best, right) > 0) {
min = rchild;
best = right;
}
}
- if(min == pos) {
+ if (min == pos) {
break;
}
queue[pos] = best;
@@ -396,56 +408,53 @@ public class Heap<E> extends AbstractQueue<E> implements Serializable {
return (pos == ipos);
}
- @SuppressWarnings("unchecked")
- protected final E castQueueElement(int n) {
- return (E) queue[n];
- }
-
- @Override
+ /**
+ * Get the heap size.
+ *
+ * @return Heap size
+ */
public int size() {
return this.size;
}
/**
+ * Test for emptiness.
+ *
+ * @return true when the heap is empty
+ */
+ public boolean isEmpty() {
+ return size == 0;
+ }
+
+ /**
* Test whether we need to resize to have the requested capacity.
*
* @param requiredSize required capacity
*/
protected final void resize(int requiredSize) {
// Double until 64, then increase by 50% each time.
- int newCapacity = ((queue.length < 64) ? ((queue.length + 1) * 2) : ((queue.length / 2) * 3));
+ int newCapacity = ((queue.length < 64) ? ((queue.length + 1) << 1) : ((queue.length >> 1) * 3));
// overflow?
- if(newCapacity < 0) {
+ if (newCapacity < 0) {
throw new OutOfMemoryError();
}
- if(requiredSize > newCapacity) {
+ if (requiredSize > newCapacity) {
newCapacity = requiredSize;
}
queue = Arrays.copyOf(queue, newCapacity);
}
- @Override
+ /**
+ * Clear the heap.
+ */
public void clear() {
// clean up references in the array for memory management
- for(int i = 0; i < size; i++) {
+ for (int i = 0; i < size; i++) {
queue[i] = null;
}
this.size = 0;
this.validSize = -1;
- modCount++;
- }
-
- @Override
- public boolean contains(Object o) {
- if(o != null) {
- // TODO: use order to reduce search space?
- for(int i = 0; i < size; i++) {
- if(o.equals(queue[i])) {
- return true;
- }
- }
- }
- return false;
+ heapModified();
}
@Override
@@ -453,19 +462,11 @@ public class Heap<E> extends AbstractQueue<E> implements Serializable {
return new Itr();
}
- @Override
- public boolean addAll(Collection<? extends E> c) {
- final int addsize = c.size();
- if(addsize <= 0) {
- return false;
- }
- if(size + addsize > queue.length) {
- resize(size + addsize);
- }
- for(E elem : c) {
- add(elem);
- }
- return true;
+ /**
+ * Called at the end of each heap modification.
+ */
+ protected void heapModified() {
+ modCount++;
}
/**
@@ -477,7 +478,7 @@ public class Heap<E> extends AbstractQueue<E> implements Serializable {
*/
protected final class Itr implements Iterator<E> {
/**
- * Cursor position
+ * Cursor position.
*/
private int cursor = 0;
@@ -491,26 +492,26 @@ public class Heap<E> extends AbstractQueue<E> implements Serializable {
return cursor < size;
}
+ @SuppressWarnings("unchecked")
@Override
public E next() {
- if(expectedModCount != modCount) {
+ if (expectedModCount != modCount) {
throw new ConcurrentModificationException();
}
- if(cursor < size) {
- return castQueueElement(cursor++);
+ if (cursor < size) {
+ return (E) queue[cursor++];
}
throw new NoSuchElementException();
}
@Override
public void remove() {
- if(expectedModCount != modCount) {
+ if (expectedModCount != modCount) {
throw new ConcurrentModificationException();
}
- if(cursor > 0) {
+ if (cursor > 0) {
cursor--;
- }
- else {
+ } else {
throw new IllegalStateException();
}
expectedModCount = modCount;
@@ -518,20 +519,6 @@ public class Heap<E> extends AbstractQueue<E> implements Serializable {
}
/**
- * Return the heap as a sorted array list, by repeated polling. This will
- * empty the heap!
- *
- * @return new array list
- */
- public ArrayList<E> toSortedArrayList() {
- ArrayList<E> ret = new ArrayList<E>(size());
- while(!isEmpty()) {
- ret.add(poll());
- }
- return ret;
- }
-
- /**
* Test whether the heap is still valid.
*
* Debug method.
@@ -540,24 +527,23 @@ public class Heap<E> extends AbstractQueue<E> implements Serializable {
*/
protected String checkHeap() {
ensureValid();
- if(comparator == null) {
- for(int i = 1; i < size; i++) {
+ if (comparator == null) {
+ for (int i = 1; i < size; i++) {
final int parent = (i - 1) >>> 1;
@SuppressWarnings("unchecked")
Comparable<Object> po = (Comparable<Object>) queue[parent];
- if(po.compareTo(queue[i]) > 0) {
+ if (po.compareTo(queue[i]) > 0) {
return "@" + parent + ": " + queue[parent] + " < @" + i + ": " + queue[i];
}
}
- }
- else {
- for(int i = 1; i < size; i++) {
+ } else {
+ for (int i = 1; i < size; i++) {
final int parent = (i - 1) >>> 1;
- if(comparator.compare(queue[parent], queue[i]) > 0) {
+ if (comparator.compare(queue[parent], queue[i]) > 0) {
return "@" + parent + ": " + queue[parent] + " < @" + i + ": " + queue[i];
}
}
}
return null;
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/IntegerHeap.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/IntegerHeap.java
new file mode 100644
index 00000000..6203ad96
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/IntegerHeap.java
@@ -0,0 +1,264 @@
+package de.lmu.ifi.dbs.elki.utilities.datastructures.heap;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.Arrays;
+
+import de.lmu.ifi.dbs.elki.math.MathUtil;
+
+/**
+ * Basic in-memory heap structure.
+ *
+ * This heap is built lazily: if you first add many elements, then poll the
+ * heap, it will be bulk-loaded in O(n) instead of iteratively built in O(n log
+ * n). This is implemented via a simple validTo counter.
+ *
+ * @author Erich Schubert
+ */
+public abstract class IntegerHeap extends AbstractHeap {
+ /**
+ * Heap storage: queue
+ */
+ protected transient int[] queue;
+
+ /**
+ * Constructor with initial capacity.
+ *
+ * @param size initial capacity
+ */
+ public IntegerHeap(int size) {
+ super();
+ this.size = 0;
+ this.queue = new int[size];
+ }
+
+ /**
+ * Add a key-value pair to the heap
+ *
+ * @param key Key
+ */
+ public void add(int key) {
+ // resize when needed
+ if (size + 1 > queue.length) {
+ resize(size + 1);
+ }
+ // final int pos = size;
+ this.queue[size] = key;
+ this.size += 1;
+ heapifyUp(size - 1, key);
+ validSize += 1;
+ heapModified();
+ }
+
+ /**
+ * Add a key-value pair to the heap, except if the new element is larger than
+ * the top, and we are at design size (overflow)
+ *
+ * @param key Key
+ * @param max Maximum size of heap
+ */
+ public void add(int key, int max) {
+ if (size < max) {
+ add(key);
+ } else if (comp(key, peek())) {
+ replaceTopElement(key);
+ }
+ }
+
+ /**
+ * Combined operation that removes the top element, and inserts a new element
+ * instead.
+ *
+ * @param e New element to insert
+ * @return Previous top element of the heap
+ */
+ public int replaceTopElement(int e) {
+ ensureValid();
+ int oldroot = queue[0];
+ heapifyDown(0, e);
+ heapModified();
+ return oldroot;
+ }
+
+ /**
+ * Get the current top key
+ *
+ * @return Top key
+ */
+ public int peek() {
+ if (size == 0) {
+ throw new ArrayIndexOutOfBoundsException("Peek() on an empty heap!");
+ }
+ ensureValid();
+ return queue[0];
+ }
+
+ /**
+ * Remove the first element
+ *
+ * @return Top element
+ */
+ public int poll() {
+ return removeAt(0);
+ }
+
+ /**
+ * Repair the heap
+ */
+ protected void ensureValid() {
+ if (validSize != size) {
+ if (size > 1) {
+ // Parent of first invalid
+ int nextmin = validSize > 0 ? ((validSize - 1) >>> 1) : 0;
+ int curmin = MathUtil.nextAllOnesInt(nextmin); // Next line
+ int nextmax = curmin - 1; // End of valid line
+ int pos = (size - 2) >>> 1; // Parent of last element
+ // System.err.println(validSize+"<="+size+" iter:"+pos+"->"+curmin+", "+nextmin);
+ while (pos >= nextmin) {
+ // System.err.println(validSize+"<="+size+" iter:"+pos+"->"+curmin);
+ while (pos >= curmin) {
+ if (!heapifyDown(pos, queue[pos])) {
+ final int parent = (pos - 1) >>> 1;
+ if (parent < curmin) {
+ nextmin = Math.min(nextmin, parent);
+ nextmax = Math.max(nextmax, parent);
+ }
+ }
+ pos--;
+ }
+ curmin = nextmin;
+ pos = Math.min(pos, nextmax);
+ nextmax = -1;
+ }
+ }
+ validSize = size;
+ }
+ }
+
+ /**
+ * Remove the element at the given position.
+ *
+ * @param pos Element position.
+ * @return Removed element
+ */
+ protected int removeAt(int pos) {
+ if (pos < 0 || pos >= size) {
+ return 0;
+ }
+ final int top = queue[0];
+ // Replacement object:
+ final int reinkey = queue[size - 1];
+ // Keep heap in sync
+ if (validSize == size) {
+ size -= 1;
+ validSize -= 1;
+ heapifyDown(pos, reinkey);
+ } else {
+ size -= 1;
+ validSize = Math.min(pos >>> 1, validSize);
+ queue[pos] = reinkey;
+ }
+ heapModified();
+ return top;
+ }
+
+ /**
+ * Execute a "Heapify Upwards" aka "SiftUp". Used in insertions.
+ *
+ * @param pos insertion position
+ * @param curkey Current key
+ */
+ protected void heapifyUp(int pos, int curkey) {
+ while (pos > 0) {
+ final int parent = (pos - 1) >>> 1;
+ int parkey = queue[parent];
+
+ if (comp(curkey, parkey)) { // Compare
+ break;
+ }
+ queue[pos] = parkey;
+ pos = parent;
+ }
+ queue[pos] = curkey;
+ }
+
+ /**
+ * Execute a "Heapify Downwards" aka "SiftDown". Used in deletions.
+ *
+ * @param ipos re-insertion position
+ * @param curkey Current key
+ * @return true when the order was changed
+ */
+ protected boolean heapifyDown(final int ipos, int curkey) {
+ int pos = ipos;
+ final int half = size >>> 1;
+ while (pos < half) {
+ // Get left child (must exist!)
+ int cpos = (pos << 1) + 1;
+ int chikey = queue[cpos];
+ // Test right child, if present
+ final int rchild = cpos + 1;
+ if (rchild < size) {
+ int right = queue[rchild];
+ if (comp(chikey, right)) { // Compare
+ cpos = rchild;
+ chikey = right;
+ }
+ }
+
+ if (comp(chikey, curkey)) { // Compare
+ break;
+ }
+ queue[pos] = chikey;
+ pos = cpos;
+ }
+ queue[pos] = curkey;
+ return (pos == ipos);
+ }
+
+ /**
+ * Test whether we need to resize to have the requested capacity.
+ *
+ * @param requiredSize required capacity
+ */
+ protected final void resize(int requiredSize) {
+ queue = Arrays.copyOf(queue, desiredSize(requiredSize, queue.length));
+ }
+
+ /**
+ * Delete all elements from the heap.
+ */
+ @Override
+ public void clear() {
+ super.clear();
+ for (int i = 0; i < size; i++) {
+ queue[i] = 0;
+ }
+ }
+
+ /**
+ * Compare two objects
+ */
+ abstract protected boolean comp(int o1, int o2);
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/IntegerMaxHeap.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/IntegerMaxHeap.java
new file mode 100644
index 00000000..383eb727
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/IntegerMaxHeap.java
@@ -0,0 +1,62 @@
+package de.lmu.ifi.dbs.elki.utilities.datastructures.heap;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * Basic in-memory heap structure.
+ *
+ * This heap is built lazily: if you first add many elements, then poll the
+ * heap, it will be bulk-loaded in O(n) instead of iteratively built in O(n log
+ * n). This is implemented via a simple validTo counter.
+ *
+ * @author Erich Schubert
+ */
+public class IntegerMaxHeap extends IntegerHeap {
+ /**
+ * Constructor with default capacity.
+ */
+ public IntegerMaxHeap() {
+ super(DEFAULT_INITIAL_CAPACITY);
+ }
+
+ /**
+ * Constructor with initial capacity.
+ *
+ * @param size initial capacity
+ */
+ public IntegerMaxHeap(int size) {
+ super(size);
+ }
+
+ /**
+ * Compare two objects
+ *
+ * @param o1 First object
+ * @param o2 Second object
+ */
+ @Override
+ protected boolean comp(int o1, int o2) {
+ return o1 < o2;
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/IntegerMinHeap.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/IntegerMinHeap.java
new file mode 100644
index 00000000..f81fe275
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/IntegerMinHeap.java
@@ -0,0 +1,62 @@
+package de.lmu.ifi.dbs.elki.utilities.datastructures.heap;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * Basic in-memory heap structure.
+ *
+ * This heap is built lazily: if you first add many elements, then poll the
+ * heap, it will be bulk-loaded in O(n) instead of iteratively built in O(n log
+ * n). This is implemented via a simple validTo counter.
+ *
+ * @author Erich Schubert
+ */
+public class IntegerMinHeap extends IntegerHeap {
+ /**
+ * Constructor with default capacity.
+ */
+ public IntegerMinHeap() {
+ super(DEFAULT_INITIAL_CAPACITY);
+ }
+
+ /**
+ * Constructor with initial capacity.
+ *
+ * @param size initial capacity
+ */
+ public IntegerMinHeap(int size) {
+ super(size);
+ }
+
+ /**
+ * Compare two objects
+ *
+ * @param o1 First object
+ * @param o2 Second object
+ */
+ @Override
+ protected boolean comp(int o1, int o2) {
+ return o1 > o2;
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/IntegerPriorityObject.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/IntegerPriorityObject.java
index 9dd941ec..2014de65 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/IntegerPriorityObject.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/IntegerPriorityObject.java
@@ -79,8 +79,9 @@ public class IntegerPriorityObject<O> implements PairInterface<Integer, O>, Comp
}
@Override
+ @Deprecated
public Integer getFirst() {
- return priority;
+ return Integer.valueOf(priority);
}
@Override
@@ -120,8 +121,8 @@ public class IntegerPriorityObject<O> implements PairInterface<Integer, O>, Comp
@Override
public String toString() {
- StringBuffer buf = new StringBuffer();
- buf.append(priority).append(":").append(object.toString());
+ StringBuilder buf = new StringBuilder();
+ buf.append(priority).append(':').append(object.toString());
return buf.toString();
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/KNNHeap.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/KNNHeap.java
deleted file mode 100644
index 6c59123b..00000000
--- a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/KNNHeap.java
+++ /dev/null
@@ -1,153 +0,0 @@
-package de.lmu.ifi.dbs.elki.utilities.datastructures.heap;
-
-/*
- This file is part of ELKI:
- Environment for Developing KDD-Applications Supported by Index-Structures
-
- Copyright (C) 2012
- Ludwig-Maximilians-Universität München
- Lehr- und Forschungseinheit für Datenbanksysteme
- ELKI Development Team
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-
-import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
-import de.lmu.ifi.dbs.elki.database.query.GenericDistanceResultPair;
-import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
-
-/**
- * Heap used for KNN management.
- *
- * @author Erich Schubert
- *
- * @apiviz.has KNNList oneway - - serializes to
- *
- * @param <D> distance type
- */
-public class KNNHeap<D extends Distance<D>> extends TiedTopBoundedHeap<DistanceResultPair<D>> {
- /**
- * Serial version
- */
- private static final long serialVersionUID = 1L;
-
- /**
- * Maximum distance, usually infiniteDistance
- */
- private final D maxdist;
-
- /**
- * Constructor.
- *
- * @param k k Parameter
- * @param maxdist k-distance to return for less than k neighbors - usually
- * infiniteDistance
- */
- public KNNHeap(int k, D maxdist) {
- super(k, new Comp<D>());
- this.maxdist = maxdist;
- }
-
- /**
- * Simplified constructor. Will return {@code null} as kNN distance with less
- * than k entries.
- *
- * @param k k Parameter
- */
- public KNNHeap(int k) {
- this(k, null);
- }
-
- @Override
- public ArrayList<DistanceResultPair<D>> toSortedArrayList() {
- ArrayList<DistanceResultPair<D>> list = super.toSortedArrayList();
- Collections.reverse(list);
- return list;
- }
-
- /**
- * Serialize to a {@link KNNList}. This empties the heap!
- *
- * @return KNNList with the heaps contents.
- */
- public KNNList<D> toKNNList() {
- return new KNNList<D>(this);
- }
-
- /**
- * Get the K parameter ("maxsize" internally).
- *
- * @return K
- */
- public int getK() {
- return super.getMaxSize();
- }
-
- /**
- * Get the distance to the k nearest neighbor, or maxdist otherwise.
- *
- * @return Maximum distance
- */
- public D getKNNDistance() {
- if(size() < getK()) {
- return maxdist;
- }
- return peek().getDistance();
- }
-
- /**
- * Get maximum distance in heap
- */
- public D getMaximumDistance() {
- if(isEmpty()) {
- return maxdist;
- }
- return peek().getDistance();
- }
-
- /**
- * Add a distance-id pair to the heap unless the distance is too large.
- *
- * Compared to the super.add() method, this often saves the pair construction.
- *
- * @param distance Distance value
- * @param id ID number
- * @return success code
- */
- public boolean add(D distance, DBIDRef id) {
- if(size() < maxsize || peek().getDistance().compareTo(distance) >= 0) {
- return super.add(new GenericDistanceResultPair<D>(distance, id.getDBID()));
- }
- return true; /* "success" */
- }
-
- /**
- * Comparator to use.
- *
- * @author Erich Schubert
- *
- * @apiviz.exclude
- */
- public static class Comp<D extends Distance<D>> implements Comparator<DistanceResultPair<D>> {
- @Override
- public int compare(DistanceResultPair<D> o1, DistanceResultPair<D> o2) {
- return -o1.compareByDistance(o2);
- }
- }
-} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/ObjectHeap.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/ObjectHeap.java
new file mode 100644
index 00000000..2e20ed56
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/ObjectHeap.java
@@ -0,0 +1,267 @@
+package de.lmu.ifi.dbs.elki.utilities.datastructures.heap;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.Arrays;
+
+import de.lmu.ifi.dbs.elki.math.MathUtil;
+
+/**
+ * Basic in-memory heap structure.
+ *
+ * This heap is built lazily: if you first add many elements, then poll the
+ * heap, it will be bulk-loaded in O(n) instead of iteratively built in O(n log
+ * n). This is implemented via a simple validTo counter.
+ *
+ * @author Erich Schubert
+ */
+public abstract class ObjectHeap<K> extends AbstractHeap {
+ /**
+ * Heap storage: queue
+ */
+ protected transient Object[] queue;
+
+ /**
+ * Constructor with initial capacity.
+ *
+ * @param size initial capacity
+ */
+ public ObjectHeap(int size) {
+ super();
+ this.size = 0;
+ this.queue = new Object[size];
+ }
+
+ /**
+ * Add a key-value pair to the heap
+ *
+ * @param key Key
+ */
+ public void add(Object key) {
+ // resize when needed
+ if (size + 1 > queue.length) {
+ resize(size + 1);
+ }
+ // final int pos = size;
+ this.queue[size] = key;
+ this.size += 1;
+ heapifyUp(size - 1, key);
+ validSize += 1;
+ heapModified();
+ }
+
+ /**
+ * Add a key-value pair to the heap, except if the new element is larger than
+ * the top, and we are at design size (overflow)
+ *
+ * @param key Key
+ * @param max Maximum size of heap
+ */
+ public void add(Object key, int max) {
+ if (size < max) {
+ add(key);
+ } else if (comp(key, peek())) {
+ replaceTopElement(key);
+ }
+ }
+
+ /**
+ * Combined operation that removes the top element, and inserts a new element
+ * instead.
+ *
+ * @param e New element to insert
+ * @return Previous top element of the heap
+ */
+ @SuppressWarnings("unchecked")
+ public Object replaceTopElement(Object e) {
+ ensureValid();
+ Object oldroot = (K) queue[0];
+ heapifyDown(0, e);
+ heapModified();
+ return oldroot;
+ }
+
+ /**
+ * Get the current top key
+ *
+ * @return Top key
+ */
+ @SuppressWarnings("unchecked")
+ public Object peek() {
+ if (size == 0) {
+ throw new ArrayIndexOutOfBoundsException("Peek() on an empty heap!");
+ }
+ ensureValid();
+ return (K) queue[0];
+ }
+
+ /**
+ * Remove the first element
+ *
+ * @return Top element
+ */
+ public Object poll() {
+ return removeAt(0);
+ }
+
+ /**
+ * Repair the heap
+ */
+ protected void ensureValid() {
+ if (validSize != size) {
+ if (size > 1) {
+ // Parent of first invalid
+ int nextmin = validSize > 0 ? ((validSize - 1) >>> 1) : 0;
+ int curmin = MathUtil.nextAllOnesInt(nextmin); // Next line
+ int nextmax = curmin - 1; // End of valid line
+ int pos = (size - 2) >>> 1; // Parent of last element
+ // System.err.println(validSize+"<="+size+" iter:"+pos+"->"+curmin+", "+nextmin);
+ while (pos >= nextmin) {
+ // System.err.println(validSize+"<="+size+" iter:"+pos+"->"+curmin);
+ while (pos >= curmin) {
+ if (!heapifyDown(pos, queue[pos])) {
+ final int parent = (pos - 1) >>> 1;
+ if (parent < curmin) {
+ nextmin = Math.min(nextmin, parent);
+ nextmax = Math.max(nextmax, parent);
+ }
+ }
+ pos--;
+ }
+ curmin = nextmin;
+ pos = Math.min(pos, nextmax);
+ nextmax = -1;
+ }
+ }
+ validSize = size;
+ }
+ }
+
+ /**
+ * Remove the element at the given position.
+ *
+ * @param pos Element position.
+ * @return Removed element
+ */
+ @SuppressWarnings("unchecked")
+ protected Object removeAt(int pos) {
+ if (pos < 0 || pos >= size) {
+ return null;
+ }
+ final Object top = (K) queue[0];
+ // Replacement object:
+ final Object reinkey = queue[size - 1];
+ // Keep heap in sync
+ if (validSize == size) {
+ size -= 1;
+ validSize -= 1;
+ heapifyDown(pos, reinkey);
+ } else {
+ size -= 1;
+ validSize = Math.min(pos >>> 1, validSize);
+ queue[pos] = reinkey;
+ }
+ heapModified();
+ return top;
+ }
+
+ /**
+ * Execute a "Heapify Upwards" aka "SiftUp". Used in insertions.
+ *
+ * @param pos insertion position
+ * @param curkey Current key
+ */
+ protected void heapifyUp(int pos, Object curkey) {
+ while (pos > 0) {
+ final int parent = (pos - 1) >>> 1;
+ Object parkey = queue[parent];
+
+ if (comp(curkey, parkey)) { // Compare
+ break;
+ }
+ queue[pos] = parkey;
+ pos = parent;
+ }
+ queue[pos] = curkey;
+ }
+
+ /**
+ * Execute a "Heapify Downwards" aka "SiftDown". Used in deletions.
+ *
+ * @param ipos re-insertion position
+ * @param curkey Current key
+ * @return true when the order was changed
+ */
+ protected boolean heapifyDown(final int ipos, Object curkey) {
+ int pos = ipos;
+ final int half = size >>> 1;
+ while (pos < half) {
+ // Get left child (must exist!)
+ int cpos = (pos << 1) + 1;
+ Object chikey = queue[cpos];
+ // Test right child, if present
+ final int rchild = cpos + 1;
+ if (rchild < size) {
+ Object right = queue[rchild];
+ if (comp(chikey, right)) { // Compare
+ cpos = rchild;
+ chikey = right;
+ }
+ }
+
+ if (comp(chikey, curkey)) { // Compare
+ break;
+ }
+ queue[pos] = chikey;
+ pos = cpos;
+ }
+ queue[pos] = curkey;
+ return (pos == ipos);
+ }
+
+ /**
+ * Test whether we need to resize to have the requested capacity.
+ *
+ * @param requiredSize required capacity
+ */
+ protected final void resize(int requiredSize) {
+ queue = Arrays.copyOf(queue, desiredSize(requiredSize, queue.length));
+ }
+
+ /**
+ * Delete all elements from the heap.
+ */
+ @Override
+ public void clear() {
+ super.clear();
+ for (int i = 0; i < size; i++) {
+ queue[i] = null;
+ }
+ }
+
+ /**
+ * Compare two objects
+ */
+ abstract protected boolean comp(Object o1, Object o2);
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/TiedTopBoundedHeap.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/TiedTopBoundedHeap.java
index c0ce3acf..2daaafa4 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/TiedTopBoundedHeap.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/TiedTopBoundedHeap.java
@@ -41,11 +41,6 @@ import de.lmu.ifi.dbs.elki.utilities.iterator.MergedIterator;
*/
public class TiedTopBoundedHeap<E> extends TopBoundedHeap<E> {
/**
- * Serial version
- */
- private static final long serialVersionUID = 1L;
-
- /**
* List to keep ties in.
*/
private List<E> ties = new ArrayList<E>();
@@ -80,11 +75,6 @@ public class TiedTopBoundedHeap<E> extends TopBoundedHeap<E> {
ties.clear();
}
- @Override
- public boolean contains(Object o) {
- return ties.contains(o) || super.contains(o);
- }
-
@SuppressWarnings("unchecked")
@Override
public Iterator<E> iterator() {
@@ -93,45 +83,52 @@ public class TiedTopBoundedHeap<E> extends TopBoundedHeap<E> {
@Override
public E peek() {
- if(ties.isEmpty()) {
+ if (ties.isEmpty()) {
return super.peek();
- }
- else {
+ } else {
return ties.get(ties.size() - 1);
}
}
@Override
public E poll() {
- if(ties.isEmpty()) {
+ if (ties.isEmpty()) {
return super.poll();
- }
- else {
+ } else {
return ties.remove(ties.size() - 1);
}
}
@Override
+ public E replaceTopElement(E e) {
+ if (ties.isEmpty()) {
+ return super.replaceTopElement(e);
+ }
+ // Fall back to classic emulation via poll and offer:
+ E prev = poll();
+ add(e);
+ return prev;
+ }
+
+ @Override
protected void handleOverflow(E e) {
boolean tied = false;
- if(comparator == null) {
+ if (comparator == null) {
@SuppressWarnings("unchecked")
Comparable<Object> c = (Comparable<Object>) e;
- if(c.compareTo(queue[0]) == 0) {
+ if (c.compareTo(queue[0]) == 0) {
tied = true;
}
- }
- else {
- if(comparator.compare(e, queue[0]) == 0) {
+ } else {
+ if (comparator.compare(e, queue[0]) == 0) {
tied = true;
}
}
- if(tied) {
+ if (tied) {
ties.add(e);
- }
- else {
+ } else {
// Also remove old ties.
ties.clear();
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/TiedTopBoundedUpdatableHeap.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/TiedTopBoundedUpdatableHeap.java
index 4fbc852e..8e39af1d 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/TiedTopBoundedUpdatableHeap.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/TiedTopBoundedUpdatableHeap.java
@@ -42,11 +42,6 @@ import de.lmu.ifi.dbs.elki.utilities.iterator.MergedIterator;
*/
public class TiedTopBoundedUpdatableHeap<E> extends TopBoundedUpdatableHeap<E> {
/**
- * Serial version
- */
- private static final long serialVersionUID = 1L;
-
- /**
* List to keep ties in.
*/
private List<E> ties = new ArrayList<E>();
@@ -81,11 +76,6 @@ public class TiedTopBoundedUpdatableHeap<E> extends TopBoundedUpdatableHeap<E> {
ties.clear();
}
- @Override
- public boolean contains(Object o) {
- return ties.contains(o) || super.contains(o);
- }
-
@SuppressWarnings("unchecked")
@Override
public Iterator<E> iterator() {
@@ -93,7 +83,7 @@ public class TiedTopBoundedUpdatableHeap<E> extends TopBoundedUpdatableHeap<E> {
}
@Override
- public boolean offerAt(int pos, E e) {
+ public void offerAt(int pos, E e) {
if(pos == IN_TIES) {
for(Iterator<E> i = ties.iterator(); i.hasNext();) {
E e2 = i.next();
@@ -102,8 +92,7 @@ public class TiedTopBoundedUpdatableHeap<E> extends TopBoundedUpdatableHeap<E> {
i.remove();
index.remove(e2);
}
- // while we did not change, this still was "successful".
- return true;
+ return;
}
}
throw new AbortException("Heap corrupt - should not be reached");
@@ -117,9 +106,9 @@ public class TiedTopBoundedUpdatableHeap<E> extends TopBoundedUpdatableHeap<E> {
final E e2 = ties.remove(ties.size() - 1);
// index.remove(e2);
super.offerAt(NO_VALUE, e2);
- return true;
+ return;
}
- return super.offerAt(pos, e);
+ super.offerAt(pos, e);
}
@Override
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/TopBoundedHeap.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/TopBoundedHeap.java
index 5b61e6f3..07b595f6 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/TopBoundedHeap.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/TopBoundedHeap.java
@@ -36,12 +36,7 @@ import java.util.Comparator;
*/
public class TopBoundedHeap<E> extends Heap<E> {
/**
- * Serial version
- */
- private static final long serialVersionUID = 1L;
-
- /**
- * Maximum size
+ * Maximum size.
*/
protected int maxsize;
@@ -67,31 +62,32 @@ public class TopBoundedHeap<E> extends Heap<E> {
}
@Override
- public boolean offer(E e) {
- // don't add if we hit maxsize and are worse
- if(super.size() >= maxsize) {
- ensureValid();
- if(comparator == null) {
- @SuppressWarnings("unchecked")
- Comparable<Object> c = (Comparable<Object>) e;
- if(c.compareTo(queue[0]) < 0) {
- // while we did not change, this still was "successful".
- return true;
- }
- }
- else {
- if(comparator.compare(e, queue[0]) < 0) {
- // while we did not change, this still was "successful".
- return true;
- }
- }
+ public void add(E e) {
+ if (super.size() < maxsize) {
+ // Just offer.
+ super.add(e);
+ return;
}
- boolean result = super.offer(e);
- // purge unneeded entry(s)
- while(super.size() > maxsize) {
- handleOverflow(super.poll());
+ // Peek at the top element, return if we are worse.
+ ensureValid();
+ final int comp;
+ if (comparator == null) {
+ @SuppressWarnings("unchecked")
+ Comparable<Object> c = (Comparable<Object>) e;
+ comp = c.compareTo(queue[0]);
+ } else {
+ comp = comparator.compare(e, queue[0]);
+ }
+ if (comp < 0) {
+ return;
+ }
+ if (comp == 0) {
+ handleOverflow(e);
+ } else {
+ // Otherwise, replace and repair:
+ E prev = super.replaceTopElement(e);
+ handleOverflow(prev);
}
- return result;
}
/**
@@ -105,9 +101,11 @@ public class TopBoundedHeap<E> extends Heap<E> {
}
/**
+ * Get the maximum size.
+ *
* @return the maximum size
*/
public int getMaxSize() {
return maxsize;
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/TopBoundedUpdatableHeap.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/TopBoundedUpdatableHeap.java
index 2b22eba2..75f2abcf 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/TopBoundedUpdatableHeap.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/TopBoundedUpdatableHeap.java
@@ -36,12 +36,7 @@ import java.util.Comparator;
*/
public class TopBoundedUpdatableHeap<E> extends UpdatableHeap<E> {
/**
- * Serial version
- */
- private static final long serialVersionUID = 1L;
-
- /**
- * Maximum size
+ * Maximum size.
*/
protected int maxsize;
@@ -67,22 +62,19 @@ public class TopBoundedUpdatableHeap<E> extends UpdatableHeap<E> {
}
@Override
- public boolean offerAt(int pos, E e) {
+ public void offerAt(int pos, E e) {
// don't add if we hit maxsize and are worse
- if(pos == NO_VALUE && super.size() >= maxsize) {
- ensureValid();
- if(compare(e, queue[0]) < 0) {
- // while we did not change, this still was "successful".
- return true;
- }
- // pos = index.get(e); // Should not be needed.
+ if (pos != NO_VALUE || super.size() < maxsize) {
+ super.offerAt(pos, e);
+ return;
}
- boolean result = super.offerAt(pos, e);
- // purge unneeded entry(s)
- while(super.size() > maxsize) {
- handleOverflow(super.poll());
+ ensureValid();
+ if (compare(e, queue[0]) < 0) {
+ // while we did not change, this still was "successful".
+ return;
}
- return result;
+ E prev = super.replaceTopElement(e);
+ handleOverflow(prev);
}
/**
@@ -93,12 +85,11 @@ public class TopBoundedUpdatableHeap<E> extends UpdatableHeap<E> {
* @return True when an update is needed
*/
protected int compare(Object e, Object object) {
- if(comparator == null) {
+ if (comparator == null) {
@SuppressWarnings("unchecked")
Comparable<Object> c = (Comparable<Object>) e;
return c.compareTo(queue[0]);
- }
- else {
+ } else {
return comparator.compare(e, queue[0]);
}
}
@@ -114,9 +105,11 @@ public class TopBoundedUpdatableHeap<E> extends UpdatableHeap<E> {
}
/**
+ * Get the maximum size.
+ *
* @return the maximum size
*/
public int getMaxSize() {
return maxsize;
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/UpdatableHeap.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/UpdatableHeap.java
index 05858981..1ab5f4df 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/UpdatableHeap.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/UpdatableHeap.java
@@ -37,7 +37,7 @@ import java.util.Comparator;
*/
public class UpdatableHeap<O> extends Heap<O> {
/**
- * Constant for "not in heap"
+ * Constant for "not in heap".
*/
protected static final int NO_VALUE = Integer.MIN_VALUE;
@@ -47,11 +47,6 @@ public class UpdatableHeap<O> extends Heap<O> {
protected static final int IN_TIES = -1;
/**
- * Serial version
- */
- private static final long serialVersionUID = 1L;
-
- /**
* Holds the indices in the heap of each element.
*/
protected final TObjectIntMap<Object> index = new TObjectIntHashMap<Object>(100, 0.5f, NO_VALUE);
@@ -73,7 +68,7 @@ public class UpdatableHeap<O> extends Heap<O> {
}
/**
- * Constructor with comparator
+ * Constructor with comparator.
*
* @param comparator Comparator
*/
@@ -82,7 +77,7 @@ public class UpdatableHeap<O> extends Heap<O> {
}
/**
- * Constructor with predefined size and comparator
+ * Constructor with predefined size and comparator.
*
* @param size Size
* @param comparator Comparator
@@ -98,12 +93,18 @@ public class UpdatableHeap<O> extends Heap<O> {
}
@Override
- public boolean offer(O e) {
+ public void add(O e) {
final int pos = index.get(e);
- return offerAt(pos, e);
+ offerAt(pos, e);
}
- protected boolean offerAt(final int pos, O e) {
+ /**
+ * Offer element at the given position.
+ *
+ * @param pos Position
+ * @param e Element
+ */
+ protected void offerAt(final int pos, O e) {
if(pos == NO_VALUE) {
// resize when needed
if(size + 1 > queue.length) {
@@ -114,9 +115,8 @@ public class UpdatableHeap<O> extends Heap<O> {
index.put(e, size);
size += 1;
// We do NOT YET update the heap. This is done lazily.
- // We have changed - return true according to {@link Collection#put}
- modCount++;
- return true;
+ heapModified();
+ return;
}
else {
assert (pos >= 0) : "Unexpected negative position.";
@@ -126,14 +126,12 @@ public class UpdatableHeap<O> extends Heap<O> {
@SuppressWarnings("unchecked")
Comparable<Object> c = (Comparable<Object>) e;
if(c.compareTo(queue[pos]) >= 0) {
- // Ignore, but return true according to {@link Collection#put}
- return true;
+ return;
}
}
else {
if(comparator.compare(e, queue[pos]) >= 0) {
- // Ignore, but return true according to {@link Collection#put}
- return true;
+ return;
}
}
if(pos >= validSize) {
@@ -144,9 +142,8 @@ public class UpdatableHeap<O> extends Heap<O> {
// ensureValid();
heapifyUp(pos, e);
}
- modCount++;
- // We have changed - return true according to {@link Collection#put}
- return true;
+ heapModified();
+ return;
}
}
@@ -155,7 +152,8 @@ public class UpdatableHeap<O> extends Heap<O> {
if(pos < 0 || pos >= size) {
return null;
}
- final O ret = castQueueElement(pos);
+ @SuppressWarnings("unchecked")
+ final O ret = (O) queue[pos];
// Replacement object:
final Object reinsert = queue[size - 1];
queue[size - 1] = null;
@@ -188,7 +186,7 @@ public class UpdatableHeap<O> extends Heap<O> {
queue[pos] = reinsert;
index.put(reinsert, pos);
}
- modCount++;
+ heapModified();
// Keep index up to date
index.remove(ret);
return ret;
@@ -216,6 +214,13 @@ public class UpdatableHeap<O> extends Heap<O> {
index.remove(node);
return node;
}
+
+ @Override
+ public O replaceTopElement(O e) {
+ O node = super.replaceTopElement(e);
+ index.remove(node);
+ return node;
+ }
/**
* Execute a "Heapify Upwards" aka "SiftUp". Used in insertions.
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/hierarchy/HierarchyHashmapList.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/hierarchy/HierarchyHashmapList.java
index 76bee0f9..bd6d67bf 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/hierarchy/HierarchyHashmapList.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/hierarchy/HierarchyHashmapList.java
@@ -71,7 +71,7 @@ public class HierarchyHashmapList<O> implements ModifiableHierarchy<O> {
if(!pchi.contains(child)) {
pchi.add(child);
} else {
- LoggingUtil.warning("Result added twice: "+parent+" -> "+child);
+ LoggingUtil.warning("Result added twice: "+parent+" -> "+child, new Throwable());
}
}
// Add child to parent
@@ -84,7 +84,7 @@ public class HierarchyHashmapList<O> implements ModifiableHierarchy<O> {
if(!cpar.contains(parent)) {
cpar.add(parent);
} else {
- LoggingUtil.warning("Result added twice: "+parent+" <- "+child);
+ LoggingUtil.warning("Result added twice: "+parent+" <- "+child, new Throwable());
}
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/AbstractObjDynamicHistogram.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/AbstractObjDynamicHistogram.java
new file mode 100644
index 00000000..9d0dba0d
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/AbstractObjDynamicHistogram.java
@@ -0,0 +1,271 @@
+package de.lmu.ifi.dbs.elki.utilities.datastructures.histogram;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+import de.lmu.ifi.dbs.elki.math.scales.LinearScale;
+import de.lmu.ifi.dbs.elki.utilities.BitsUtil;
+
+/**
+ * A dynamic histogram can dynamically adapt the number of bins to the data fed
+ * into the histogram.
+ *
+ * @author Erich Schubert
+ *
+ * @param <T> Data type
+ */
+public abstract class AbstractObjDynamicHistogram<T> extends AbstractObjStaticHistogram<T> {
+ /**
+ * Cache for positions to be inserted.
+ */
+ private double[] cacheposs;
+
+ /**
+ * Cache for data to be inserted.
+ */
+ private Object[] cachevals;
+
+ /**
+ * Cache fill size
+ */
+ private int cachefill;
+
+ /**
+ * Destination (minimum) size of the structure. At most 2*destsize bins are
+ * allowed.
+ */
+ private int destsize;
+
+ /**
+ * Constructor.
+ *
+ * @param bins Design number of bins - may become twice as large!
+ */
+ public AbstractObjDynamicHistogram(int bins) {
+ super(-1, 0.0, 1.0);
+ this.destsize = bins;
+ cacheposs = new double[this.destsize << 1];
+ cachevals = new Object[this.destsize << 1];
+ cachefill = 0;
+ }
+
+ /**
+ * Materialize the histogram from the cache.
+ */
+ @SuppressWarnings("unchecked")
+ void materialize() {
+ // already materialized?
+ if (cachefill <= 0) {
+ return;
+ }
+ // Compute minimum and maximum
+ double min = Double.MAX_VALUE, max = Double.MIN_VALUE;
+ for (int i = 0; i < cachefill; i++) {
+ min = Math.min(min, cacheposs[i]);
+ max = Math.max(max, cacheposs[i]);
+ }
+ // use the LinearScale magic to round to "likely suiteable" step sizes.
+ // TODO: extract into a reusable function?
+ LinearScale scale = new LinearScale(min, max);
+ min = scale.getMin();
+ max = scale.getMax();
+ this.base = min;
+ this.max = max;
+ this.binsize = (max - min) / this.destsize;
+ // initialize array
+ this.data = new Object[this.destsize << 1];
+ for (int i = 0; i < this.destsize; i++) {
+ this.data[i] = makeObject();
+ }
+ size = destsize;
+ // re-insert data we have
+ final int end = cachefill;
+ cachefill = -1; // So reinsert works!
+ for (int i = 0; i < end; i++) {
+ putData(cacheposs[i], (T) cachevals[i]);
+ }
+ // delete cache, signal that we're initialized
+ cacheposs = null;
+ cachevals = null;
+ }
+
+ @Override
+ public T get(double coord) {
+ materialize();
+ testResample(coord);
+ T ret = super.get(coord);
+ return ret;
+ }
+
+ /**
+ * Put fresh data into the histogram (or into the cache)
+ *
+ * @param coord Coordinate
+ * @param value Value
+ */
+ @Override
+ public void putData(double coord, T value) {
+ // Store in cache
+ if (cachefill >= 0) {
+ if (cachefill < cacheposs.length) {
+
+ cacheposs[cachefill] = coord;
+ cachevals[cachefill] = cloneForCache(value);
+ cachefill++;
+ return;
+ } else {
+ materialize();
+ // But continue below!
+ }
+ }
+ // Check if we need to resample to accomodate this bin.
+ testResample(coord);
+ // super class will handle histogram resizing / shifting
+ T exist = get(coord);
+ data[getBinNr(coord)] = aggregate(exist, value);
+ }
+
+ /**
+ * Test (and perform) downsampling when neede.
+ *
+ * @param coord coordinate to accomodate.
+ */
+ private void testResample(double coord) {
+ final int bin = getBinNr(coord);
+ final int sizereq, off;
+ if (bin < 0) {
+ sizereq = size - bin;
+ off = -bin;
+ } else if (bin >= data.length) {
+ sizereq = bin + 1;
+ off = 0;
+ } else {
+ // Within the designated size - nothing to do.
+ return;
+ }
+ if (sizereq < data.length) {
+ // Accomodate by shifting. Let super do the job in {@link #get}
+ return;
+ }
+ // Resampling, eventually by multiple levels.
+ final int levels = BitsUtil.magnitude(sizereq / this.destsize) - 1;
+ assert (levels > 0) : "No resampling required?!?";
+ final int step = 1 << levels;
+
+ final int fixpoint = off / (step - 1);
+ {
+ // Start positions for in-place bottom-up downsampling.
+ int oup = fixpoint;
+ int inp = (oup << levels) - off;
+ assert (-step < inp && inp <= oup && oup < inp + step) : (inp + " -> " + oup + " s=" + step + " o=" + off + " l=" + levels);
+ for (; inp < size; inp += step, oup++) {
+ assert (oup < inp + step && oup < data.length);
+ data[oup] = downsample(data, Math.max(0, inp), Math.min(size, inp + step), step);
+ }
+ // Clean upwards
+ for (; oup < data.length; oup++) {
+ data[oup] = null;
+ }
+ }
+ if (off >= step) {
+ // Start positions for in-place downsampling top-down:
+ int oup = fixpoint - 1;
+ int inp = (oup << levels) - off;
+ assert (oup > inp) : (inp + " -> " + oup + " s=" + step + " o=" + off + " l=" + levels);
+ for (; inp > -step; inp -= step, oup--) {
+ assert (oup >= inp && oup >= 0);
+ data[oup] = downsample(data, Math.max(0, inp), Math.min(size, inp + step), step);
+ }
+ for (; oup >= 0; oup--) {
+ data[oup] = makeObject();
+ }
+ }
+ // recalculate histogram base.
+ base = base - (offset + off) * binsize;
+ offset = 0;
+ size = (size + 1) >> levels;
+ binsize = binsize * (1 << levels);
+ max = base + binsize * size;
+ }
+
+ @Override
+ public Iter iter() {
+ materialize();
+ return super.iter();
+ }
+
+ @Override
+ public int getNumBins() {
+ materialize();
+ return super.getNumBins();
+ }
+
+ @Override
+ public double getBinsize() {
+ materialize();
+ return super.getBinsize();
+ }
+
+ @Override
+ public double getCoverMinimum() {
+ materialize();
+ return super.getCoverMinimum();
+ }
+
+ @Override
+ public double getCoverMaximum() {
+ materialize();
+ return super.getCoverMaximum();
+ }
+
+ /**
+ * Perform downsampling on a number of bins.
+ *
+ * @param data Data array (needs cast!)
+ * @param start Interval start
+ * @param end Interval end (exclusive)
+ * @param size Intended size - extra bins are assumed to be empty, should be a
+ * power of two
+ * @return New bin value
+ */
+ protected abstract T downsample(Object[] data, int start, int end, int size);
+
+ /**
+ * Rule to combine two bins or entries into one.
+ *
+ * Note: first and second MAY be modified and returned, they will not be used
+ * afterwards.
+ *
+ * @param first First bin value
+ * @param second Second bin value
+ * @return combined bin value
+ */
+ protected abstract T aggregate(T first, T second);
+
+ /**
+ * Clone a data passed to the algorithm for computing the initial size.
+ *
+ * @param data Data to be cloned
+ * @return cloned data
+ */
+ protected abstract T cloneForCache(T data);
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/AbstractObjStaticHistogram.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/AbstractObjStaticHistogram.java
new file mode 100644
index 00000000..c1882302
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/AbstractObjStaticHistogram.java
@@ -0,0 +1,129 @@
+package de.lmu.ifi.dbs.elki.utilities.datastructures.histogram;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * Histogram class storing double values.
+ *
+ * The histogram will start with "bin" bins, but it can grow dynamicall to the
+ * left and right.
+ *
+ * @author Erich Schubert
+ *
+ * @param <T> Data type
+ */
+public abstract class AbstractObjStaticHistogram<T> extends AbstractStaticHistogram implements ObjHistogram<T> {
+ /**
+ * Constructor.
+ *
+ * @param bins Number of bins
+ * @param min Cover minimum
+ * @param max Cover maximum
+ */
+ public AbstractObjStaticHistogram(int bins, double min, double max) {
+ super(bins, min, max);
+ if (bins >= 0) {
+ // -1 will be used by FlexiHistogram to delay initialization.
+ data = new Object[bins];
+ }
+ }
+
+ /**
+ * Data store
+ */
+ Object[] data;
+
+ /**
+ * Access the value of a bin with new data.
+ *
+ * @param coord Coordinate
+ * @return bin contents
+ */
+ @SuppressWarnings("unchecked")
+ public T get(double coord) {
+ int bin = getBinNr(coord);
+ if (bin < 0) {
+ if (size - bin > data.length) {
+ // Reallocate. TODO: use an arraylist-like grow strategy!
+ Object[] tmpdata = new Object[growSize(data.length, size - bin)];
+ System.arraycopy(data, 0, tmpdata, -bin, size);
+ data = tmpdata;
+ } else {
+ // Shift in place
+ System.arraycopy(data, 0, data, -bin, size);
+ }
+ for (int i = 0; i < -bin; i++) {
+ data[i] = makeObject();
+ }
+ // Note that bin is negative, -bin is the shift offset!
+ offset -= bin;
+ size -= bin;
+ // TODO: modCounter++; and have iterators fast-fail
+ // Unset max value when resizing
+ max = Double.MAX_VALUE;
+ return (T) data[0];
+ } else if (bin >= size) {
+ if (bin >= data.length) {
+ Object[] tmpdata = new Object[growSize(data.length, bin + 1)];
+ System.arraycopy(data, 0, tmpdata, 0, size);
+ data = tmpdata;
+ }
+ for (int i = size; i <= bin; i++) {
+ data[i] = makeObject();
+ }
+ size = bin + 1;
+ // TODO: modCounter++; and have iterators fast-fail
+ // Unset max value when resizing
+ max = Double.MAX_VALUE;
+ return (T) data[bin];
+ } else {
+ return (T) data[bin];
+ }
+ }
+
+ /**
+ * Class to make a new object for the data store.
+ *
+ * @return New instance.
+ */
+ protected abstract T makeObject();
+
+ @Override
+ public Iter iter() {
+ return new Iter();
+ }
+
+ /**
+ * Iterator class.
+ *
+ * @author Erich Schubert
+ */
+ public class Iter extends AbstractStaticHistogram.Iter implements ObjHistogram.Iter<T> {
+ @SuppressWarnings("unchecked")
+ @Override
+ public T getValue() {
+ return (T) data[bin];
+ }
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/AbstractStaticHistogram.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/AbstractStaticHistogram.java
new file mode 100644
index 00000000..799ac009
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/AbstractStaticHistogram.java
@@ -0,0 +1,220 @@
+package de.lmu.ifi.dbs.elki.utilities.datastructures.histogram;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+/**
+ * Abstract base class for histograms.
+ *
+ * Note that this is abstracted from the actual data storage, so it can be
+ * adapted for multiple use cases.
+ *
+ * @author Erich Schubert
+ */
+public abstract class AbstractStaticHistogram implements Histogram {
+ /**
+ * Array shift to account for negative indices.
+ */
+ protected int offset = 0;
+
+ /**
+ * Size of array storage.
+ */
+ protected int size;
+
+ /**
+ * Array 'base', i.e. the point of 0.0. Usually the minimum.
+ */
+ protected double base;
+
+ /**
+ * To avoid introducing an extra bucket for the maximum value.
+ */
+ protected double max;
+
+ /**
+ * Width of a bin.
+ */
+ protected double binsize;
+
+ /**
+ * Histogram constructor
+ *
+ * @param bins Number of bins to use.
+ * @param min Minimum Value
+ * @param max Maximum Value
+ */
+ public AbstractStaticHistogram(int bins, double min, double max) {
+ this.base = min;
+ this.max = max;
+ this.binsize = (max - min) / bins;
+ this.size = bins;
+ }
+
+ /**
+ * Compute the bin number. Has a special case for rounding max down to the
+ * last bin.
+ *
+ * @param coord Coordinate
+ * @return bin number
+ */
+ protected int getBinNr(double coord) {
+ if (Double.isInfinite(coord) || Double.isNaN(coord)) {
+ throw new UnsupportedOperationException("Encountered non-finite value in Histogram: " + coord);
+ }
+ if (coord == max) {
+ // System.err.println("Triggered special case: "+ (Math.floor((coord -
+ // base) / binsize) + offset) + " vs. " + (size - 1));
+ return size - 1;
+ }
+ return (int) Math.floor((coord - base) / binsize) + offset;
+ }
+
+ /**
+ * Compute the size to grow to.
+ *
+ * @param current Current size
+ * @param requiredSize Required size
+ * @return Size to allocate
+ */
+ protected static int growSize(int current, int requiredSize) {
+ // Double until 64, then increase by 50% each time.
+ int newCapacity = ((current < 64) ? ((current + 1) << 1) : ((current >> 1) * 3));
+ // overflow?
+ if (newCapacity < 0) {
+ throw new OutOfMemoryError();
+ }
+ if (requiredSize > newCapacity) {
+ newCapacity = requiredSize;
+ }
+ return requiredSize;
+ }
+
+ /**
+ * Get the number of bins actually in use.
+ *
+ * @return number of bins
+ */
+ @Override
+ public int getNumBins() {
+ return size;
+ }
+
+ /**
+ * Get the size (width) of a bin.
+ *
+ * @return bin size
+ */
+ @Override
+ public double getBinsize() {
+ return binsize;
+ }
+
+ /**
+ * Get minimum (covered by bins, not data!)
+ *
+ * @return minimum
+ */
+ @Override
+ public double getCoverMinimum() {
+ return base - offset * binsize;
+ }
+
+ /**
+ * Get maximum (covered by bins, not data!)
+ *
+ * @return maximum
+ */
+ @Override
+ public double getCoverMaximum() {
+ return base + (size - offset) * binsize;
+ }
+
+ /**
+ * Get an iterator over all histogram bins.
+ *
+ * @return Iterator
+ */
+ @Override
+ public abstract Iter iter();
+
+ /**
+ * Iterator class to iterate over all bins.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public abstract class Iter implements Histogram.Iter {
+ /**
+ * Current bin number
+ */
+ int bin = 0;
+
+ @Override
+ public double getCenter() {
+ return base + (bin + 0.5 - offset) * binsize;
+ }
+
+ @Override
+ public double getLeft() {
+ return base + (bin - offset) * binsize;
+ }
+
+ @Override
+ public double getRight() {
+ return base + (bin + 1 - offset) * binsize;
+ }
+
+ @Override
+ public boolean valid() {
+ return bin >= 0 && bin < size;
+ }
+
+ @Override
+ public void advance() {
+ bin++;
+ }
+
+ @Override
+ public int getOffset() {
+ return bin;
+ }
+
+ @Override
+ public void advance(int count) {
+ bin += count;
+ }
+
+ @Override
+ public void retract() {
+ bin--;
+ }
+
+ @Override
+ public void seek(int off) {
+ bin = off;
+ }
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/DoubleArrayStaticHistogram.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/DoubleArrayStaticHistogram.java
new file mode 100644
index 00000000..aeba3c4b
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/DoubleArrayStaticHistogram.java
@@ -0,0 +1,85 @@
+package de.lmu.ifi.dbs.elki.utilities.datastructures.histogram;
+
+/*
+This file is part of ELKI:
+Environment for Developing KDD-Applications Supported by Index-Structures
+
+Copyright (C) 2012
+Ludwig-Maximilians-Universität München
+Lehr- und Forschungseinheit für Datenbanksysteme
+ELKI Development Team
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * Histogram containing an array of doubles (i.e. {@code double[]}). This is
+ * actually one of the simpler specializations, as arrays are objects not
+ * primitive in Java.
+ *
+ * @author Erich Schubert
+ */
+public final class DoubleArrayStaticHistogram extends AbstractObjStaticHistogram<double[]> {
+ /**
+ * Desired number of columns in each bin.
+ */
+ private final int cols;
+
+ /**
+ * Constructor.
+ *
+ * @param bins Number of bins
+ * @param min Minimum value for the coordinates
+ * @param max Maximum value for the coordinates
+ * @param cols Number of columns in each bin.
+ */
+ public DoubleArrayStaticHistogram(int bins, double min, double max, int cols) {
+ super(bins, min, max);
+ this.cols = cols;
+ for (int i = 0; i < bins; i++) {
+ data[i] = new double[cols];
+ }
+ }
+
+ /**
+ * Increment histogram by a double[].
+ *
+ * @param coord Coordinate
+ * @param data Data to increment by.
+ */
+ public void increment(double coord, double[] data) {
+ double[] existing = get(coord);
+ for (int i = 0; i < existing.length; i++) {
+ existing[i] += data[i];
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Data is combined by incrementing.
+ *
+ * @deprecated use the explicit {@link #increment} instead.
+ */
+ @Deprecated
+ @Override
+ public void putData(double coord, double[] data) {
+ increment(coord, data);
+ }
+
+ @Override
+ protected double[] makeObject() {
+ return new double[cols];
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/DoubleDynamicHistogram.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/DoubleDynamicHistogram.java
new file mode 100644
index 00000000..77a1f9e4
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/DoubleDynamicHistogram.java
@@ -0,0 +1,248 @@
+package de.lmu.ifi.dbs.elki.utilities.datastructures.histogram;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+import de.lmu.ifi.dbs.elki.math.scales.LinearScale;
+import de.lmu.ifi.dbs.elki.utilities.BitsUtil;
+
+/**
+ * A flexible histogram storing double, that can dynamically adapt the number of
+ * bins to the data fed into the histogram.
+ *
+ * @author Erich Schubert
+ */
+public class DoubleDynamicHistogram extends DoubleStaticHistogram {
+ /**
+ * Cache for data to be inserted.
+ */
+ private double[] cachec;
+
+ /**
+ * Cache for data to be inserted.
+ */
+ private double[] cachev;
+
+ /**
+ * Cache fill size
+ */
+ private int cachefill;
+
+ /**
+ * Destination (minimum) size of the structure.
+ * At most destsize * 2 bins are allowed.
+ */
+ private int destsize;
+
+ /**
+ * Constructor.
+ *
+ * @param bins Design number of bins - may become twice as large!
+ */
+ public DoubleDynamicHistogram(int bins) {
+ super(-1, 0.0, 1.0);
+ this.destsize = bins;
+ cachec = new double[this.destsize << CACHE_SHIFT];
+ cachev = new double[this.destsize << CACHE_SHIFT];
+ cachefill = 0;
+ }
+
+ /**
+ * Materialize the histogram from the cache.
+ */
+ void materialize() {
+ // already materialized?
+ if (cachefill < 0) {
+ return;
+ }
+ // Compute minimum and maximum
+ double min = Double.MAX_VALUE, max = Double.MIN_VALUE;
+ for (int i = 0; i < cachefill; i++) {
+ min = Math.min(min, cachec[i]);
+ max = Math.max(max, cachec[i]);
+ }
+ // use the LinearScale magic to round to "likely suiteable" step sizes.
+ // TODO: extract into a reusable function?
+ LinearScale scale = new LinearScale(min, max);
+ min = scale.getMin();
+ max = scale.getMax();
+ this.base = min;
+ this.max = max;
+ this.binsize = (max - min) / this.destsize;
+ // initialize array
+ this.data = new double[this.destsize << 1];
+ size = destsize;
+ // re-insert data we have
+ final int end = cachefill;
+ cachefill = -1; // So reinsert works!
+ for (int i = 0; i < end; i++) {
+ increment(cachec[i], cachev[i]);
+ }
+ // delete cache, signal that we're initialized
+ cachec = null;
+ cachev = null;
+ }
+
+ @Override
+ public double get(double coord) {
+ materialize();
+ testResample(coord);
+ return super.get(coord);
+ }
+
+ /**
+ * Put fresh data into the histogram (or into the cache)
+ *
+ * @param coord Coordinate
+ * @param value Value
+ */
+ @Override
+ public void increment(double coord, double value) {
+ // Store in cache
+ if (cachefill >= 0) {
+ if (cachefill < cachec.length) {
+ cachec[cachefill] = coord;
+ cachev[cachefill] = value;
+ cachefill ++;
+ return;
+ } else {
+ materialize();
+ // But continue below!
+ }
+ }
+ // Check if we need to resample to accomodate this bin.
+ testResample(coord);
+ // super class will handle histogram resizing / shifting
+ super.increment(coord, value);
+ }
+
+ /**
+ * Test (and perform) downsampling when neede.
+ *
+ * @param coord coordinate to accomodate.
+ */
+ private void testResample(double coord) {
+ final int bin = getBinNr(coord);
+ final int sizereq, off;
+ if (bin < 0) {
+ sizereq = size - bin;
+ off = -bin;
+ } else if (bin >= data.length) {
+ sizereq = bin + 1;
+ off = 0;
+ } else {
+ // Within the designated size - nothing to do.
+ return;
+ }
+ if (sizereq < data.length) {
+ // Accomodate by shifting. Let super do the job in {@link #get}
+ return;
+ }
+ // Resampling, eventually by multiple levels.
+ final int levels = BitsUtil.magnitude(sizereq / this.destsize) - 1;
+ assert (levels > 0) : "No resampling required?!? sizereq=" + sizereq + " destsize=" + destsize + " array=" + data.length;
+ final int step = 1 << levels;
+
+ final int fixpoint = off / (step - 1);
+ {
+ // Start positions for in-place bottom-up downsampling.
+ int oup = fixpoint;
+ int inp = (oup << levels) - off;
+ assert (-step < inp && inp <= oup && oup < inp + step) : (inp + " -> " + oup + " s=" + step + " o=" + off + " l=" + levels);
+ for (; inp < size; inp += step, oup++) {
+ assert (oup < inp + step && oup < data.length);
+ data[oup] = downsample(data, Math.max(0, inp), Math.min(size, inp + step), step);
+ }
+ // Clean upwards
+ for (; oup < data.length; oup++) {
+ data[oup] = 0;
+ }
+ }
+ if (off > 0) {
+ // Start positions for in-place downsampling top-down:
+ int oup = fixpoint - 1;
+ int inp = (oup << levels) - off;
+ assert (oup > inp) : (inp + " -> " + oup + " s=" + step + " o=" + off + " l=" + levels);
+ for (; inp > -step; inp -= step, oup--) {
+ assert (oup >= inp && oup >= 0);
+ data[oup] = downsample(data, Math.max(0, inp), Math.min(size, inp + step), step);
+ }
+ for (; oup >= 0; oup--) {
+ data[oup] = 0;
+ }
+ }
+ // recalculate histogram base.
+ base = base - (offset + off) * binsize;
+ offset = 0;
+ size = (size + 1) >> levels;
+ binsize = binsize * (1 << levels);
+ max = base + binsize * size;
+ }
+
+ @Override
+ public Iter iter() {
+ materialize();
+ return super.iter();
+ }
+
+ @Override
+ public int getNumBins() {
+ materialize();
+ return super.getNumBins();
+ }
+
+ @Override
+ public double getBinsize() {
+ materialize();
+ return super.getBinsize();
+ }
+
+ @Override
+ public double getCoverMinimum() {
+ materialize();
+ return super.getCoverMinimum();
+ }
+
+ @Override
+ public double getCoverMaximum() {
+ materialize();
+ return super.getCoverMaximum();
+ }
+
+ /**
+ * Perform downsampling on a number of bins.
+ *
+ * @param data Data array (needs cast!)
+ * @param start Interval start
+ * @param end Interval end (exclusive)
+ * @param size Intended size - extra bins are assumed to be empty, should be a
+ * power of two
+ * @return New bin value
+ */
+ protected double downsample(double[] data, int start, int end, int size) {
+ double sum = 0;
+ for (int i = start; i < end; i++) {
+ sum += data[i];
+ }
+ return sum;
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/DoubleHistogram.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/DoubleHistogram.java
new file mode 100644
index 00000000..d5cee785
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/DoubleHistogram.java
@@ -0,0 +1,58 @@
+package de.lmu.ifi.dbs.elki.utilities.datastructures.histogram;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * Histogram storing double values.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.has Iter
+ */
+public interface DoubleHistogram extends Histogram {
+ /**
+ * Increment the histogram at a given position.
+ *
+ * @param coord Coordinate
+ * @param value Value to increment by
+ */
+ public void increment(double coord, double value);
+
+ @Override
+ public Iter iter();
+
+ /**
+ * Iterator interface.
+ *
+ * @author Erich Schubert
+ */
+ public static interface Iter extends Histogram.Iter {
+ /**
+ * Get the value of the bin.
+ *
+ * @return Bin value
+ */
+ public double getValue();
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/DoubleStaticHistogram.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/DoubleStaticHistogram.java
new file mode 100644
index 00000000..db839d10
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/DoubleStaticHistogram.java
@@ -0,0 +1,132 @@
+package de.lmu.ifi.dbs.elki.utilities.datastructures.histogram;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.Arrays;
+
+/**
+ * Histogram class storing double values.
+ *
+ * The histogram will start with "bin" bins, but it can grow dynamically to the
+ * left and right.
+ *
+ * @author Erich Schubert
+ */
+public class DoubleStaticHistogram extends AbstractStaticHistogram implements DoubleHistogram {
+ /**
+ * Constructor.
+ *
+ * @param bins Number of bins
+ * @param min Cover minimum
+ * @param max Cover maximum
+ */
+ public DoubleStaticHistogram(int bins, double min, double max) {
+ super(bins, min, max);
+ if (bins >= 0) {
+ data = new double[bins];
+ } else {
+ data = null;
+ }
+ }
+
+ /**
+ * Data store
+ */
+ double[] data;
+
+ /**
+ * Increment the value of a bin.
+ *
+ * @param coord Coordinate
+ * @param val Value
+ */
+ @Override
+ public void increment(double coord, double val) {
+ int bin = getBinNr(coord);
+ if (bin < 0) {
+ if (size - bin > data.length) {
+ // Reallocate. TODO: use an arraylist-like grow strategy!
+ double[] tmpdata = new double[growSize(data.length, size - bin)];
+ System.arraycopy(data, 0, tmpdata, -bin, size);
+ data = tmpdata;
+ } else {
+ // Shift in place and clear head
+ System.arraycopy(data, 0, data, -bin, size);
+ Arrays.fill(data, 0, -bin, (double) 0);
+ }
+ data[0] = val;
+ // Note that bin is negative, -bin is the shift offset!
+ assert (data.length >= size - bin);
+ offset -= bin;
+ size -= bin;
+ // TODO: modCounter++; and have iterators fast-fail
+ } else if (bin >= data.length) {
+ double[] tmpdata = new double[growSize(data.length, bin + 1)];
+ System.arraycopy(data, 0, tmpdata, 0, size);
+ tmpdata[bin] = val;
+ data = tmpdata;
+ size = bin + 1;
+ // TODO: modCounter++; and have iterators fast-fail
+ // Unset max value when resizing
+ max = Double.MAX_VALUE;
+ } else {
+ if (bin >= size) {
+ // TODO: reset bins to 0 first?
+ size = bin + 1;
+ }
+ data[bin] += val;
+ }
+ }
+
+ /**
+ * Get the value at a particular position.
+ *
+ * @param coord Coordinate
+ * @return Value
+ */
+ public double get(double coord) {
+ int bin = getBinNr(coord);
+ if (bin < 0 || bin >= size) {
+ return 0;
+ }
+ return data[bin];
+ }
+
+ @Override
+ public Iter iter() {
+ return new Iter();
+ }
+
+ /**
+ * Iterator class.
+ *
+ * @author Erich Schubert
+ */
+ public class Iter extends AbstractStaticHistogram.Iter implements DoubleHistogram.Iter {
+ @Override
+ public double getValue() {
+ return data[bin];
+ }
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/FloatDynamicHistogram.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/FloatDynamicHistogram.java
new file mode 100644
index 00000000..a14ed00a
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/FloatDynamicHistogram.java
@@ -0,0 +1,248 @@
+package de.lmu.ifi.dbs.elki.utilities.datastructures.histogram;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+import de.lmu.ifi.dbs.elki.math.scales.LinearScale;
+import de.lmu.ifi.dbs.elki.utilities.BitsUtil;
+
+/**
+ * A flexible histogram storing float, that can dynamically adapt the number of
+ * bins to the data fed into the histogram.
+ *
+ * @author Erich Schubert
+ */
+public class FloatDynamicHistogram extends FloatStaticHistogram {
+ /**
+ * Cache for data to be inserted.
+ */
+ private double[] cachec;
+
+ /**
+ * Cache for data to be inserted.
+ */
+ private float[] cachev;
+
+ /**
+ * Cache fill size
+ */
+ private int cachefill;
+
+ /**
+ * Destination (minimum) size of the structure.
+ * At most destsize * 2 bins are allowed.
+ */
+ private int destsize;
+
+ /**
+ * Constructor.
+ *
+ * @param bins Design number of bins - may become twice as large!
+ */
+ public FloatDynamicHistogram(int bins) {
+ super(-1, 0.0, 1.0);
+ this.destsize = bins;
+ cachec = new double[this.destsize << CACHE_SHIFT];
+ cachev = new float[this.destsize << CACHE_SHIFT];
+ cachefill = 0;
+ }
+
+ /**
+ * Materialize the histogram from the cache.
+ */
+ void materialize() {
+ // already materialized?
+ if (cachefill < 0) {
+ return;
+ }
+ // Compute minimum and maximum
+ double min = Double.MAX_VALUE, max = Double.MIN_VALUE;
+ for (int i = 0; i < cachefill; i++) {
+ min = Math.min(min, cachec[i]);
+ max = Math.max(max, cachec[i]);
+ }
+ // use the LinearScale magic to round to "likely suiteable" step sizes.
+ // TODO: extract into a reusable function?
+ LinearScale scale = new LinearScale(min, max);
+ min = scale.getMin();
+ max = scale.getMax();
+ this.base = min;
+ this.max = max;
+ this.binsize = (max - min) / this.destsize;
+ // initialize array
+ this.data = new float[this.destsize << 1];
+ size = destsize;
+ // re-insert data we have
+ final int end = cachefill;
+ cachefill = -1; // So reinsert works!
+ for (int i = 0; i < end; i++) {
+ increment(cachec[i], cachev[i]);
+ }
+ // delete cache, signal that we're initialized
+ cachec = null;
+ cachev = null;
+ }
+
+ @Override
+ public float get(double coord) {
+ materialize();
+ testResample(coord);
+ return super.get(coord);
+ }
+
+ /**
+ * Put fresh data into the histogram (or into the cache)
+ *
+ * @param coord Coordinate
+ * @param value Value
+ */
+ @Override
+ public void increment(double coord, float value) {
+ // Store in cache
+ if (cachefill >= 0) {
+ if (cachefill < cachec.length) {
+ cachec[cachefill] = coord;
+ cachev[cachefill] = value;
+ cachefill ++;
+ return;
+ } else {
+ materialize();
+ // But continue below!
+ }
+ }
+ // Check if we need to resample to accomodate this bin.
+ testResample(coord);
+ // super class will handle histogram resizing / shifting
+ super.increment(coord, value);
+ }
+
+ /**
+ * Test (and perform) downsampling when neede.
+ *
+ * @param coord coordinate to accomodate.
+ */
+ private void testResample(double coord) {
+ final int bin = getBinNr(coord);
+ final int sizereq, off;
+ if (bin < 0) {
+ sizereq = size - bin;
+ off = -bin;
+ } else if (bin >= data.length) {
+ sizereq = bin + 1;
+ off = 0;
+ } else {
+ // Within the designated size - nothing to do.
+ return;
+ }
+ if (sizereq < data.length) {
+ // Accomodate by shifting. Let super do the job in {@link #get}
+ return;
+ }
+ // Resampling, eventually by multiple levels.
+ final int levels = BitsUtil.magnitude(sizereq / this.destsize) - 1;
+ assert (levels > 0) : "No resampling required?!? sizereq=" + sizereq + " destsize=" + destsize + " array=" + data.length;
+ final int step = 1 << levels;
+
+ final int fixpoint = off / (step - 1);
+ {
+ // Start positions for in-place bottom-up downsampling.
+ int oup = fixpoint;
+ int inp = (oup << levels) - off;
+ assert (-step < inp && inp <= oup && oup < inp + step) : (inp + " -> " + oup + " s=" + step + " o=" + off + " l=" + levels);
+ for (; inp < size; inp += step, oup++) {
+ assert (oup < inp + step && oup < data.length);
+ data[oup] = downsample(data, Math.max(0, inp), Math.min(size, inp + step), step);
+ }
+ // Clean upwards
+ for (; oup < data.length; oup++) {
+ data[oup] = 0;
+ }
+ }
+ if (off > 0) {
+ // Start positions for in-place downsampling top-down:
+ int oup = fixpoint - 1;
+ int inp = (oup << levels) - off;
+ assert (oup > inp) : (inp + " -> " + oup + " s=" + step + " o=" + off + " l=" + levels);
+ for (; inp > -step; inp -= step, oup--) {
+ assert (oup >= inp && oup >= 0);
+ data[oup] = downsample(data, Math.max(0, inp), Math.min(size, inp + step), step);
+ }
+ for (; oup >= 0; oup--) {
+ data[oup] = 0;
+ }
+ }
+ // recalculate histogram base.
+ base = base - (offset + off) * binsize;
+ offset = 0;
+ size = (size + 1) >> levels;
+ binsize = binsize * (1 << levels);
+ max = base + binsize * size;
+ }
+
+ @Override
+ public Iter iter() {
+ materialize();
+ return super.iter();
+ }
+
+ @Override
+ public int getNumBins() {
+ materialize();
+ return super.getNumBins();
+ }
+
+ @Override
+ public double getBinsize() {
+ materialize();
+ return super.getBinsize();
+ }
+
+ @Override
+ public double getCoverMinimum() {
+ materialize();
+ return super.getCoverMinimum();
+ }
+
+ @Override
+ public double getCoverMaximum() {
+ materialize();
+ return super.getCoverMaximum();
+ }
+
+ /**
+ * Perform downsampling on a number of bins.
+ *
+ * @param data Data array (needs cast!)
+ * @param start Interval start
+ * @param end Interval end (exclusive)
+ * @param size Intended size - extra bins are assumed to be empty, should be a
+ * power of two
+ * @return New bin value
+ */
+ protected float downsample(float[] data, int start, int end, int size) {
+ float sum = 0;
+ for (int i = start; i < end; i++) {
+ sum += data[i];
+ }
+ return sum;
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/FloatHistogram.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/FloatHistogram.java
new file mode 100644
index 00000000..f5a65bfa
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/FloatHistogram.java
@@ -0,0 +1,58 @@
+package de.lmu.ifi.dbs.elki.utilities.datastructures.histogram;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * Histogram storing float values.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.has Iter
+ */
+public interface FloatHistogram extends Histogram {
+ /**
+ * Increment the histogram at a given position.
+ *
+ * @param coord Coordinate
+ * @param value Value to increment by
+ */
+ public void increment(double coord, float value);
+
+ @Override
+ public Iter iter();
+
+ /**
+ * Iterator interface.
+ *
+ * @author Erich Schubert
+ */
+ public static interface Iter extends Histogram.Iter {
+ /**
+ * Get the value of the bin.
+ *
+ * @return Bin value
+ */
+ public float getValue();
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/FloatStaticHistogram.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/FloatStaticHistogram.java
new file mode 100644
index 00000000..b3f41994
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/FloatStaticHistogram.java
@@ -0,0 +1,132 @@
+package de.lmu.ifi.dbs.elki.utilities.datastructures.histogram;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.Arrays;
+
+/**
+ * Histogram class storing float values.
+ *
+ * The histogram will start with "bin" bins, but it can grow dynamically to the
+ * left and right.
+ *
+ * @author Erich Schubert
+ */
+public class FloatStaticHistogram extends AbstractStaticHistogram implements FloatHistogram {
+ /**
+ * Constructor.
+ *
+ * @param bins Number of bins
+ * @param min Cover minimum
+ * @param max Cover maximum
+ */
+ public FloatStaticHistogram(int bins, double min, double max) {
+ super(bins, min, max);
+ if (bins >= 0) {
+ data = new float[bins];
+ } else {
+ data = null;
+ }
+ }
+
+ /**
+ * Data store
+ */
+ float[] data;
+
+ /**
+ * Increment the value of a bin.
+ *
+ * @param coord Coordinate
+ * @param val Value
+ */
+ @Override
+ public void increment(double coord, float val) {
+ int bin = getBinNr(coord);
+ if (bin < 0) {
+ if (size - bin > data.length) {
+ // Reallocate. TODO: use an arraylist-like grow strategy!
+ float[] tmpdata = new float[growSize(data.length, size - bin)];
+ System.arraycopy(data, 0, tmpdata, -bin, size);
+ data = tmpdata;
+ } else {
+ // Shift in place and clear head
+ System.arraycopy(data, 0, data, -bin, size);
+ Arrays.fill(data, 0, -bin, (float) 0);
+ }
+ data[0] = val;
+ // Note that bin is negative, -bin is the shift offset!
+ assert (data.length >= size - bin);
+ offset -= bin;
+ size -= bin;
+ // TODO: modCounter++; and have iterators fast-fail
+ } else if (bin >= data.length) {
+ float[] tmpdata = new float[growSize(data.length, bin + 1)];
+ System.arraycopy(data, 0, tmpdata, 0, size);
+ tmpdata[bin] = val;
+ data = tmpdata;
+ size = bin + 1;
+ // TODO: modCounter++; and have iterators fast-fail
+ // Unset max value when resizing
+ max = Double.MAX_VALUE;
+ } else {
+ if (bin >= size) {
+ // TODO: reset bins to 0 first?
+ size = bin + 1;
+ }
+ data[bin] += val;
+ }
+ }
+
+ /**
+ * Get the value at a particular position.
+ *
+ * @param coord Coordinate
+ * @return Value
+ */
+ public float get(double coord) {
+ int bin = getBinNr(coord);
+ if (bin < 0 || bin >= size) {
+ return 0;
+ }
+ return data[bin];
+ }
+
+ @Override
+ public Iter iter() {
+ return new Iter();
+ }
+
+ /**
+ * Iterator class.
+ *
+ * @author Erich Schubert
+ */
+ public class Iter extends AbstractStaticHistogram.Iter implements FloatHistogram.Iter {
+ @Override
+ public float getValue() {
+ return data[bin];
+ }
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/Histogram.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/Histogram.java
new file mode 100644
index 00000000..75be6830
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/Histogram.java
@@ -0,0 +1,105 @@
+package de.lmu.ifi.dbs.elki.utilities.datastructures.histogram;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import de.lmu.ifi.dbs.elki.utilities.iterator.ArrayIter;
+
+/**
+ * Abstract API for histograms. Without specific type information, to allow this
+ * to be shared for primitive types, too!
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.has Iter
+ */
+public interface Histogram {
+ /**
+ * This parameter controls the cache size used for dynamic histograms before
+ * setting the initial thresholds.
+ */
+ public final static int CACHE_SHIFT = 2;
+
+ /**
+ * Get the number of bins actually in use.
+ *
+ * @return number of bins
+ */
+ public abstract int getNumBins();
+
+ /**
+ * Get the size (width) of a bin.
+ *
+ * @return bin size
+ */
+ public abstract double getBinsize();
+
+ /**
+ * Get minimum (covered by bins, not data!)
+ *
+ * @return minimum
+ */
+ public abstract double getCoverMinimum();
+
+ /**
+ * Get maximum (covered by bins, not data!)
+ *
+ * @return maximum
+ */
+ public abstract double getCoverMaximum();
+
+ /**
+ * Get an iterator over all histogram bins.
+ *
+ * @return Iterator
+ */
+ public abstract Iter iter();
+
+ /**
+ * Array iterator.
+ *
+ * @author Erich Schubert
+ */
+ public static interface Iter extends ArrayIter {
+ /**
+ * Get the bin center.
+ *
+ * @return bin center value
+ */
+ public double getCenter();
+
+ /**
+ * Get the bin minimum.
+ *
+ * @return bin left value
+ */
+ public double getLeft();
+
+ /**
+ * Get the bin maximum.
+ *
+ * @return bin right value
+ */
+ public double getRight();
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/IntArrayStaticHistogram.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/IntArrayStaticHistogram.java
new file mode 100644
index 00000000..8d00604b
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/IntArrayStaticHistogram.java
@@ -0,0 +1,85 @@
+package de.lmu.ifi.dbs.elki.utilities.datastructures.histogram;
+
+/*
+This file is part of ELKI:
+Environment for Developing KDD-Applications Supported by Index-Structures
+
+Copyright (C) 2012
+Ludwig-Maximilians-Universität München
+Lehr- und Forschungseinheit für Datenbanksysteme
+ELKI Development Team
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * Histogram containing an array of ints (i.e. {@code int[]}). This is
+ * actually one of the simpler specializations, as arrays are objects not
+ * primitive in Java.
+ *
+ * @author Erich Schubert
+ */
+public final class IntArrayStaticHistogram extends AbstractObjStaticHistogram<int[]> {
+ /**
+ * Desired number of columns in each bin.
+ */
+ private final int cols;
+
+ /**
+ * Constructor.
+ *
+ * @param bins Number of bins
+ * @param min Minimum value for the coordinates
+ * @param max Maximum value for the coordinates
+ * @param cols Number of columns in each bin.
+ */
+ public IntArrayStaticHistogram(int bins, double min, double max, int cols) {
+ super(bins, min, max);
+ this.cols = cols;
+ for (int i = 0; i < bins; i++) {
+ data[i] = new int[cols];
+ }
+ }
+
+ /**
+ * Increment histogram by a double[].
+ *
+ * @param coord Coordinate
+ * @param data Data to increment by.
+ */
+ public void increment(double coord, int[] data) {
+ int[] existing = get(coord);
+ for (int i = 0; i < existing.length; i++) {
+ existing[i] += data[i];
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Data is combined by incrementing.
+ *
+ * @deprecated use the explicit {@link #increment} instead.
+ */
+ @Deprecated
+ @Override
+ public void putData(double coord, int[] data) {
+ increment(coord, data);
+ }
+
+ @Override
+ protected int[] makeObject() {
+ return new int[cols];
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/IntDynamicHistogram.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/IntDynamicHistogram.java
new file mode 100644
index 00000000..0967ebd5
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/IntDynamicHistogram.java
@@ -0,0 +1,248 @@
+package de.lmu.ifi.dbs.elki.utilities.datastructures.histogram;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+import de.lmu.ifi.dbs.elki.math.scales.LinearScale;
+import de.lmu.ifi.dbs.elki.utilities.BitsUtil;
+
+/**
+ * A flexible histogram storing int, that can dynamically adapt the number of
+ * bins to the data fed into the histogram.
+ *
+ * @author Erich Schubert
+ */
+public class IntDynamicHistogram extends IntStaticHistogram {
+ /**
+ * Cache for data to be inserted.
+ */
+ private double[] cachec;
+
+ /**
+ * Cache for data to be inserted.
+ */
+ private int[] cachev;
+
+ /**
+ * Cache fill size
+ */
+ private int cachefill;
+
+ /**
+ * Destination (minimum) size of the structure.
+ * At most destsize * 2 bins are allowed.
+ */
+ private int destsize;
+
+ /**
+ * Constructor.
+ *
+ * @param bins Design number of bins - may become twice as large!
+ */
+ public IntDynamicHistogram(int bins) {
+ super(-1, 0.0, 1.0);
+ this.destsize = bins;
+ cachec = new double[this.destsize << CACHE_SHIFT];
+ cachev = new int[this.destsize << CACHE_SHIFT];
+ cachefill = 0;
+ }
+
+ /**
+ * Materialize the histogram from the cache.
+ */
+ void materialize() {
+ // already materialized?
+ if (cachefill < 0) {
+ return;
+ }
+ // Compute minimum and maximum
+ double min = Double.MAX_VALUE, max = Double.MIN_VALUE;
+ for (int i = 0; i < cachefill; i++) {
+ min = Math.min(min, cachec[i]);
+ max = Math.max(max, cachec[i]);
+ }
+ // use the LinearScale magic to round to "likely suiteable" step sizes.
+ // TODO: extract into a reusable function?
+ LinearScale scale = new LinearScale(min, max);
+ min = scale.getMin();
+ max = scale.getMax();
+ this.base = min;
+ this.max = max;
+ this.binsize = (max - min) / this.destsize;
+ // initialize array
+ this.data = new int[this.destsize << 1];
+ size = destsize;
+ // re-insert data we have
+ final int end = cachefill;
+ cachefill = -1; // So reinsert works!
+ for (int i = 0; i < end; i++) {
+ increment(cachec[i], cachev[i]);
+ }
+ // delete cache, signal that we're initialized
+ cachec = null;
+ cachev = null;
+ }
+
+ @Override
+ public int get(double coord) {
+ materialize();
+ testResample(coord);
+ return super.get(coord);
+ }
+
+ /**
+ * Put fresh data into the histogram (or into the cache)
+ *
+ * @param coord Coordinate
+ * @param value Value
+ */
+ @Override
+ public void increment(double coord, int value) {
+ // Store in cache
+ if (cachefill >= 0) {
+ if (cachefill < cachec.length) {
+ cachec[cachefill] = coord;
+ cachev[cachefill] = value;
+ cachefill ++;
+ return;
+ } else {
+ materialize();
+ // But continue below!
+ }
+ }
+ // Check if we need to resample to accomodate this bin.
+ testResample(coord);
+ // super class will handle histogram resizing / shifting
+ super.increment(coord, value);
+ }
+
+ /**
+ * Test (and perform) downsampling when neede.
+ *
+ * @param coord coordinate to accomodate.
+ */
+ private void testResample(double coord) {
+ final int bin = getBinNr(coord);
+ final int sizereq, off;
+ if (bin < 0) {
+ sizereq = size - bin;
+ off = -bin;
+ } else if (bin >= data.length) {
+ sizereq = bin + 1;
+ off = 0;
+ } else {
+ // Within the designated size - nothing to do.
+ return;
+ }
+ if (sizereq < data.length) {
+ // Accomodate by shifting. Let super do the job in {@link #get}
+ return;
+ }
+ // Resampling, eventually by multiple levels.
+ final int levels = BitsUtil.magnitude(sizereq / this.destsize) - 1;
+ assert (levels > 0) : "No resampling required?!? sizereq=" + sizereq + " destsize=" + destsize + " array=" + data.length;
+ final int step = 1 << levels;
+
+ final int fixpoint = off / (step - 1);
+ {
+ // Start positions for in-place bottom-up downsampling.
+ int oup = fixpoint;
+ int inp = (oup << levels) - off;
+ assert (-step < inp && inp <= oup && oup < inp + step) : (inp + " -> " + oup + " s=" + step + " o=" + off + " l=" + levels);
+ for (; inp < size; inp += step, oup++) {
+ assert (oup < inp + step && oup < data.length);
+ data[oup] = downsample(data, Math.max(0, inp), Math.min(size, inp + step), step);
+ }
+ // Clean upwards
+ for (; oup < data.length; oup++) {
+ data[oup] = 0;
+ }
+ }
+ if (off > 0) {
+ // Start positions for in-place downsampling top-down:
+ int oup = fixpoint - 1;
+ int inp = (oup << levels) - off;
+ assert (oup > inp) : (inp + " -> " + oup + " s=" + step + " o=" + off + " l=" + levels);
+ for (; inp > -step; inp -= step, oup--) {
+ assert (oup >= inp && oup >= 0);
+ data[oup] = downsample(data, Math.max(0, inp), Math.min(size, inp + step), step);
+ }
+ for (; oup >= 0; oup--) {
+ data[oup] = 0;
+ }
+ }
+ // recalculate histogram base.
+ base = base - (offset + off) * binsize;
+ offset = 0;
+ size = (size + 1) >> levels;
+ binsize = binsize * (1 << levels);
+ max = base + binsize * size;
+ }
+
+ @Override
+ public Iter iter() {
+ materialize();
+ return super.iter();
+ }
+
+ @Override
+ public int getNumBins() {
+ materialize();
+ return super.getNumBins();
+ }
+
+ @Override
+ public double getBinsize() {
+ materialize();
+ return super.getBinsize();
+ }
+
+ @Override
+ public double getCoverMinimum() {
+ materialize();
+ return super.getCoverMinimum();
+ }
+
+ @Override
+ public double getCoverMaximum() {
+ materialize();
+ return super.getCoverMaximum();
+ }
+
+ /**
+ * Perform downsampling on a number of bins.
+ *
+ * @param data Data array (needs cast!)
+ * @param start Interval start
+ * @param end Interval end (exclusive)
+ * @param size Intended size - extra bins are assumed to be empty, should be a
+ * power of two
+ * @return New bin value
+ */
+ protected int downsample(int[] data, int start, int end, int size) {
+ int sum = 0;
+ for (int i = start; i < end; i++) {
+ sum += data[i];
+ }
+ return sum;
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/IntHistogram.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/IntHistogram.java
new file mode 100644
index 00000000..9ec4ec56
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/IntHistogram.java
@@ -0,0 +1,58 @@
+package de.lmu.ifi.dbs.elki.utilities.datastructures.histogram;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * Histogram storing int values.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.has Iter
+ */
+public interface IntHistogram extends Histogram {
+ /**
+ * Increment the histogram at a given position.
+ *
+ * @param coord Coordinate
+ * @param value Value to increment by
+ */
+ public void increment(double coord, int value);
+
+ @Override
+ public Iter iter();
+
+ /**
+ * Iterator interface.
+ *
+ * @author Erich Schubert
+ */
+ public static interface Iter extends Histogram.Iter {
+ /**
+ * Get the value of the bin.
+ *
+ * @return Bin value
+ */
+ public int getValue();
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/IntStaticHistogram.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/IntStaticHistogram.java
new file mode 100644
index 00000000..d4de36d7
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/IntStaticHistogram.java
@@ -0,0 +1,132 @@
+package de.lmu.ifi.dbs.elki.utilities.datastructures.histogram;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.Arrays;
+
+/**
+ * Histogram class storing int values.
+ *
+ * The histogram will start with "bin" bins, but it can grow dynamically to the
+ * left and right.
+ *
+ * @author Erich Schubert
+ */
+public class IntStaticHistogram extends AbstractStaticHistogram implements IntHistogram {
+ /**
+ * Constructor.
+ *
+ * @param bins Number of bins
+ * @param min Cover minimum
+ * @param max Cover maximum
+ */
+ public IntStaticHistogram(int bins, double min, double max) {
+ super(bins, min, max);
+ if (bins >= 0) {
+ data = new int[bins];
+ } else {
+ data = null;
+ }
+ }
+
+ /**
+ * Data store
+ */
+ int[] data;
+
+ /**
+ * Increment the value of a bin.
+ *
+ * @param coord Coordinate
+ * @param val Value
+ */
+ @Override
+ public void increment(double coord, int val) {
+ int bin = getBinNr(coord);
+ if (bin < 0) {
+ if (size - bin > data.length) {
+ // Reallocate. TODO: use an arraylist-like grow strategy!
+ int[] tmpdata = new int[growSize(data.length, size - bin)];
+ System.arraycopy(data, 0, tmpdata, -bin, size);
+ data = tmpdata;
+ } else {
+ // Shift in place and clear head
+ System.arraycopy(data, 0, data, -bin, size);
+ Arrays.fill(data, 0, -bin, (int) 0);
+ }
+ data[0] = val;
+ // Note that bin is negative, -bin is the shift offset!
+ assert (data.length >= size - bin);
+ offset -= bin;
+ size -= bin;
+ // TODO: modCounter++; and have iterators fast-fail
+ } else if (bin >= data.length) {
+ int[] tmpdata = new int[growSize(data.length, bin + 1)];
+ System.arraycopy(data, 0, tmpdata, 0, size);
+ tmpdata[bin] = val;
+ data = tmpdata;
+ size = bin + 1;
+ // TODO: modCounter++; and have iterators fast-fail
+ // Unset max value when resizing
+ max = Double.MAX_VALUE;
+ } else {
+ if (bin >= size) {
+ // TODO: reset bins to 0 first?
+ size = bin + 1;
+ }
+ data[bin] += val;
+ }
+ }
+
+ /**
+ * Get the value at a particular position.
+ *
+ * @param coord Coordinate
+ * @return Value
+ */
+ public int get(double coord) {
+ int bin = getBinNr(coord);
+ if (bin < 0 || bin >= size) {
+ return 0;
+ }
+ return data[bin];
+ }
+
+ @Override
+ public Iter iter() {
+ return new Iter();
+ }
+
+ /**
+ * Iterator class.
+ *
+ * @author Erich Schubert
+ */
+ public class Iter extends AbstractStaticHistogram.Iter implements IntHistogram.Iter {
+ @Override
+ public int getValue() {
+ return data[bin];
+ }
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/LongArrayStaticHistogram.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/LongArrayStaticHistogram.java
new file mode 100644
index 00000000..efbf751f
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/LongArrayStaticHistogram.java
@@ -0,0 +1,85 @@
+package de.lmu.ifi.dbs.elki.utilities.datastructures.histogram;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * Histogram containing an array of longs (i.e. {@code long[]}). This is
+ * actually one of the simpler specializations, as arrays are objects not
+ * primitive in Java.
+ *
+ * @author Erich Schubert
+ */
+public final class LongArrayStaticHistogram extends AbstractObjStaticHistogram<long[]> {
+ /**
+ * Desired number of columns in each bin.
+ */
+ private final int cols;
+
+ /**
+ * Constructor.
+ *
+ * @param bins Number of bins
+ * @param min Minimum value for the coordinates
+ * @param max Maximum value for the coordinates
+ * @param cols Number of columns in each bin.
+ */
+ public LongArrayStaticHistogram(int bins, double min, double max, int cols) {
+ super(bins, min, max);
+ this.cols = cols;
+ for (int i = 0; i < bins; i++) {
+ data[i] = new long[cols];
+ }
+ }
+
+ /**
+ * Increment histogram by a double[].
+ *
+ * @param coord Coordinate
+ * @param data Data to increment by.
+ */
+ public void increment(double coord, long[] data) {
+ long[] existing = get(coord);
+ for (int i = 0; i < existing.length; i++) {
+ existing[i] += data[i];
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Data is combined by incrementing.
+ *
+ * @deprecated use the explicit {@link #increment} instead.
+ */
+ @Deprecated
+ @Override
+ public void putData(double coord, long[] data) {
+ increment(coord, data);
+ }
+
+ @Override
+ protected long[] makeObject() {
+ return new long[cols];
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/LongDynamicHistogram.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/LongDynamicHistogram.java
new file mode 100644
index 00000000..676a5e8f
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/LongDynamicHistogram.java
@@ -0,0 +1,248 @@
+package de.lmu.ifi.dbs.elki.utilities.datastructures.histogram;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+import de.lmu.ifi.dbs.elki.math.scales.LinearScale;
+import de.lmu.ifi.dbs.elki.utilities.BitsUtil;
+
+/**
+ * A flexible histogram storing long, that can dynamically adapt the number of
+ * bins to the data fed into the histogram.
+ *
+ * @author Erich Schubert
+ */
+public class LongDynamicHistogram extends LongStaticHistogram {
+ /**
+ * Cache for data to be inserted.
+ */
+ private double[] cachec;
+
+ /**
+ * Cache for data to be inserted.
+ */
+ private long[] cachev;
+
+ /**
+ * Cache fill size
+ */
+ private int cachefill;
+
+ /**
+ * Destination (minimum) size of the structure.
+ * At most destsize * 2 bins are allowed.
+ */
+ private int destsize;
+
+ /**
+ * Constructor.
+ *
+ * @param bins Design number of bins - may become twice as large!
+ */
+ public LongDynamicHistogram(int bins) {
+ super(-1, 0.0, 1.0);
+ this.destsize = bins;
+ cachec = new double[this.destsize << CACHE_SHIFT];
+ cachev = new long[this.destsize << CACHE_SHIFT];
+ cachefill = 0;
+ }
+
+ /**
+ * Materialize the histogram from the cache.
+ */
+ void materialize() {
+ // already materialized?
+ if (cachefill < 0) {
+ return;
+ }
+ // Compute minimum and maximum
+ double min = Double.MAX_VALUE, max = Double.MIN_VALUE;
+ for (int i = 0; i < cachefill; i++) {
+ min = Math.min(min, cachec[i]);
+ max = Math.max(max, cachec[i]);
+ }
+ // use the LinearScale magic to round to "likely suiteable" step sizes.
+ // TODO: extract into a reusable function?
+ LinearScale scale = new LinearScale(min, max);
+ min = scale.getMin();
+ max = scale.getMax();
+ this.base = min;
+ this.max = max;
+ this.binsize = (max - min) / this.destsize;
+ // initialize array
+ this.data = new long[this.destsize << 1];
+ size = destsize;
+ // re-insert data we have
+ final int end = cachefill;
+ cachefill = -1; // So reinsert works!
+ for (int i = 0; i < end; i++) {
+ increment(cachec[i], cachev[i]);
+ }
+ // delete cache, signal that we're initialized
+ cachec = null;
+ cachev = null;
+ }
+
+ @Override
+ public long get(double coord) {
+ materialize();
+ testResample(coord);
+ return super.get(coord);
+ }
+
+ /**
+ * Put fresh data into the histogram (or into the cache)
+ *
+ * @param coord Coordinate
+ * @param value Value
+ */
+ @Override
+ public void increment(double coord, long value) {
+ // Store in cache
+ if (cachefill >= 0) {
+ if (cachefill < cachec.length) {
+ cachec[cachefill] = coord;
+ cachev[cachefill] = value;
+ cachefill ++;
+ return;
+ } else {
+ materialize();
+ // But continue below!
+ }
+ }
+ // Check if we need to resample to accomodate this bin.
+ testResample(coord);
+ // super class will handle histogram resizing / shifting
+ super.increment(coord, value);
+ }
+
+ /**
+ * Test (and perform) downsampling when neede.
+ *
+ * @param coord coordinate to accomodate.
+ */
+ private void testResample(double coord) {
+ final int bin = getBinNr(coord);
+ final int sizereq, off;
+ if (bin < 0) {
+ sizereq = size - bin;
+ off = -bin;
+ } else if (bin >= data.length) {
+ sizereq = bin + 1;
+ off = 0;
+ } else {
+ // Within the designated size - nothing to do.
+ return;
+ }
+ if (sizereq < data.length) {
+ // Accomodate by shifting. Let super do the job in {@link #get}
+ return;
+ }
+ // Resampling, eventually by multiple levels.
+ final int levels = BitsUtil.magnitude(sizereq / this.destsize) - 1;
+ assert (levels > 0) : "No resampling required?!? sizereq=" + sizereq + " destsize=" + destsize + " array=" + data.length;
+ final int step = 1 << levels;
+
+ final int fixpoint = off / (step - 1);
+ {
+ // Start positions for in-place bottom-up downsampling.
+ int oup = fixpoint;
+ int inp = (oup << levels) - off;
+ assert (-step < inp && inp <= oup && oup < inp + step) : (inp + " -> " + oup + " s=" + step + " o=" + off + " l=" + levels);
+ for (; inp < size; inp += step, oup++) {
+ assert (oup < inp + step && oup < data.length);
+ data[oup] = downsample(data, Math.max(0, inp), Math.min(size, inp + step), step);
+ }
+ // Clean upwards
+ for (; oup < data.length; oup++) {
+ data[oup] = 0;
+ }
+ }
+ if (off > 0) {
+ // Start positions for in-place downsampling top-down:
+ int oup = fixpoint - 1;
+ int inp = (oup << levels) - off;
+ assert (oup > inp) : (inp + " -> " + oup + " s=" + step + " o=" + off + " l=" + levels);
+ for (; inp > -step; inp -= step, oup--) {
+ assert (oup >= inp && oup >= 0);
+ data[oup] = downsample(data, Math.max(0, inp), Math.min(size, inp + step), step);
+ }
+ for (; oup >= 0; oup--) {
+ data[oup] = 0;
+ }
+ }
+ // recalculate histogram base.
+ base = base - (offset + off) * binsize;
+ offset = 0;
+ size = (size + 1) >> levels;
+ binsize = binsize * (1 << levels);
+ max = base + binsize * size;
+ }
+
+ @Override
+ public Iter iter() {
+ materialize();
+ return super.iter();
+ }
+
+ @Override
+ public int getNumBins() {
+ materialize();
+ return super.getNumBins();
+ }
+
+ @Override
+ public double getBinsize() {
+ materialize();
+ return super.getBinsize();
+ }
+
+ @Override
+ public double getCoverMinimum() {
+ materialize();
+ return super.getCoverMinimum();
+ }
+
+ @Override
+ public double getCoverMaximum() {
+ materialize();
+ return super.getCoverMaximum();
+ }
+
+ /**
+ * Perform downsampling on a number of bins.
+ *
+ * @param data Data array (needs cast!)
+ * @param start Interval start
+ * @param end Interval end (exclusive)
+ * @param size Intended size - extra bins are assumed to be empty, should be a
+ * power of two
+ * @return New bin value
+ */
+ protected long downsample(long[] data, int start, int end, int size) {
+ long sum = 0;
+ for (int i = start; i < end; i++) {
+ sum += data[i];
+ }
+ return sum;
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/LongHistogram.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/LongHistogram.java
new file mode 100644
index 00000000..9be15e65
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/LongHistogram.java
@@ -0,0 +1,58 @@
+package de.lmu.ifi.dbs.elki.utilities.datastructures.histogram;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * Histogram storing long values.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.has Iter
+ */
+public interface LongHistogram extends Histogram {
+ /**
+ * Increment the histogram at a given position.
+ *
+ * @param coord Coordinate
+ * @param value Value to increment by
+ */
+ public void increment(double coord, long value);
+
+ @Override
+ public Iter iter();
+
+ /**
+ * Iterator interface.
+ *
+ * @author Erich Schubert
+ */
+ public static interface Iter extends Histogram.Iter {
+ /**
+ * Get the value of the bin.
+ *
+ * @return Bin value
+ */
+ public long getValue();
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/LongStaticHistogram.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/LongStaticHistogram.java
new file mode 100644
index 00000000..63e15599
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/LongStaticHistogram.java
@@ -0,0 +1,132 @@
+package de.lmu.ifi.dbs.elki.utilities.datastructures.histogram;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.Arrays;
+
+/**
+ * Histogram class storing long values.
+ *
+ * The histogram will start with "bin" bins, but it can grow dynamically to the
+ * left and right.
+ *
+ * @author Erich Schubert
+ */
+public class LongStaticHistogram extends AbstractStaticHistogram implements LongHistogram {
+ /**
+ * Constructor.
+ *
+ * @param bins Number of bins
+ * @param min Cover minimum
+ * @param max Cover maximum
+ */
+ public LongStaticHistogram(int bins, double min, double max) {
+ super(bins, min, max);
+ if (bins >= 0) {
+ data = new long[bins];
+ } else {
+ data = null;
+ }
+ }
+
+ /**
+ * Data store
+ */
+ long[] data;
+
+ /**
+ * Increment the value of a bin.
+ *
+ * @param coord Coordinate
+ * @param val Value
+ */
+ @Override
+ public void increment(double coord, long val) {
+ int bin = getBinNr(coord);
+ if (bin < 0) {
+ if (size - bin > data.length) {
+ // Reallocate. TODO: use an arraylist-like grow strategy!
+ long[] tmpdata = new long[growSize(data.length, size - bin)];
+ System.arraycopy(data, 0, tmpdata, -bin, size);
+ data = tmpdata;
+ } else {
+ // Shift in place and clear head
+ System.arraycopy(data, 0, data, -bin, size);
+ Arrays.fill(data, 0, -bin, (long) 0);
+ }
+ data[0] = val;
+ // Note that bin is negative, -bin is the shift offset!
+ assert (data.length >= size - bin);
+ offset -= bin;
+ size -= bin;
+ // TODO: modCounter++; and have iterators fast-fail
+ } else if (bin >= data.length) {
+ long[] tmpdata = new long[growSize(data.length, bin + 1)];
+ System.arraycopy(data, 0, tmpdata, 0, size);
+ tmpdata[bin] = val;
+ data = tmpdata;
+ size = bin + 1;
+ // TODO: modCounter++; and have iterators fast-fail
+ // Unset max value when resizing
+ max = Double.MAX_VALUE;
+ } else {
+ if (bin >= size) {
+ // TODO: reset bins to 0 first?
+ size = bin + 1;
+ }
+ data[bin] += val;
+ }
+ }
+
+ /**
+ * Get the value at a particular position.
+ *
+ * @param coord Coordinate
+ * @return Value
+ */
+ public long get(double coord) {
+ int bin = getBinNr(coord);
+ if (bin < 0 || bin >= size) {
+ return 0;
+ }
+ return data[bin];
+ }
+
+ @Override
+ public Iter iter() {
+ return new Iter();
+ }
+
+ /**
+ * Iterator class.
+ *
+ * @author Erich Schubert
+ */
+ public class Iter extends AbstractStaticHistogram.Iter implements LongHistogram.Iter {
+ @Override
+ public long getValue() {
+ return data[bin];
+ }
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/MeanVarianceStaticHistogram.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/MeanVarianceStaticHistogram.java
new file mode 100644
index 00000000..2a464382
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/MeanVarianceStaticHistogram.java
@@ -0,0 +1,80 @@
+package de.lmu.ifi.dbs.elki.utilities.datastructures.histogram;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import de.lmu.ifi.dbs.elki.math.MeanVariance;
+
+/**
+ * Histogram class storing MeanVaraince object.
+ *
+ * The histogram will start with "bin" bins, but it can grow dynamically to the
+ * left and right.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.composedOf MeanVariance
+ */
+public class MeanVarianceStaticHistogram extends AbstractObjStaticHistogram<MeanVariance> {
+ /**
+ * Constructor.
+ *
+ * @param bins Number of bins
+ * @param min Cover minimum
+ * @param max Cover maximum
+ */
+ public MeanVarianceStaticHistogram(int bins, double min, double max) {
+ super(bins, min, max);
+ }
+
+ /**
+ * Data store
+ */
+ MeanVariance[] data;
+
+ /**
+ * Update the value of a bin with new data.
+ *
+ * @param coord Coordinate
+ * @param val Value
+ */
+ public void put(double coord, double val) {
+ get(coord).put(val);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Data is combined by using {@link MeanVariance#put(Mean)}.
+ */
+ @Override
+ public void putData(double coord, MeanVariance data) {
+ get(coord).put(data);
+ }
+
+
+ @Override
+ protected MeanVariance makeObject() {
+ return new MeanVariance();
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/ObjHistogram.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/ObjHistogram.java
new file mode 100644
index 00000000..ac4d4e4b
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/ObjHistogram.java
@@ -0,0 +1,69 @@
+package de.lmu.ifi.dbs.elki.utilities.datastructures.histogram;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * Basic interface for object based histograms (static and flexible).
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.has Iter
+ *
+ * @param <T> data type
+ */
+public interface ObjHistogram<T> extends Histogram {
+ /**
+ * Get a histogram iterator.
+ *
+ * @return Iterator
+ */
+ @Override
+ public Iter<T> iter();
+
+ /**
+ * Aggregate new data into the histogram.
+ *
+ * Note that the actual definition of "aggregate" depends on the application.
+ * While a static histogram will likely perform this immediately, a flexible
+ * histogram will cache a number of observations.
+ *
+ * @param coord Coordinate
+ * @param data Data
+ */
+ public void putData(double coord, T data);
+
+ /**
+ * Histogram iterator.
+ *
+ * @author Erich Schubert
+ */
+ public static interface Iter<T> extends Histogram.Iter {
+ /**
+ * Get the value of the bin.
+ *
+ * @return Bin value
+ */
+ public T getValue();
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/ShortDynamicHistogram.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/ShortDynamicHistogram.java
new file mode 100644
index 00000000..ff94928b
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/ShortDynamicHistogram.java
@@ -0,0 +1,248 @@
+package de.lmu.ifi.dbs.elki.utilities.datastructures.histogram;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+import de.lmu.ifi.dbs.elki.math.scales.LinearScale;
+import de.lmu.ifi.dbs.elki.utilities.BitsUtil;
+
+/**
+ * A flexible histogram storing short, that can dynamically adapt the number of
+ * bins to the data fed into the histogram.
+ *
+ * @author Erich Schubert
+ */
+public class ShortDynamicHistogram extends ShortStaticHistogram {
+ /**
+ * Cache for data to be inserted.
+ */
+ private double[] cachec;
+
+ /**
+ * Cache for data to be inserted.
+ */
+ private short[] cachev;
+
+ /**
+ * Cache fill size
+ */
+ private int cachefill;
+
+ /**
+ * Destination (minimum) size of the structure.
+ * At most destsize * 2 bins are allowed.
+ */
+ private int destsize;
+
+ /**
+ * Constructor.
+ *
+ * @param bins Design number of bins - may become twice as large!
+ */
+ public ShortDynamicHistogram(int bins) {
+ super(-1, 0.0, 1.0);
+ this.destsize = bins;
+ cachec = new double[this.destsize << CACHE_SHIFT];
+ cachev = new short[this.destsize << CACHE_SHIFT];
+ cachefill = 0;
+ }
+
+ /**
+ * Materialize the histogram from the cache.
+ */
+ void materialize() {
+ // already materialized?
+ if (cachefill < 0) {
+ return;
+ }
+ // Compute minimum and maximum
+ double min = Double.MAX_VALUE, max = Double.MIN_VALUE;
+ for (int i = 0; i < cachefill; i++) {
+ min = Math.min(min, cachec[i]);
+ max = Math.max(max, cachec[i]);
+ }
+ // use the LinearScale magic to round to "likely suiteable" step sizes.
+ // TODO: extract into a reusable function?
+ LinearScale scale = new LinearScale(min, max);
+ min = scale.getMin();
+ max = scale.getMax();
+ this.base = min;
+ this.max = max;
+ this.binsize = (max - min) / this.destsize;
+ // initialize array
+ this.data = new short[this.destsize << 1];
+ size = destsize;
+ // re-insert data we have
+ final int end = cachefill;
+ cachefill = -1; // So reinsert works!
+ for (int i = 0; i < end; i++) {
+ increment(cachec[i], cachev[i]);
+ }
+ // delete cache, signal that we're initialized
+ cachec = null;
+ cachev = null;
+ }
+
+ @Override
+ public short get(double coord) {
+ materialize();
+ testResample(coord);
+ return super.get(coord);
+ }
+
+ /**
+ * Put fresh data into the histogram (or into the cache)
+ *
+ * @param coord Coordinate
+ * @param value Value
+ */
+ @Override
+ public void increment(double coord, short value) {
+ // Store in cache
+ if (cachefill >= 0) {
+ if (cachefill < cachec.length) {
+ cachec[cachefill] = coord;
+ cachev[cachefill] = value;
+ cachefill ++;
+ return;
+ } else {
+ materialize();
+ // But continue below!
+ }
+ }
+ // Check if we need to resample to accomodate this bin.
+ testResample(coord);
+ // super class will handle histogram resizing / shifting
+ super.increment(coord, value);
+ }
+
+ /**
+ * Test (and perform) downsampling when neede.
+ *
+ * @param coord coordinate to accomodate.
+ */
+ private void testResample(double coord) {
+ final int bin = getBinNr(coord);
+ final int sizereq, off;
+ if (bin < 0) {
+ sizereq = size - bin;
+ off = -bin;
+ } else if (bin >= data.length) {
+ sizereq = bin + 1;
+ off = 0;
+ } else {
+ // Within the designated size - nothing to do.
+ return;
+ }
+ if (sizereq < data.length) {
+ // Accomodate by shifting. Let super do the job in {@link #get}
+ return;
+ }
+ // Resampling, eventually by multiple levels.
+ final int levels = BitsUtil.magnitude(sizereq / this.destsize) - 1;
+ assert (levels > 0) : "No resampling required?!? sizereq=" + sizereq + " destsize=" + destsize + " array=" + data.length;
+ final int step = 1 << levels;
+
+ final int fixpoint = off / (step - 1);
+ {
+ // Start positions for in-place bottom-up downsampling.
+ int oup = fixpoint;
+ int inp = (oup << levels) - off;
+ assert (-step < inp && inp <= oup && oup < inp + step) : (inp + " -> " + oup + " s=" + step + " o=" + off + " l=" + levels);
+ for (; inp < size; inp += step, oup++) {
+ assert (oup < inp + step && oup < data.length);
+ data[oup] = downsample(data, Math.max(0, inp), Math.min(size, inp + step), step);
+ }
+ // Clean upwards
+ for (; oup < data.length; oup++) {
+ data[oup] = 0;
+ }
+ }
+ if (off > 0) {
+ // Start positions for in-place downsampling top-down:
+ int oup = fixpoint - 1;
+ int inp = (oup << levels) - off;
+ assert (oup > inp) : (inp + " -> " + oup + " s=" + step + " o=" + off + " l=" + levels);
+ for (; inp > -step; inp -= step, oup--) {
+ assert (oup >= inp && oup >= 0);
+ data[oup] = downsample(data, Math.max(0, inp), Math.min(size, inp + step), step);
+ }
+ for (; oup >= 0; oup--) {
+ data[oup] = 0;
+ }
+ }
+ // recalculate histogram base.
+ base = base - (offset + off) * binsize;
+ offset = 0;
+ size = (size + 1) >> levels;
+ binsize = binsize * (1 << levels);
+ max = base + binsize * size;
+ }
+
+ @Override
+ public Iter iter() {
+ materialize();
+ return super.iter();
+ }
+
+ @Override
+ public int getNumBins() {
+ materialize();
+ return super.getNumBins();
+ }
+
+ @Override
+ public double getBinsize() {
+ materialize();
+ return super.getBinsize();
+ }
+
+ @Override
+ public double getCoverMinimum() {
+ materialize();
+ return super.getCoverMinimum();
+ }
+
+ @Override
+ public double getCoverMaximum() {
+ materialize();
+ return super.getCoverMaximum();
+ }
+
+ /**
+ * Perform downsampling on a number of bins.
+ *
+ * @param data Data array (needs cast!)
+ * @param start Interval start
+ * @param end Interval end (exclusive)
+ * @param size Intended size - extra bins are assumed to be empty, should be a
+ * power of two
+ * @return New bin value
+ */
+ protected short downsample(short[] data, int start, int end, int size) {
+ short sum = 0;
+ for (int i = start; i < end; i++) {
+ sum += data[i];
+ }
+ return sum;
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/ShortHistogram.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/ShortHistogram.java
new file mode 100644
index 00000000..699df896
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/ShortHistogram.java
@@ -0,0 +1,58 @@
+package de.lmu.ifi.dbs.elki.utilities.datastructures.histogram;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * Histogram storing short values.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.has Iter
+ */
+public interface ShortHistogram extends Histogram {
+ /**
+ * Increment the histogram at a given position.
+ *
+ * @param coord Coordinate
+ * @param value Value to increment by
+ */
+ public void increment(double coord, short value);
+
+ @Override
+ public Iter iter();
+
+ /**
+ * Iterator interface.
+ *
+ * @author Erich Schubert
+ */
+ public static interface Iter extends Histogram.Iter {
+ /**
+ * Get the value of the bin.
+ *
+ * @return Bin value
+ */
+ public short getValue();
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/ShortStaticHistogram.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/ShortStaticHistogram.java
new file mode 100644
index 00000000..b2809e1e
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/ShortStaticHistogram.java
@@ -0,0 +1,132 @@
+package de.lmu.ifi.dbs.elki.utilities.datastructures.histogram;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.Arrays;
+
+/**
+ * Histogram class storing short values.
+ *
+ * The histogram will start with "bin" bins, but it can grow dynamically to the
+ * left and right.
+ *
+ * @author Erich Schubert
+ */
+public class ShortStaticHistogram extends AbstractStaticHistogram implements ShortHistogram {
+ /**
+ * Constructor.
+ *
+ * @param bins Number of bins
+ * @param min Cover minimum
+ * @param max Cover maximum
+ */
+ public ShortStaticHistogram(int bins, double min, double max) {
+ super(bins, min, max);
+ if (bins >= 0) {
+ data = new short[bins];
+ } else {
+ data = null;
+ }
+ }
+
+ /**
+ * Data store
+ */
+ short[] data;
+
+ /**
+ * Increment the value of a bin.
+ *
+ * @param coord Coordinate
+ * @param val Value
+ */
+ @Override
+ public void increment(double coord, short val) {
+ int bin = getBinNr(coord);
+ if (bin < 0) {
+ if (size - bin > data.length) {
+ // Reallocate. TODO: use an arraylist-like grow strategy!
+ short[] tmpdata = new short[growSize(data.length, size - bin)];
+ System.arraycopy(data, 0, tmpdata, -bin, size);
+ data = tmpdata;
+ } else {
+ // Shift in place and clear head
+ System.arraycopy(data, 0, data, -bin, size);
+ Arrays.fill(data, 0, -bin, (short) 0);
+ }
+ data[0] = val;
+ // Note that bin is negative, -bin is the shift offset!
+ assert (data.length >= size - bin);
+ offset -= bin;
+ size -= bin;
+ // TODO: modCounter++; and have iterators fast-fail
+ } else if (bin >= data.length) {
+ short[] tmpdata = new short[growSize(data.length, bin + 1)];
+ System.arraycopy(data, 0, tmpdata, 0, size);
+ tmpdata[bin] = val;
+ data = tmpdata;
+ size = bin + 1;
+ // TODO: modCounter++; and have iterators fast-fail
+ // Unset max value when resizing
+ max = Double.MAX_VALUE;
+ } else {
+ if (bin >= size) {
+ // TODO: reset bins to 0 first?
+ size = bin + 1;
+ }
+ data[bin] += val;
+ }
+ }
+
+ /**
+ * Get the value at a particular position.
+ *
+ * @param coord Coordinate
+ * @return Value
+ */
+ public short get(double coord) {
+ int bin = getBinNr(coord);
+ if (bin < 0 || bin >= size) {
+ return 0;
+ }
+ return data[bin];
+ }
+
+ @Override
+ public Iter iter() {
+ return new Iter();
+ }
+
+ /**
+ * Iterator class.
+ *
+ * @author Erich Schubert
+ */
+ public class Iter extends AbstractStaticHistogram.Iter implements ShortHistogram.Iter {
+ @Override
+ public short getValue() {
+ return data[bin];
+ }
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/package-info.java b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/package-info.java
new file mode 100644
index 00000000..65dd6446
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/package-info.java
@@ -0,0 +1,34 @@
+/**
+ * <p>Classes for computing histograms.</p>
+ *
+ * This package contains two families of histograms. Static histograms have a fixed initial number of bins.
+ * When encountering values outside of their range, they will grow similar to an ArrayList by adding additional bins.
+ *
+ * "Dynamic" histograms are more useful when you do not know the value range of the data:
+ * they start by collecting a number of sample data, then use this to estimate the initial histogram range.
+ * If they grow to twice their initial size they will downsample, to keep the histogram size in the bounds n to 2n-1,
+ * which effectively limits the memory use and histogram complexity.
+ */
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package de.lmu.ifi.dbs.elki.utilities.datastructures.histogram; \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/documentation/DocumentationUtil.java b/src/de/lmu/ifi/dbs/elki/utilities/documentation/DocumentationUtil.java
index 1bdef265..0fa869f4 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/documentation/DocumentationUtil.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/documentation/DocumentationUtil.java
@@ -43,7 +43,7 @@ public final class DocumentationUtil {
*/
public static String getTitle(Class<?> c) {
Title title = c.getAnnotation(Title.class);
- if(title != null && title.value() != "") {
+ if(title != null && title.value().length() > 0) {
return title.value();
}
return c.getSimpleName();
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/ensemble/EnsembleVoting.java b/src/de/lmu/ifi/dbs/elki/utilities/ensemble/EnsembleVoting.java
new file mode 100644
index 00000000..a83517f0
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/ensemble/EnsembleVoting.java
@@ -0,0 +1,42 @@
+package de.lmu.ifi.dbs.elki.utilities.ensemble;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable;
+
+/**
+ * Interface for ensemble voting rules
+ *
+ * @author Erich Schubert
+ */
+public interface EnsembleVoting extends Parameterizable {
+ /**
+ * Combine scores function. Note: it is assumed that the scores are
+ * comparable.
+ *
+ * @param scores Scores to combine
+ * @return combined score.
+ */
+ public double combine(double[] scores);
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/ensemble/EnsembleVotingBayes.java b/src/de/lmu/ifi/dbs/elki/utilities/ensemble/EnsembleVotingBayes.java
new file mode 100644
index 00000000..7263513b
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/ensemble/EnsembleVotingBayes.java
@@ -0,0 +1,106 @@
+package de.lmu.ifi.dbs.elki.utilities.ensemble;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterEqualConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.LessConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.DoubleParameter;
+
+/**
+ * Combination rule based on Bayes theorems.
+ *
+ * Note: this assumes that the scores are probabilistic!
+ *
+ * @author Erich Schubert
+ */
+public class EnsembleVotingBayes implements EnsembleVoting {
+ /**
+ * Minimum vote to cast
+ */
+ private double minvote = 0.05;
+
+ /**
+ * Constructor.
+ *
+ * @param minvote Minimum vote to cast (0 to 0.5)
+ */
+ public EnsembleVotingBayes(double minvote) {
+ this.minvote = minvote;
+ }
+
+ @Override
+ public double combine(double[] scores) {
+ double pos = 1.0;
+ double neg = 1.0;
+ for (double score : scores) {
+ if (score < minvote) {
+ score = minvote;
+ } else if (score > 1.0 - minvote) {
+ score = 1.0 - minvote;
+ }
+ pos *= score;
+ neg *= (1.0 - score);
+ }
+ return pos / (pos + neg);
+ }
+
+ /**
+ * Parameterization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public static class Parameterizer extends AbstractParameterizer {
+ /**
+ * Option ID for the minimum and maximum vote
+ */
+ public static final OptionID MIN_ID = new OptionID("ensemble.bayes.min", "Minimum (and maximum) vote share, in the range 0 to 0.5");
+
+ /**
+ * Minimum vote to cast
+ */
+ private double minvote = 0.05;
+
+ @Override
+ protected void makeOptions(Parameterization config) {
+ super.makeOptions(config);
+ DoubleParameter minvoteP = new DoubleParameter(MIN_ID, 0.05);
+ minvoteP.addConstraint(new GreaterEqualConstraint(0.0));
+ minvoteP.addConstraint(new LessConstraint(0.5));
+
+ if (config.grab(minvoteP)) {
+ minvote = minvoteP.getValue();
+ }
+ }
+
+ @Override
+ protected EnsembleVotingBayes makeInstance() {
+ return new EnsembleVotingBayes(minvote);
+ }
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/ensemble/EnsembleVotingMax.java b/src/de/lmu/ifi/dbs/elki/utilities/ensemble/EnsembleVotingMax.java
new file mode 100644
index 00000000..b467e9f6
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/ensemble/EnsembleVotingMax.java
@@ -0,0 +1,48 @@
+package de.lmu.ifi.dbs.elki.utilities.ensemble;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * Simple combination rule, by taking the maximum.
+ *
+ * @author Erich Schubert
+ */
+public class EnsembleVotingMax implements EnsembleVoting {
+ /**
+ * Constructor, adhering to
+ * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
+ */
+ public EnsembleVotingMax() {
+ // empty
+ }
+
+ @Override
+ public double combine(double[] scores) {
+ double max = Double.NEGATIVE_INFINITY;
+ for (double val : scores) {
+ max = Math.max(max, val);
+ }
+ return max;
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/ensemble/EnsembleVotingMean.java b/src/de/lmu/ifi/dbs/elki/utilities/ensemble/EnsembleVotingMean.java
new file mode 100644
index 00000000..20746927
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/ensemble/EnsembleVotingMean.java
@@ -0,0 +1,48 @@
+package de.lmu.ifi.dbs.elki.utilities.ensemble;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * Simple combination rule, by taking the mean
+ *
+ * @author Erich Schubert
+ */
+public class EnsembleVotingMean implements EnsembleVoting {
+ /**
+ * Constructor, adhering to
+ * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
+ */
+ public EnsembleVotingMean() {
+ // Empty.
+ }
+
+ @Override
+ public double combine(double[] scores) {
+ double sum = 0.0;
+ for (double score : scores) {
+ sum += score;
+ }
+ return sum / scores.length;
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/ensemble/EnsembleVotingMedian.java b/src/de/lmu/ifi/dbs/elki/utilities/ensemble/EnsembleVotingMedian.java
new file mode 100644
index 00000000..fa1b3fa6
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/ensemble/EnsembleVotingMedian.java
@@ -0,0 +1,95 @@
+package de.lmu.ifi.dbs.elki.utilities.ensemble;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import de.lmu.ifi.dbs.elki.utilities.datastructures.QuickSelect;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterEqualConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.LessEqualConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.DoubleParameter;
+
+/**
+ * Simple combination rule, by taking the median.
+ *
+ * Note: median is very similar to a <em>majority voting</em>!
+ *
+ * @author Erich Schubert
+ */
+public class EnsembleVotingMedian implements EnsembleVoting {
+ /**
+ * Quantile to use
+ */
+ private double quantile = 0.5;
+
+ /**
+ * Constructor.
+ *
+ * @param quantile Quantile
+ */
+ public EnsembleVotingMedian(double quantile) {
+ this.quantile = quantile;
+ }
+
+ @Override
+ public double combine(double[] scores) {
+ return QuickSelect.quantile(scores, quantile);
+ }
+
+ /**
+ * Parameterization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public static class Parameterizer extends AbstractParameterizer {
+ /**
+ * Option ID for the quantile
+ */
+ public static final OptionID QUANTILE_ID = new OptionID("ensemble.median.quantile", "Quantile to use in median voting.");
+
+ /**
+ * Quantile to use
+ */
+ private double quantile = 0.5;
+
+ @Override
+ protected void makeOptions(Parameterization config) {
+ super.makeOptions(config);
+ DoubleParameter quantileP = new DoubleParameter(QUANTILE_ID, 0.5);
+ quantileP.addConstraint(new GreaterEqualConstraint(0.0));
+ quantileP.addConstraint(new LessEqualConstraint(1.0));
+ if (config.grab(quantileP)) {
+ quantile = quantileP.getValue();
+ }
+ }
+
+ @Override
+ protected EnsembleVotingMedian makeInstance() {
+ return new EnsembleVotingMedian(quantile);
+ }
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/ensemble/EnsembleVotingMin.java b/src/de/lmu/ifi/dbs/elki/utilities/ensemble/EnsembleVotingMin.java
new file mode 100644
index 00000000..fcf5c138
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/ensemble/EnsembleVotingMin.java
@@ -0,0 +1,49 @@
+package de.lmu.ifi.dbs.elki.utilities.ensemble;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+/**
+ * Simple combination rule, by taking the minimum.
+ *
+ * @author Erich Schubert
+ */
+public class EnsembleVotingMin implements EnsembleVoting {
+ /**
+ * Constructor, adhering to
+ * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
+ */
+ public EnsembleVotingMin() {
+ // empty
+ }
+
+ @Override
+ public double combine(double[] scores) {
+ double min = Double.POSITIVE_INFINITY;
+ for (double val : scores) {
+ min = Math.min(min, val);
+ }
+ return min;
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/ensemble/EnsembleVotingRestrictedBayes.java b/src/de/lmu/ifi/dbs/elki/utilities/ensemble/EnsembleVotingRestrictedBayes.java
new file mode 100644
index 00000000..c2edcbfd
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/ensemble/EnsembleVotingRestrictedBayes.java
@@ -0,0 +1,127 @@
+package de.lmu.ifi.dbs.elki.utilities.ensemble;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterEqualConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.LessConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.LessEqualConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.LessGlobalConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.DoubleParameter;
+
+/**
+ * Combination rule based on Bayes theorems.
+ *
+ * @author Erich Schubert
+ */
+public class EnsembleVotingRestrictedBayes implements EnsembleVoting {
+ /**
+ * Minimum vote to cast
+ */
+ private double minvote = 0.05;
+
+ /**
+ * Maximum vote to cast
+ */
+ private double maxvote = 1.0;
+
+ /**
+ * Constructor.
+ *
+ * @param minvote minimum vote
+ * @param maxvote maximum vote
+ */
+ public EnsembleVotingRestrictedBayes(double minvote, double maxvote) {
+ this.minvote = minvote;
+ this.maxvote = maxvote;
+ }
+
+ @Override
+ public double combine(double[] scores) {
+ double pos = 1.0;
+ double neg = 1.0;
+ for (double score : scores) {
+ score = Math.min(minvote, Math.max(maxvote, score));
+ final double cscore = score;
+ pos *= cscore;
+ neg *= (1.0 - cscore);
+ }
+ return pos / (pos + neg);
+ }
+
+ /**
+ * Parameterization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public static class Parameterizer extends AbstractParameterizer {
+ /**
+ * Option ID for the minimum and maximum vote
+ */
+ public static final OptionID MIN_ID = new OptionID("ensemble.bayes.min", "Minimum vote share.");
+
+ /**
+ * Option ID for the minimum and maximum vote
+ */
+ public static final OptionID MAX_ID = new OptionID("ensemble.bayes.max", "Maximum vote share.");
+
+ /**
+ * Minimum vote to cast
+ */
+ private double minvote = 0.05;
+
+ /**
+ * Maximum vote to cast
+ */
+ private double maxvote = 1.0;
+
+ @Override
+ protected void makeOptions(Parameterization config) {
+ super.makeOptions(config);
+ DoubleParameter minvoteP = new DoubleParameter(MIN_ID, 0.05);
+ minvoteP.addConstraint(new GreaterEqualConstraint(0.0));
+ minvoteP.addConstraint(new LessConstraint(0.0));
+ if (config.grab(minvoteP)) {
+ minvote = minvoteP.doubleValue();
+ }
+ DoubleParameter maxvoteP = new DoubleParameter(MAX_ID, 0.95);
+ maxvoteP.addConstraint(new GreaterConstraint(0.0));
+ maxvoteP.addConstraint(new LessEqualConstraint(0.0));
+ if (config.grab(maxvoteP)) {
+ maxvote = maxvoteP.doubleValue();
+ }
+ config.checkConstraint(new LessGlobalConstraint<Double>(minvoteP, maxvoteP));
+ }
+
+ @Override
+ protected EnsembleVotingRestrictedBayes makeInstance() {
+ return new EnsembleVotingRestrictedBayes(minvote, maxvote);
+ }
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/ensemble/package-info.java b/src/de/lmu/ifi/dbs/elki/utilities/ensemble/package-info.java
new file mode 100644
index 00000000..572b40bb
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/ensemble/package-info.java
@@ -0,0 +1,26 @@
+/**
+ * <p>Utility classes for simple ensembles.</p>
+ */
+/*
+This file is part of ELKI:
+Environment for Developing KDD-Applications Supported by Index-Structures
+
+Copyright (C) 2012
+Ludwig-Maximilians-Universität München
+Lehr- und Forschungseinheit für Datenbanksysteme
+ELKI Development Team
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+package de.lmu.ifi.dbs.elki.utilities.ensemble; \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/iterator/AbstractFilteredIterator.java b/src/de/lmu/ifi/dbs/elki/utilities/iterator/AbstractFilteredIterator.java
index a56b34db..9a729595 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/iterator/AbstractFilteredIterator.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/iterator/AbstractFilteredIterator.java
@@ -57,7 +57,7 @@ public abstract class AbstractFilteredIterator<IN, OUT extends IN> implements It
}
/**
- * Init the iterators
+ * Init the iterators.
*/
protected void init() {
this.itr = getParentIterator();
@@ -71,7 +71,7 @@ public abstract class AbstractFilteredIterator<IN, OUT extends IN> implements It
*
* @return iterator
*/
- abstract protected Iterator<IN> getParentIterator();
+ protected abstract Iterator<IN> getParentIterator();
/**
* Test the filter predicate for a new object.
@@ -79,7 +79,7 @@ public abstract class AbstractFilteredIterator<IN, OUT extends IN> implements It
* @param nextobj Object to test
* @return cast object when true, {@code null} otherwise
*/
- abstract protected OUT testFilter(IN nextobj);
+ protected abstract OUT testFilter(IN nextobj);
/**
* Find the next visualizer.
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/iterator/ArrayIter.java b/src/de/lmu/ifi/dbs/elki/utilities/iterator/ArrayIter.java
new file mode 100644
index 00000000..fb6601d1
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/iterator/ArrayIter.java
@@ -0,0 +1,59 @@
+package de.lmu.ifi.dbs.elki.utilities.iterator;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * Array iterators can also go backwards and seek.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.landmark
+ */
+public interface ArrayIter extends Iter {
+ /**
+ * Get current iterator offset.
+ *
+ * @return Iterator position
+ */
+ public int getOffset();
+
+ /**
+ * Moves the iterator forward or backward by the given offset.
+ *
+ * @param count offset to move forward or backwards
+ */
+ public void advance(int count);
+
+ /**
+ * Moves the iterator backward to the previous entry.
+ */
+ public void retract();
+
+ /**
+ * Moves the iterator to the given position
+ *
+ * @param off Seek offset
+ */
+ public void seek(int off);
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/iterator/EmptyIterator.java b/src/de/lmu/ifi/dbs/elki/utilities/iterator/EmptyIterator.java
index 7ec63d62..6cbdad29 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/iterator/EmptyIterator.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/iterator/EmptyIterator.java
@@ -56,7 +56,7 @@ public final class EmptyIterator<T> implements Iterator<T>, Iterable<T> {
/**
* Static instance
*/
- protected final static EmptyIterator<?> STATIC_INSTANCE = new EmptyIterator<Object>();
+ protected static final EmptyIterator<?> STATIC_INSTANCE = new EmptyIterator<Object>();
/**
* Access the static instance.
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/iterator/Iter.java b/src/de/lmu/ifi/dbs/elki/utilities/iterator/Iter.java
index 11fead71..8bb1dcc6 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/iterator/Iter.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/iterator/Iter.java
@@ -52,6 +52,8 @@ package de.lmu.ifi.dbs.elki.utilities.iterator;
* </pre>
*
* @author Erich Schubert
+ *
+ * @apiviz.landmark
*/
public interface Iter {
/**
@@ -63,9 +65,6 @@ public interface Iter {
/**
* Moves the iterator forward to the next entry.
- *
- * @throws java.util.NoSuchElementException if the iterator is already
- * exhausted
*/
public void advance();
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/database/query/DistanceDBIDResult.java b/src/de/lmu/ifi/dbs/elki/utilities/iterator/MIter.java
index ead3eebc..d9017d6c 100644
--- a/src/de/lmu/ifi/dbs/elki/database/query/DistanceDBIDResult.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/iterator/MIter.java
@@ -1,4 +1,5 @@
-package de.lmu.ifi.dbs.elki.database.query;
+package de.lmu.ifi.dbs.elki.utilities.iterator;
+
/*
This file is part of ELKI:
@@ -23,19 +24,14 @@ package de.lmu.ifi.dbs.elki.database.query;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.util.List;
-
-import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
-
/**
- * Collection of objects and their distances.
+ * Modifiable iterator, that also supports removal.
*
* @author Erich Schubert
- *
- * @apiviz.composedOf DistanceResultPair
- *
- * @param <D> Distance type
*/
-public interface DistanceDBIDResult<D extends Distance<D>> extends List<DistanceResultPair<D>> {
- // Empty. TODO: add "sorted" property?
+public interface MIter extends Iter {
+ /**
+ * Remove the object the iterator currently points to.
+ */
+ void remove();
}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/AbstractParameterizer.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/AbstractParameterizer.java
index 32543ace..0a094ced 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/AbstractParameterizer.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/AbstractParameterizer.java
@@ -37,22 +37,22 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameteriz
*/
public abstract class AbstractParameterizer implements Parameterizer {
/**
- * Constant for "fresh" state
+ * Constant for "fresh" state.
*/
private static final int STATE_FRESH = 0;
/**
- * Constant for "initializing" state
+ * Constant for "initializing" state.
*/
private static final int STATE_INIT = 1;
/**
- * Constant for "complete" state
+ * Constant for "complete" state.
*/
private static final int STATE_COMPLETE = 2;
/**
- * Constant for "errors" state
+ * Constant for "errors" state.
*/
private static final int STATE_ERRORS = -1;
@@ -84,7 +84,7 @@ public abstract class AbstractParameterizer implements Parameterizer {
*
* @return instance
*/
- abstract protected Object makeInstance();
+ protected abstract Object makeInstance();
/**
* Method to configure a class, then instantiate when the configuration step
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/InternalParameterizationErrors.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/InternalParameterizationErrors.java
index 4bec9193..c8797c73 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/InternalParameterizationErrors.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/InternalParameterizationErrors.java
@@ -23,6 +23,7 @@ package de.lmu.ifi.dbs.elki.utilities.optionhandling;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+import java.util.ArrayList;
import java.util.Collection;
/**
@@ -62,7 +63,7 @@ public class InternalParameterizationErrors extends ParameterException {
*/
public InternalParameterizationErrors(String message, Exception internalError) {
super(message);
- final java.util.Vector<Exception> errors = new java.util.Vector<Exception>(1);
+ final ArrayList<Exception> errors = new ArrayList<Exception>(1);
errors.add(internalError);
this.internalErrors = errors;
}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/NoParameterValueException.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/NoParameterValueException.java
index 2965f207..ce422de7 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/NoParameterValueException.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/NoParameterValueException.java
@@ -23,7 +23,6 @@ package de.lmu.ifi.dbs.elki.utilities.optionhandling;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-
/**
* Thrown by OptionHandler in case of incorrect parameter-array.
*/
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/OptionID.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/OptionID.java
index bea6bb4f..2c9814e3 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/OptionID.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/OptionID.java
@@ -23,8 +23,6 @@ package de.lmu.ifi.dbs.elki.utilities.optionhandling;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import de.lmu.ifi.dbs.elki.utilities.ConstantObject;
-
/**
* An OptionID is used by option handlers as a unique identifier for specific
* options. There is no option possible without a specific OptionID defined
@@ -32,7 +30,7 @@ import de.lmu.ifi.dbs.elki.utilities.ConstantObject;
*
* @author Elke Achtert
*/
-public final class OptionID extends ConstantObject<OptionID> {
+public final class OptionID {
/**
* Flag to obtain help-message.
* <p>
@@ -113,6 +111,11 @@ public final class OptionID extends ConstantObject<OptionID> {
public static final OptionID TIME_FLAG = new OptionID("time", "Enable logging of runtime data. Do not combine with more verbose logging, since verbose logging can significantly impact performance.");
/**
+ * Option name
+ */
+ private String name;
+
+ /**
* The description of the OptionID.
*/
private String description;
@@ -126,8 +129,9 @@ public final class OptionID extends ConstantObject<OptionID> {
* @param name the name of the option
* @param description the description of the option
*/
- private OptionID(final String name, final String description) {
- super(name);
+ public OptionID(final String name, final String description) {
+ super();
+ this.name = name;
this.description = description;
}
@@ -141,15 +145,6 @@ public final class OptionID extends ConstantObject<OptionID> {
}
/**
- * Sets the description of this OptionID.
- *
- * @param description the description to be set
- */
- public void setDescription(String description) {
- this.description = description;
- }
-
- /**
* Gets or creates the OptionID for the given class and given name. The
* OptionID usually is named as the classes name (lowercase) as name-prefix
* and the given name as suffix of the complete name, separated by a dot. For
@@ -163,24 +158,7 @@ public final class OptionID extends ConstantObject<OptionID> {
* @return the OptionID for the given name
*/
public static OptionID getOrCreateOptionID(final String name, final String description) {
- OptionID optionID = getOptionID(name);
- if(optionID == null) {
- optionID = new OptionID(name, description);
- }
- else {
- optionID.setDescription(description);
- }
- return optionID;
- }
-
- /**
- * Returns the OptionID for the given name if it exists, null otherwise.
- *
- * @param name name of the desired OptionID
- * @return the OptionID for the given name
- */
- public static OptionID getOptionID(final String name) {
- return OptionID.lookup(OptionID.class, name);
+ return new OptionID(name, description);
}
/**
@@ -194,4 +172,12 @@ public final class OptionID extends ConstantObject<OptionID> {
return getName();
}
+ /**
+ * Get the option name.
+ *
+ * @return Option name
+ */
+ public String getName() {
+ return name;
+ }
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/OptionUtil.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/OptionUtil.java
index 2aa68611..76ae9feb 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/OptionUtil.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/OptionUtil.java
@@ -43,6 +43,13 @@ import de.lmu.ifi.dbs.elki.utilities.pairs.Pair;
*/
public final class OptionUtil {
/**
+ * Fake constructor. Use static method.
+ */
+ private OptionUtil() {
+ // Do not instantiate.
+ }
+
+ /**
* Returns a string representation of the specified list of options containing
* the names of the options.
*
@@ -50,16 +57,16 @@ public final class OptionUtil {
* @param options the list of options
* @return the names of the options
*/
- public static <O extends Parameter<?, ?>> String optionsNamesToString(List<O> options) {
- StringBuffer buffer = new StringBuffer();
- buffer.append("[");
+ public static <O extends Parameter<?>> String optionsNamesToString(List<O> options) {
+ StringBuilder buffer = new StringBuilder();
+ buffer.append('[');
for(int i = 0; i < options.size(); i++) {
buffer.append(options.get(i).getName());
if(i != options.size() - 1) {
- buffer.append(",");
+ buffer.append(',');
}
}
- buffer.append("]");
+ buffer.append(']');
return buffer.toString();
}
@@ -71,16 +78,16 @@ public final class OptionUtil {
* @param options the list of options
* @return the names of the options
*/
- public static <O extends Parameter<?, ?>> String optionsNamesToString(O[] options) {
- StringBuffer buffer = new StringBuffer();
- buffer.append("[");
+ public static <O extends Parameter<?>> String optionsNamesToString(O[] options) {
+ StringBuilder buffer = new StringBuilder();
+ buffer.append('[');
for(int i = 0; i < options.length; i++) {
buffer.append(options[i].getName());
if(i != options.length - 1) {
- buffer.append(",");
+ buffer.append(',');
}
}
- buffer.append("]");
+ buffer.append(']');
return buffer.toString();
}
@@ -92,18 +99,18 @@ public final class OptionUtil {
* @param parameters the list of number parameters
* @return the names and the values of the parameters
*/
- public static <N extends Parameter<?, ?>> String parameterNamesAndValuesToString(List<N> parameters) {
- StringBuffer buffer = new StringBuffer();
- buffer.append("[");
+ public static <N extends Parameter<?>> String parameterNamesAndValuesToString(List<N> parameters) {
+ StringBuilder buffer = new StringBuilder();
+ buffer.append('[');
for(int i = 0; i < parameters.size(); i++) {
buffer.append(parameters.get(i).getName());
- buffer.append(":");
+ buffer.append(':');
buffer.append(parameters.get(i).getValueAsString());
if(i != parameters.size() - 1) {
buffer.append(", ");
}
}
- buffer.append("]");
+ buffer.append(']');
return buffer.toString();
}
@@ -116,15 +123,15 @@ public final class OptionUtil {
* @param indent Indentation string
* @param options List of options
*/
- public static void formatForConsole(StringBuffer buf, int width, String indent, Collection<Pair<Object, Parameter<?, ?>>> options) {
- for(Pair<Object, Parameter<?, ?>> pair : options) {
+ public static void formatForConsole(StringBuilder buf, int width, String indent, Collection<Pair<Object, Parameter<?>>> options) {
+ for(Pair<Object, Parameter<?>> pair : options) {
String currentOption = pair.getSecond().getName();
String syntax = pair.getSecond().getSyntax();
String longDescription = pair.getSecond().getFullDescription();
buf.append(SerializedParameterization.OPTION_PREFIX);
buf.append(currentOption);
- buf.append(" ");
+ buf.append(' ');
buf.append(syntax);
buf.append(FormatUtil.NEWLINE);
println(buf, width, longDescription, indent);
@@ -139,7 +146,7 @@ public final class OptionUtil {
* @param data Data to write.
* @param indent Indentation
*/
- public static void println(StringBuffer buf, int width, String data, String indent) {
+ public static void println(StringBuilder buf, int width, String data, String indent) {
for(String line : FormatUtil.splitAtLastBlank(data, width - indent.length())) {
buf.append(indent);
buf.append(line);
@@ -158,7 +165,7 @@ public final class OptionUtil {
* @param indent Text indent
* @return Formatted description
*/
- public static StringBuffer describeParameterizable(StringBuffer buf, Class<?> pcls, int width, String indent) {
+ public static StringBuilder describeParameterizable(StringBuilder buf, Class<?> pcls, int width, String indent) {
try {
println(buf, width, "Description for class " + pcls.getName(), "");
@@ -189,7 +196,7 @@ public final class OptionUtil {
TrackParameters track = new TrackParameters(config);
@SuppressWarnings("unused")
Object p = ClassGenericsUtil.tryInstantiate(Object.class, pcls, track);
- Collection<Pair<Object, Parameter<?, ?>>> options = track.getAllParameters();
+ Collection<Pair<Object, Parameter<?>>> options = track.getAllParameters();
if(options.size() > 0) {
OptionUtil.formatForConsole(buf, width, indent, options);
}
@@ -198,7 +205,7 @@ public final class OptionUtil {
}
catch(Exception e) {
LoggingUtil.exception("Error instantiating class to describe.", e.getCause());
- buf.append("No description available: " + e);
+ buf.append("No description available: ").append(e);
return buf;
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/Parameterizable.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/Parameterizable.java
index 4149658a..13f202dd 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/Parameterizable.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/Parameterizable.java
@@ -47,7 +47,7 @@ import de.lmu.ifi.dbs.elki.utilities.InspectionUtilFrequentlyScanned;
* <em>must have a constructor that either is</em> <blockquote>
*
* <pre>
- * @code
+ * {@code
* public Class(Parameterizable config) { ... }
* }
* </pre>
@@ -55,7 +55,7 @@ import de.lmu.ifi.dbs.elki.utilities.InspectionUtilFrequentlyScanned;
* </blockquote> or <blockquote>
*
* <pre>
- * @code
+ * {@code
* public Class() { ... }
* }
* </pre>
@@ -91,11 +91,12 @@ import de.lmu.ifi.dbs.elki.utilities.InspectionUtilFrequentlyScanned;
*
* @apiviz.exclude
* @apiviz.excludeSubtypes
- * @apiviz.has de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.Parameter oneway - n
+ * @apiviz.has de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.Parameter
+ * oneway - n
* @apiviz.uses de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization
* .Parameterization oneway
*/
public interface Parameterizable extends InspectionUtilFrequentlyScanned {
// Empty marker interface - the \@Description / \@Title / \@Reference and
// constructor requirements cannot be specified in Java!
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/UnspecifiedParameterException.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/UnspecifiedParameterException.java
index 8e294ddb..ab138704 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/UnspecifiedParameterException.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/UnspecifiedParameterException.java
@@ -27,6 +27,7 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.Parameter;
/**
* Exception when a required parameter was not given.
+ *
* @author Erich Schubert
*/
public class UnspecifiedParameterException extends WrongParameterValueException {
@@ -36,27 +37,26 @@ public class UnspecifiedParameterException extends WrongParameterValueException
private static final long serialVersionUID = -7142809547201980898L;
/**
- * Constructor with missing Parameter
- * @param parameter Missing parameter
+ * Parameter that was missing.
*/
- public UnspecifiedParameterException(Parameter<?, ?> parameter) {
- super("No value given for parameter \"" + parameter.getName() + "\":\n" + "Expected: " + parameter.getFullDescription());
- }
+ private String parameter;
/**
- * Constructor with missing Parameter and cause
+ * Constructor with missing Parameter
+ *
* @param parameter Missing parameter
- * @param cause Cause
*/
- public UnspecifiedParameterException(Parameter<?, ?> parameter, Throwable cause) {
- super("No value given for parameter \"" + parameter.getName() + "\":\n" + "Expected: " + parameter.getFullDescription(), cause);
+ public UnspecifiedParameterException(Parameter<?> parameter) {
+ super("No value given for parameter \"" + parameter.getName() + "\":\n" + "Expected: " + parameter.getFullDescription());
+ this.parameter = parameter.getName();
}
/**
- * Constructor with error message.
- * @param message Message
+ * Get the parameter name that was missing.
+ *
+ * @return Parameter name
*/
- public UnspecifiedParameterException(String message) {
- super(message);
+ public String getParameterName() {
+ return parameter;
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/WrongParameterValueException.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/WrongParameterValueException.java
index 219b32cd..c51eae0e 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/WrongParameterValueException.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/WrongParameterValueException.java
@@ -27,6 +27,8 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.Parameter;
/**
* Thrown by a Parameterizable object in case of wrong parameter format.
+ *
+ * @author Steffi Wanka
*/
public class WrongParameterValueException extends ParameterException {
/**
@@ -40,7 +42,7 @@ public class WrongParameterValueException extends ParameterException {
* @param parameter the parameter that has a wrong value
* @param read the value of the parameter read by the option handler
*/
- public WrongParameterValueException(Parameter<?,?> parameter, String read) {
+ public WrongParameterValueException(Parameter<?> parameter, String read) {
this("Wrong value of parameter \"" + parameter.getName() + "\".\n" + "Read: " + read + ".\n" + "Expected: " + parameter.getFullDescription());
}
@@ -51,8 +53,8 @@ public class WrongParameterValueException extends ParameterException {
* @param read the value of the parameter read by the option handler
* @param cause the cause
*/
- public WrongParameterValueException(Parameter<?, ?> parameter, String read, Throwable cause) {
- this("Wrong value of parameter \"" + parameter.getName() + "\".\n" + "Read: " + read + ".\n" + "Expected: " + parameter.getFullDescription(), cause);
+ public WrongParameterValueException(Parameter<?> parameter, String read, Throwable cause) {
+ this("Wrong value of parameter \"" + parameter.getName() + "\".\n" + "Read: " + read + ".\n" + "Expected: " + parameter.getFullDescription() + "\n" + cause.getMessage(), cause);
}
/**
@@ -63,8 +65,8 @@ public class WrongParameterValueException extends ParameterException {
* @param reason detailed error description
* @param cause the cause
*/
- public WrongParameterValueException(Parameter<?, ?> parameter, String read, String reason, Throwable cause) {
- this("Wrong value of parameter " + parameter.getName() + ".\n" + "Read: " + read + ".\n" + "Expected: " + parameter.getFullDescription()+"\n"+reason, cause);
+ public WrongParameterValueException(Parameter<?> parameter, String read, String reason, Throwable cause) {
+ this("Wrong value of parameter " + parameter.getName() + ".\n" + "Read: " + read + ".\n" + "Expected: " + parameter.getFullDescription() + "\n" + reason + "\n" + cause.getMessage(), cause);
}
/**
@@ -74,8 +76,8 @@ public class WrongParameterValueException extends ParameterException {
* @param read the value of the parameter read by the option handler
* @param reason detailed error description
*/
- public WrongParameterValueException(Parameter<?, ?> parameter, String read, String reason) {
- this("Wrong value of parameter " + parameter.getName() + ".\n" + "Read: " + read + ".\n" + "Expected: " + parameter.getFullDescription()+"\n"+reason);
+ public WrongParameterValueException(Parameter<?> parameter, String read, String reason) {
+ this("Wrong value of parameter " + parameter.getName() + ".\n" + "Read: " + read + ".\n" + "Expected: " + parameter.getFullDescription() + "\n" + reason);
}
/**
@@ -86,7 +88,7 @@ public class WrongParameterValueException extends ParameterException {
public WrongParameterValueException(String message) {
super(message);
}
-
+
/**
* Thrown by a Parameterizable object in case of wrong parameter format.
*
@@ -94,6 +96,6 @@ public class WrongParameterValueException extends ParameterException {
* @param e cause
*/
public WrongParameterValueException(String message, Throwable e) {
- super(message,e );
+ super(message, e);
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/AllOrNoneMustBeSetGlobalConstraint.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/AllOrNoneMustBeSetGlobalConstraint.java
index 42209d87..c2448ba5 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/AllOrNoneMustBeSetGlobalConstraint.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/AllOrNoneMustBeSetGlobalConstraint.java
@@ -23,8 +23,8 @@ package de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+import java.util.ArrayList;
import java.util.List;
-import java.util.Vector;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionUtil;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.ParameterException;
@@ -33,9 +33,7 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.Parameter;
/**
* Global parameter constraint specifying that either all elements of a list of
- * parameters (
- * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.Parameter})
- * must be set, or none of them.
+ * parameters ({@link Parameter}) must be set, or none of them.
*
* @author Steffi Wanka
*/
@@ -43,7 +41,7 @@ public class AllOrNoneMustBeSetGlobalConstraint implements GlobalParameterConstr
/**
* List of parameters to be checked
*/
- private List<Parameter<?, ?>> parameterList;
+ private List<Parameter<?>> parameterList;
/**
* Constructs a global parameter constraint for testing if either all elements
@@ -51,7 +49,7 @@ public class AllOrNoneMustBeSetGlobalConstraint implements GlobalParameterConstr
*
* @param parameters list of parameters to be checked
*/
- public AllOrNoneMustBeSetGlobalConstraint(List<Parameter<?, ?>> parameters) {
+ public AllOrNoneMustBeSetGlobalConstraint(List<Parameter<?>> parameters) {
this.parameterList = parameters;
}
@@ -62,10 +60,10 @@ public class AllOrNoneMustBeSetGlobalConstraint implements GlobalParameterConstr
@Override
public void test() throws ParameterException {
- Vector<String> set = new Vector<String>();
- Vector<String> notSet = new Vector<String>();
+ ArrayList<String> set = new ArrayList<String>();
+ ArrayList<String> notSet = new ArrayList<String>();
- for(Parameter<?, ?> p : parameterList) {
+ for(Parameter<?> p : parameterList) {
if(p.isDefined()) {
set.add(p.getName());
}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/EqualStringConstraint.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/EqualStringConstraint.java
index ad567fa1..4c4a8de3 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/EqualStringConstraint.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/EqualStringConstraint.java
@@ -57,16 +57,16 @@ public class EqualStringConstraint implements ParameterConstraint<String> {
}
private String constraintStrings() {
- StringBuffer buffer = new StringBuffer();
- buffer.append("[");
+ StringBuilder buffer = new StringBuilder();
+ buffer.append('[');
for(int i = 0; i < testStrings.length; i++) {
buffer.append(testStrings[i]);
if(i != testStrings.length - 1) {
- buffer.append(",");
+ buffer.append(',');
}
}
- buffer.append("]");
+ buffer.append(']');
return buffer.toString();
}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/GlobalListSizeConstraint.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/GlobalListSizeConstraint.java
index b30a45d9..1bad7d00 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/GlobalListSizeConstraint.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/GlobalListSizeConstraint.java
@@ -71,7 +71,7 @@ public class GlobalListSizeConstraint implements GlobalParameterConstraint {
return;
}
- if(list.getListSize() != length.getValue()) {
+ if(list.getListSize() != length.getValue().intValue()) {
throw new WrongParameterValueException("Global Parameter Constraint Error." + "\nThe size of the list parameter \"" + list.getName() + "\" must be " + length.getValue() + ", current size is " + list.getListSize() + ". The value is defined by the integer parameter " + length.getName() + ".\n");
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/GlobalVectorListElementSizeConstraint.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/GlobalVectorListElementSizeConstraint.java
index 740d9545..6bc9fd2d 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/GlobalVectorListElementSizeConstraint.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/GlobalVectorListElementSizeConstraint.java
@@ -75,7 +75,7 @@ public class GlobalVectorListElementSizeConstraint implements GlobalParameterCon
}
for(List<Double> vec : vector.getValue()) {
- if(vec.size() != size.getValue()) {
+ if(vec.size() != size.getValue().intValue()) {
throw new WrongParameterValueException("Global Parameter Constraint Error.\n" + "The vectors of vector list parameter " + vector.getName() + " have not the required dimension of " + size.getValue() + " given by integer parameter " + size.getName() + ".");
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/GreaterConstraint.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/GreaterConstraint.java
index 22cc50f9..17a9a54d 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/GreaterConstraint.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/GreaterConstraint.java
@@ -28,42 +28,63 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.WrongParameterValueException
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.NumberParameter;
/**
- * Represents a parameter constraint for testing if the value of the
- * number parameter ({@link NumberParameter})
- * tested is greater than the specified constraint value.
- *
+ * Represents a parameter constraint for testing if the value of the number
+ * parameter ({@link NumberParameter}) tested is greater than the specified
+ * constraint value.
+ *
* @author Steffi Wanka
*/
public class GreaterConstraint extends AbstractNumberConstraint<Number> {
- /**
- * Creates a Greater-Than-Number parameter constraint.
- * <p/>
- * That is, the value of the number
- * parameter has to be greater than the given constraint value.
- *
- * @param constraintValue the constraint value
- */
- public GreaterConstraint(Number constraintValue) {
- super(constraintValue);
- }
+ /**
+ * Creates a Greater-Than-Number parameter constraint.
+ * <p/>
+ * That is, the value of the number parameter has to be greater than the given
+ * constraint value.
+ *
+ * @param constraintValue the constraint value
+ */
+ public GreaterConstraint(Number constraintValue) {
+ super(constraintValue);
+ }
- /**
- * Checks if the number value given by the number parameter is greater than
- * the constraint value. If not, a parameter exception is thrown.
- *
- */
- @Override
- public void test(Number t) throws ParameterException {
- if (t.doubleValue() <= constraintValue.doubleValue()) {
- throw new WrongParameterValueException("Parameter Constraint Error:\n"
- + "The parameter value specified has to be greater than "
- + constraintValue.toString() +
- ". (current value: " + t.doubleValue() + ")\n");
- }
- }
+ /**
+ * Creates a Greater-Than-Number parameter constraint.
+ * <p/>
+ * That is, the value of the number parameter has to be greater than the given
+ * constraint value.
+ *
+ * @param constraintValue the constraint value
+ */
+ public GreaterConstraint(int constraintValue) {
+ super(Integer.valueOf(constraintValue));
+ }
- @Override
- public String getDescription(String parameterName) {
- return parameterName + " > " + constraintValue;
+ /**
+ * Creates a Greater-Than-Number parameter constraint.
+ * <p/>
+ * That is, the value of the number parameter has to be greater than the given
+ * constraint value.
+ *
+ * @param constraintValue the constraint value
+ */
+ public GreaterConstraint(double constraintValue) {
+ super(Double.valueOf(constraintValue));
+ }
+
+ /**
+ * Checks if the number value given by the number parameter is greater than
+ * the constraint value. If not, a parameter exception is thrown.
+ *
+ */
+ @Override
+ public void test(Number t) throws ParameterException {
+ if (t.doubleValue() <= constraintValue.doubleValue()) {
+ throw new WrongParameterValueException("Parameter Constraint Error:\n" + "The parameter value specified has to be greater than " + constraintValue.toString() + ". (current value: " + t.doubleValue() + ")\n");
}
+ }
+
+ @Override
+ public String getDescription(String parameterName) {
+ return parameterName + " > " + constraintValue;
+ }
}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/GreaterEqualConstraint.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/GreaterEqualConstraint.java
index 26145620..7eb0de83 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/GreaterEqualConstraint.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/GreaterEqualConstraint.java
@@ -28,45 +28,64 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.WrongParameterValueException
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.NumberParameter;
/**
- * Represents a Greater-Equal-Than-Number parameter constraint. The
- * value of the number parameter ({@link NumberParameter})
- * tested has to be greater equal than the specified
- * constraint value.
- *
+ * Represents a Greater-Equal-Than-Number parameter constraint. The value of the
+ * number parameter ({@link NumberParameter}) tested has to be greater equal
+ * than the specified constraint value.
+ *
* @author Steffi Wanka
*/
public class GreaterEqualConstraint extends AbstractNumberConstraint<Number> {
- /**
- * Creates a Greater-Equal parameter constraint.
- * <p/>
- * That is, the value of the number
- * parameter given has to be greater equal than the constraint value given.
- *
- * @param constraintValue the constraint value
- */
- public GreaterEqualConstraint(Number constraintValue) {
- super(constraintValue);
- }
+ /**
+ * Creates a Greater-Equal parameter constraint.
+ * <p/>
+ * That is, the value of the number parameter given has to be greater equal
+ * than the constraint value given.
+ *
+ * @param constraintValue the constraint value
+ */
+ public GreaterEqualConstraint(Number constraintValue) {
+ super(constraintValue);
+ }
- /**
- * Checks if the number value given by the number parameter is
- * greater equal than the constraint
- * value. If not, a parameter exception is thrown.
- *
- */
- @Override
- public void test(Number t) throws ParameterException {
- if (t.doubleValue() < constraintValue.doubleValue()) {
- throw new WrongParameterValueException("Parameter Constraint Error: \n"
- + "The parameter value specified has to be greater equal than "
- + constraintValue.toString() +
- ". (current value: " + t.doubleValue() + ")\n");
- }
- }
+ /**
+ * Creates a Greater-Equal parameter constraint.
+ * <p/>
+ * That is, the value of the number parameter given has to be greater equal
+ * than the constraint value given.
+ *
+ * @param constraintValue the constraint value
+ */
+ public GreaterEqualConstraint(int constraintValue) {
+ super(Integer.valueOf(constraintValue));
+ }
- @Override
- public String getDescription(String parameterName) {
- return parameterName + " >= " + constraintValue;
+ /**
+ * Creates a Greater-Equal parameter constraint.
+ * <p/>
+ * That is, the value of the number parameter given has to be greater equal
+ * than the constraint value given.
+ *
+ * @param constraintValue the constraint value
+ */
+ public GreaterEqualConstraint(double constraintValue) {
+ super(Double.valueOf(constraintValue));
+ }
+
+ /**
+ * Checks if the number value given by the number parameter is greater equal
+ * than the constraint value. If not, a parameter exception is thrown.
+ *
+ */
+ @Override
+ public void test(Number t) throws ParameterException {
+ if (t.doubleValue() < constraintValue.doubleValue()) {
+ throw new WrongParameterValueException("Parameter Constraint Error: \n" + "The parameter value specified has to be greater equal than " + constraintValue.toString() + ". (current value: " + t.doubleValue() + ")\n");
}
+ }
+
+ @Override
+ public String getDescription(String parameterName) {
+ return parameterName + " >= " + constraintValue;
+ }
}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/IntervalConstraint.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/IntervalConstraint.java
index f9e93a5b..db6a1ed8 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/IntervalConstraint.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/IntervalConstraint.java
@@ -37,7 +37,10 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.WrongParameterValueException
* @author Steffi Wanka
*
* @apiviz.uses de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.NumberParameter
+ *
+ * @deprecated Use two constraints instead.
*/
+@Deprecated
public class IntervalConstraint implements ParameterConstraint<Number> {
/**
* Available interval boundary types types:
@@ -110,6 +113,56 @@ public class IntervalConstraint implements ParameterConstraint<Number> {
}
/**
+ * Creates an IntervalConstraint parameter constraint.
+ * <p/>
+ * That is, the value of the number parameter given has to be greater than (or
+ * equal to, if specified) than the specified low constraint value and less
+ * than (or equal to, if specified) than the specified high constraint value.
+ *
+ * @param lowConstraintValue the low constraint value (left interval boundary)
+ * @param lowBoundary the interval boundary for the low constraint value (see {@link IntervalBoundary})
+ * @param highConstraintValue the high constraint value (right interval
+ * boundary)
+ * @param highBoundary the interval boundary for the high constraint value
+ * (see {@link IntervalBoundary})
+ */
+ public IntervalConstraint(int lowConstraintValue, IntervalBoundary lowBoundary, int highConstraintValue, IntervalBoundary highBoundary) {
+ if(lowConstraintValue >= highConstraintValue) {
+ throw new IllegalArgumentException("Left interval boundary is greater than " + "or equal to right interval boundary!");
+ }
+
+ this.lowConstraintValue = Integer.valueOf(lowConstraintValue);
+ this.lowBoundary = lowBoundary;
+ this.highConstraintValue = Integer.valueOf(highConstraintValue);
+ this.highBoundary = highBoundary;
+ }
+
+ /**
+ * Creates an IntervalConstraint parameter constraint.
+ * <p/>
+ * That is, the value of the number parameter given has to be greater than (or
+ * equal to, if specified) than the specified low constraint value and less
+ * than (or equal to, if specified) than the specified high constraint value.
+ *
+ * @param lowConstraintValue the low constraint value (left interval boundary)
+ * @param lowBoundary the interval boundary for the low constraint value (see {@link IntervalBoundary})
+ * @param highConstraintValue the high constraint value (right interval
+ * boundary)
+ * @param highBoundary the interval boundary for the high constraint value
+ * (see {@link IntervalBoundary})
+ */
+ public IntervalConstraint(double lowConstraintValue, IntervalBoundary lowBoundary, double highConstraintValue, IntervalBoundary highBoundary) {
+ if(lowConstraintValue >= highConstraintValue) {
+ throw new IllegalArgumentException("Left interval boundary is greater than " + "or equal to right interval boundary!");
+ }
+
+ this.lowConstraintValue = Double.valueOf(lowConstraintValue);
+ this.lowBoundary = lowBoundary;
+ this.highConstraintValue = Double.valueOf(highConstraintValue);
+ this.highBoundary = highBoundary;
+ }
+
+ /**
* Checks if the number value given by the number parameter is greater equal
* than the constraint value. If not, a parameter exception is thrown.
*
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/LessConstraint.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/LessConstraint.java
index 0994425e..a8369304 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/LessConstraint.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/LessConstraint.java
@@ -28,41 +28,64 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.WrongParameterValueException
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.NumberParameter;
/**
- * Represents a Less-Than-Number parameter constraint. The value of the
- * number parameter ({@link NumberParameter}) tested has to be less than the specified constraint value.
- *
+ * Represents a Less-Than-Number parameter constraint. The value of the number
+ * parameter ({@link NumberParameter}) tested has to be less than the specified
+ * constraint value.
+ *
* @author Steffi Wanka
*/
public class LessConstraint extends AbstractNumberConstraint<Number> {
- /**
- * Creates a Less-Than-Number parameter constraint.
- * <p/>
- * That is, the value of the number
- * parameter tested has to be less than the constraint value given.
- *
- * @param constraintValue the constraint value
- */
- public LessConstraint(Number constraintValue) {
- super(constraintValue);
- }
+ /**
+ * Creates a Less-Than-Number parameter constraint.
+ * <p/>
+ * That is, the value of the number parameter tested has to be less than the
+ * constraint value given.
+ *
+ * @param constraintValue the constraint value
+ */
+ public LessConstraint(Number constraintValue) {
+ super(constraintValue);
+ }
- /**
- * Checks if the number value given by the number parameter is less than the constraint value.
- * If not, a parameter exception is thrown.
- *
- */
- @Override
- public void test(Number t) throws ParameterException {
- if (t.doubleValue() >= constraintValue.doubleValue()) {
- throw new WrongParameterValueException("Parameter Constraint Error: \n"
- + "The parameter value specified has to be less than " + constraintValue.toString()
- + ". (current value: " + t.doubleValue() + ")\n");
- }
- }
+ /**
+ * Creates a Less-Than-Number parameter constraint.
+ * <p/>
+ * That is, the value of the number parameter tested has to be less than the
+ * constraint value given.
+ *
+ * @param constraintValue the constraint value
+ */
+ public LessConstraint(int constraintValue) {
+ super(Integer.valueOf(constraintValue));
+ }
- @Override
- public String getDescription(String parameterName) {
- return parameterName + " < " + constraintValue;
+ /**
+ * Creates a Less-Than-Number parameter constraint.
+ * <p/>
+ * That is, the value of the number parameter tested has to be less than the
+ * constraint value given.
+ *
+ * @param constraintValue the constraint value
+ */
+ public LessConstraint(double constraintValue) {
+ super(Double.valueOf(constraintValue));
+ }
+
+ /**
+ * Checks if the number value given by the number parameter is less than the
+ * constraint value. If not, a parameter exception is thrown.
+ *
+ */
+ @Override
+ public void test(Number t) throws ParameterException {
+ if (t.doubleValue() >= constraintValue.doubleValue()) {
+ throw new WrongParameterValueException("Parameter Constraint Error: \n" + "The parameter value specified has to be less than " + constraintValue.toString() + ". (current value: " + t.doubleValue() + ")\n");
}
+ }
+
+ @Override
+ public String getDescription(String parameterName) {
+ return parameterName + " < " + constraintValue;
+ }
}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/LessEqualConstraint.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/LessEqualConstraint.java
index 712630e8..e35381b3 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/LessEqualConstraint.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/LessEqualConstraint.java
@@ -29,40 +29,61 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.NumberParameter;
/**
* Represents a Less-Equal-Than-Number parameter constraint. The value of the
- * number parameter ({@link NumberParameter}) tested has to be less equal than the specified constraint
- * value.
- *
+ * number parameter ({@link NumberParameter}) tested has to be less equal than
+ * the specified constraint value.
+ *
* @author Steffi Wanka
*/
public class LessEqualConstraint extends AbstractNumberConstraint<Number> {
- /**
- * Creates a Less-Equal-Than-Number parameter constraint.
- * <p/>
- * That is, the value of
- * the appropriate number parameter has to be less equal than the given constraint value.
- *
- * @param constraintValue the constraint value
- */
- public LessEqualConstraint(Number constraintValue) {
- super(constraintValue);
- }
+ /**
+ * Creates a Less-Equal-Than-Number parameter constraint.
+ * <p/>
+ * That is, the value of the appropriate number parameter has to be less equal
+ * than the given constraint value.
+ *
+ * @param constraintValue the constraint value
+ */
+ public LessEqualConstraint(Number constraintValue) {
+ super(constraintValue);
+ }
- /**
- * Checks if the number value given by the number parameter is less equal than
- * the constraint value. If not, a parameter exception is thrown.
- *
- */
- @Override
- public void test(Number t) throws ParameterException {
- if (t.doubleValue() > constraintValue.doubleValue()) {
- throw new WrongParameterValueException("Parameter Constraint Error: \n"
- + "The parameter value specified has to be less equal than "
- + constraintValue.toString() + ". (current value: " + t.doubleValue() + ")\n");
- }
- }
+ /**
+ * Creates a Less-Equal-Than-Number parameter constraint.
+ * <p/>
+ * That is, the value of the appropriate number parameter has to be less equal
+ * than the given constraint value.
+ *
+ * @param constraintValue the constraint value
+ */
+ public LessEqualConstraint(double constraintValue) {
+ super(Double.valueOf(constraintValue));
+ }
- @Override
- public String getDescription(String parameterName) {
- return parameterName + " <= " + constraintValue;
+ /**
+ * Creates a Less-Equal-Than-Number parameter constraint.
+ * <p/>
+ * That is, the value of the appropriate number parameter has to be less equal
+ * than the given constraint value.
+ *
+ * @param constraintValue the constraint value
+ */
+ public LessEqualConstraint(int constraintValue) {
+ super(Integer.valueOf(constraintValue));
+ }
+
+ /**
+ * Checks if the number value given by the number parameter is less equal than
+ * the constraint value. If not, a parameter exception is thrown.
+ */
+ @Override
+ public void test(Number t) throws ParameterException {
+ if (t.doubleValue() > constraintValue.doubleValue()) {
+ throw new WrongParameterValueException("Parameter Constraint Error: \n" + "The parameter value specified has to be less equal than " + constraintValue.toString() + ". (current value: " + t.doubleValue() + ")\n");
}
+ }
+
+ @Override
+ public String getDescription(String parameterName) {
+ return parameterName + " <= " + constraintValue;
+ }
}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/ListEachConstraint.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/ListEachConstraint.java
new file mode 100644
index 00000000..7dab4006
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/ListEachConstraint.java
@@ -0,0 +1,90 @@
+package de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.ArrayList;
+import java.util.List;
+
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.ParameterException;
+
+/**
+ * Applies constraints to all elements of a list.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.composedOf ParameterConstraint oneway 1 n
+ */
+public class ListEachConstraint<T> implements ParameterConstraint<List<T>> {
+ /**
+ * Constraints
+ */
+ private List<ParameterConstraint<? super T>> constraints;
+
+ /**
+ * Constructor.
+ */
+ public ListEachConstraint() {
+ super();
+ this.constraints = new ArrayList<ParameterConstraint<? super T>>();
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param constraint Constraint to apply to all elements
+ */
+ public ListEachConstraint(ParameterConstraint<? super T> constraint) {
+ super();
+ this.constraints = new ArrayList<ParameterConstraint<? super T>>(1);
+ this.constraints.add(constraint);
+ }
+
+ /**
+ * Add a constraint to this operator.
+ *
+ * @param constraint Constraint
+ */
+ public void addConstraint(ParameterConstraint<? super T> constraint) {
+ this.constraints.add(constraint);
+ }
+
+ @Override
+ public void test(List<T> t) throws ParameterException {
+ for (T e : t) {
+ for (ParameterConstraint<? super T> c : constraints) {
+ c.test(e);
+ }
+ }
+ }
+
+ @Override
+ public String getDescription(String parameterName) {
+ final String all = "all elements of " + parameterName;
+ StringBuilder b = new StringBuilder();
+ for (ParameterConstraint<? super T> c : constraints) {
+ b.append(c.getDescription(all));
+ }
+ return b.toString();
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/ListGreaterEqualConstraint.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/ListGreaterEqualConstraint.java
deleted file mode 100644
index 0f33b9b2..00000000
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/ListGreaterEqualConstraint.java
+++ /dev/null
@@ -1,74 +0,0 @@
-package de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints;
-
-/*
- This file is part of ELKI:
- Environment for Developing KDD-Applications Supported by Index-Structures
-
- Copyright (C) 2012
- Ludwig-Maximilians-Universität München
- Lehr- und Forschungseinheit für Datenbanksysteme
- ELKI Development Team
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.ParameterException;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.WrongParameterValueException;
-
-import java.util.List;
-
-/**
- * Represents a Greater-Equal-Than-Number parameter constraint for a list of number values.
- * All values of the list parameter ({@link de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ListParameter})
- * tested have to be greater than or equal to the specified constraint value.
- *
- * @author Elke Achtert
- * @param <N> Number type
- */
-public class ListGreaterEqualConstraint<N extends Number> extends AbstractNumberConstraint<List<N>> {
- /**
- * Creates a Greater-Equal-Than-Number parameter constraint.
- * <p/>
- * That is, all values of the list parameter
- * tested have to be greater than or equal to the specified constraint value.
- *
- * @param constraintValue parameter constraint value
- */
- public ListGreaterEqualConstraint(N constraintValue) {
- super(constraintValue);
- }
-
- /**
- * Checks if all number values of the specified list parameter
- * are greater than or equal to the constraint value.
- * If not, a parameter exception is thrown.
- *
- */
- @Override
- public void test(List<N> t) throws ParameterException {
- for (Number n : t) {
- if (n.doubleValue() < constraintValue.doubleValue()) {
- throw new WrongParameterValueException("Parameter Constraint Error: \n"
- + "The parameter values specified have to be greater than or equal to " + constraintValue.toString()
- + ". (current value: " + t + ")\n");
- }
- }
- }
-
- @Override
- public String getDescription(String parameterName) {
- return "all elements of " + parameterName + " < " + constraintValue;
- }
-
-}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/ListParameterNoDuplicateValueConstraint.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/ListParameterNoDuplicateValueConstraint.java
new file mode 100644
index 00000000..6545cc9d
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/ListParameterNoDuplicateValueConstraint.java
@@ -0,0 +1,78 @@
+package de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.ParameterException;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.WrongParameterValueException;
+
+/**
+ * Parameter constraint specifying that a parameter list
+ * is not allowed to have duplicate values.
+ *
+ * @author Arthur Zimek
+ */
+public class ListParameterNoDuplicateValueConstraint<T extends Object> implements ParameterConstraint<List<T>> {
+
+ /**
+ * Constructs a Not-Equal-Value parameter constraint. That is, the
+ * elements of a list of parameter values are not allowed to have equal
+ * values.
+ *
+ */
+ public ListParameterNoDuplicateValueConstraint() {
+ }
+
+
+
+ /**
+ * Checks if the elements of the list of parameter values do have different
+ * values. If not, a parameter exception is thrown.
+ *
+ */
+ @Override
+ public void test(List<T> list) throws ParameterException {
+ Set<T> values = new HashSet<T>();
+
+ for(T pv : list) {
+ if(!values.add(pv)) {
+ Object[] parametervaluesarr = list.toArray();
+ throw new WrongParameterValueException("Global Parameter Constraint Error:\n" + "Parameter values must have different values. Current values: " + Arrays.deepToString(parametervaluesarr) + ".\n");
+ }
+ }
+ }
+
+ @Override
+ public String getDescription(String parameterName) {
+ return "Values for parameter "+parameterName+" must have different values.";
+ }
+
+
+
+
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/ListSizeConstraint.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/ListSizeConstraint.java
index f2ef2069..82b3ae7e 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/ListSizeConstraint.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/ListSizeConstraint.java
@@ -37,10 +37,8 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ListParameter;
* @author Steffi Wanka
*
* @apiviz.uses de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ListParameter
- *
- * @param <T> Parameter type
*/
-public class ListSizeConstraint<T> implements ParameterConstraint<List<T>> {
+public class ListSizeConstraint implements ParameterConstraint<List<?>> {
/**
* The list size constraint.
*/
@@ -63,7 +61,7 @@ public class ListSizeConstraint<T> implements ParameterConstraint<List<T>> {
* equal to the list size constraint specified.
*/
@Override
- public void test(List<T> t) throws ParameterException {
+ public void test(List<?> t) throws ParameterException {
if(t.size() != sizeConstraint) {
throw new WrongParameterValueException("Parameter Constraint Error.\n" + "List parameter has not the required size. (Requested size: " + +sizeConstraint + ", current size: " + t.size() + ").\n");
}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/NoDuplicateValueGlobalConstraint.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/NoDuplicateValueGlobalConstraint.java
index 04472fa5..39ab2680 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/NoDuplicateValueGlobalConstraint.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/NoDuplicateValueGlobalConstraint.java
@@ -32,6 +32,7 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionUtil;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.ParameterException;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.WrongParameterValueException;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.NumberParameter;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.AbstractParameter;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.Parameter;
/**
@@ -44,7 +45,7 @@ public class NoDuplicateValueGlobalConstraint implements GlobalParameterConstrai
/**
* List of number parameters to be checked.
*/
- private List<? extends Parameter<?, ?>> parameters;
+ private List<? extends AbstractParameter<?>> parameters;
/**
* Constructs a Not-Equal-Value global parameter constraint. That is, the
@@ -53,7 +54,7 @@ public class NoDuplicateValueGlobalConstraint implements GlobalParameterConstrai
*
* @param parameters list of number parameters to be tested
*/
- public NoDuplicateValueGlobalConstraint(List<? extends Parameter<?, ?>> parameters) {
+ public NoDuplicateValueGlobalConstraint(List<? extends AbstractParameter<?>> parameters) {
this.parameters = parameters;
}
@@ -64,7 +65,7 @@ public class NoDuplicateValueGlobalConstraint implements GlobalParameterConstrai
*
* @param parameters list of number parameters to be tested
*/
- public NoDuplicateValueGlobalConstraint(Parameter<?, ?>... parameters) {
+ public NoDuplicateValueGlobalConstraint(AbstractParameter<?>... parameters) {
this.parameters = Arrays.asList(parameters);
}
@@ -77,7 +78,7 @@ public class NoDuplicateValueGlobalConstraint implements GlobalParameterConstrai
public void test() throws ParameterException {
Set<Object> numbers = new HashSet<Object>();
- for(Parameter<?, ?> param : parameters) {
+ for(Parameter<?> param : parameters) {
if(param.isDefined()) {
if(!numbers.add(param.getValue())) {
throw new WrongParameterValueException("Global Parameter Constraint Error:\n" + "Parameters " + OptionUtil.optionsNamesToString(parameters) + " must have different values. Current values: " + OptionUtil.parameterNamesAndValuesToString(parameters) + ".\n");
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/OneMustBeSetGlobalConstraint.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/OneMustBeSetGlobalConstraint.java
index 1c8c6b94..c0109ac5 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/OneMustBeSetGlobalConstraint.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/OneMustBeSetGlobalConstraint.java
@@ -41,7 +41,7 @@ public class OneMustBeSetGlobalConstraint implements GlobalParameterConstraint {
/**
* List of parameters to be checked.
*/
- private List<Parameter<?,?>> parameters;
+ private List<Parameter<?>> parameters;
/**
* Creates a One-Must-Be-Set global parameter constraint. That is, at least
@@ -49,7 +49,7 @@ public class OneMustBeSetGlobalConstraint implements GlobalParameterConstraint {
*
* @param params list of parameters
*/
- public OneMustBeSetGlobalConstraint(List<Parameter<?,?>> params) {
+ public OneMustBeSetGlobalConstraint(List<Parameter<?>> params) {
parameters = params;
}
@@ -60,8 +60,8 @@ public class OneMustBeSetGlobalConstraint implements GlobalParameterConstraint {
*/
@Override
public void test() throws ParameterException {
- for(Parameter<?,?> p : parameters) {
- if(p.isDefined()) {
+ for (Parameter<?> p : parameters) {
+ if (p.isDefined()) {
return;
}
}
@@ -72,4 +72,4 @@ public class OneMustBeSetGlobalConstraint implements GlobalParameterConstraint {
public String getDescription() {
return "At least one of the parameters " + OptionUtil.optionsNamesToString(parameters) + " has to be set.";
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/OnlyOneIsAllowedToBeSetGlobalConstraint.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/OnlyOneIsAllowedToBeSetGlobalConstraint.java
index 74a1955b..22db8511 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/OnlyOneIsAllowedToBeSetGlobalConstraint.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/OnlyOneIsAllowedToBeSetGlobalConstraint.java
@@ -23,8 +23,8 @@ package de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+import java.util.ArrayList;
import java.util.List;
-import java.util.Vector;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionUtil;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.ParameterException;
@@ -42,7 +42,7 @@ public class OnlyOneIsAllowedToBeSetGlobalConstraint implements GlobalParameterC
/**
* List of parameters to be checked.
*/
- private List<Parameter<?, ?>> parameters;
+ private List<Parameter<?>> parameters;
/**
* Constructs a global parameter constraint for testing if only one parameter
@@ -50,23 +50,22 @@ public class OnlyOneIsAllowedToBeSetGlobalConstraint implements GlobalParameterC
*
* @param params list of parameters to be checked
*/
- public OnlyOneIsAllowedToBeSetGlobalConstraint(List<Parameter<?, ?>> params) {
+ public OnlyOneIsAllowedToBeSetGlobalConstraint(List<Parameter<?>> params) {
parameters = params;
}
/**
* Checks if only one parameter of a list of parameters is set. If not, a
* parameter exception is thrown.
- *
*/
@Override
public void test() throws ParameterException {
- Vector<String> set = new Vector<String>();
- for(Parameter<?, ?> p : parameters) {
+ ArrayList<String> set = new ArrayList<String>();
+ for(Parameter<?> p : parameters) {
if(p.isDefined()) {
// FIXME: Retire the use of this constraint for Flags!
if(p instanceof Flag) {
- if (((Flag)p).getValue()) {
+ if (((Flag)p).getValue().booleanValue()) {
set.add(p.getName());
}
} else {
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/ParameterFlagGlobalConstraint.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/ParameterFlagGlobalConstraint.java
index befc0788..ff2e6675 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/ParameterFlagGlobalConstraint.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/constraints/ParameterFlagGlobalConstraint.java
@@ -28,26 +28,28 @@ import java.util.List;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.ParameterException;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.UnusedParameterException;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.Flag;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.AbstractParameter;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.Parameter;
/**
* Global parameter constraint describing the dependency of a parameter (
- * {@link Parameter}) on a given flag ({@link Flag}). Depending on the status of
- * the flag the parameter is tested for keeping its constraints or not.
+ * {@link AbstractParameter}) on a given flag ({@link Flag}). Depending on the
+ * status of the flag the parameter is tested for keeping its constraints or
+ * not.
*
* @author Steffi Wanka
*
* @apiviz.uses de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.Flag
- * @apiviz.uses de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.Parameter
+ * @apiviz.uses
+ * de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.Parameter
*
- * @param <C> Constraint type
* @param <S> Parameter type
*/
-public class ParameterFlagGlobalConstraint<S, C extends S> implements GlobalParameterConstraint {
+public class ParameterFlagGlobalConstraint<S> implements GlobalParameterConstraint {
/**
* Parameter possibly to be checked.
*/
- private Parameter<S,C> param;
+ private Parameter<S> param;
/**
* Flag the checking of the parameter constraints is dependent on.
@@ -62,7 +64,7 @@ public class ParameterFlagGlobalConstraint<S, C extends S> implements GlobalPara
/**
* List of parameter constraints.
*/
- private List<ParameterConstraint<S>> cons;
+ private List<ParameterConstraint<? super S>> cons;
/**
* Constructs a global parameter constraint specifying that the testing of the
@@ -70,12 +72,13 @@ public class ParameterFlagGlobalConstraint<S, C extends S> implements GlobalPara
* the status of the flag given.
*
* @param p parameter possibly to be checked
- * @param c a list of parameter constraints, if the value is null, the parameter is just tested if it is set.
+ * @param c a list of parameter constraints, if the value is null, the
+ * parameter is just tested if it is set.
* @param f flag controlling the checking of the parameter constraints
* @param flagConstraint indicates at which status of the flag the parameter
* is to be checked
*/
- public ParameterFlagGlobalConstraint(Parameter<S, C> p, List<ParameterConstraint<S>> c, Flag f, boolean flagConstraint) {
+ public ParameterFlagGlobalConstraint(Parameter<S> p, List<ParameterConstraint<? super S>> c, Flag f, boolean flagConstraint) {
param = p;
flag = f;
this.flagConstraint = flagConstraint;
@@ -91,9 +94,9 @@ public class ParameterFlagGlobalConstraint<S, C extends S> implements GlobalPara
@Override
public void test() throws ParameterException {
// only check constraints of param if flag is set
- if(flagConstraint == flag.getValue()) {
+ if(flagConstraint == flag.getValue().booleanValue()) {
if(cons != null) {
- for(ParameterConstraint<? super C> c : cons) {
+ for(ParameterConstraint<? super S> c : cons) {
c.test(param.getValue());
}
}
@@ -107,14 +110,14 @@ public class ParameterFlagGlobalConstraint<S, C extends S> implements GlobalPara
@Override
public String getDescription() {
- StringBuffer description = new StringBuffer();
+ StringBuilder description = new StringBuilder();
if(flagConstraint) {
description.append("If ").append(flag.getName());
description.append(" is set, the following constraints for parameter ");
description.append(param.getName()).append(" have to be fullfilled: ");
if(cons != null) {
for(int i = 0; i < cons.size(); i++) {
- ParameterConstraint<? super C> c = cons.get(i);
+ ParameterConstraint<? super S> c = cons.get(i);
if(i > 0) {
description.append(", ");
}
@@ -122,7 +125,7 @@ public class ParameterFlagGlobalConstraint<S, C extends S> implements GlobalPara
}
}
else {
- description.append(param.getName()+" must be set.");
+ description.append(param.getName()).append(" must be set.");
}
}
return description.toString();
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/package-info.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/package-info.java
index 2756327e..593c11d2 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/package-info.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/package-info.java
@@ -19,7 +19,7 @@
* </li>
*
* <li><b>Parameter Object</b>: To obtain a value, you <em>must</em> use a
- * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.Parameter Parameter}
+ * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.AbstractParameter Parameter}
* object. <br />
* Parameter objects handle <em>parsing</em> of values into the desired type, and various
* subclasses for common types are provided. It is not desirable to subclass these types
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameterization/AbstractParameterization.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameterization/AbstractParameterization.java
index 156dca1d..7cc0055e 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameterization/AbstractParameterization.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameterization/AbstractParameterization.java
@@ -23,10 +23,13 @@ package de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+import java.util.ArrayList;
import java.util.Collection;
+import java.util.List;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.utilities.ClassGenericsUtil;
+import de.lmu.ifi.dbs.elki.utilities.exceptions.AbortException;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.InternalParameterizationErrors;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.ParameterException;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GlobalParameterConstraint;
@@ -43,12 +46,12 @@ public abstract class AbstractParameterization implements Parameterization {
/**
* Errors
*/
- java.util.Vector<ParameterException> errors = new java.util.Vector<ParameterException>();
+ List<ParameterException> errors = new ArrayList<ParameterException>();
/**
* The logger of the class.
*/
- private final static Logging logger = Logging.getLogger(AbstractParameterization.class);
+ private static final Logging LOG = Logging.getLogger(AbstractParameterization.class);
@Override
public Collection<ParameterException> getErrors() {
@@ -70,11 +73,11 @@ public abstract class AbstractParameterization implements Parameterization {
*/
public synchronized void logAndClearReportedErrors() {
for(ParameterException e : getErrors()) {
- if(logger.isDebugging()) {
- logger.warning(e.getMessage(), e);
+ if(LOG.isDebugging()) {
+ LOG.warning(e.getMessage(), e);
}
else {
- logger.warning(e.getMessage());
+ LOG.warning(e.getMessage());
}
}
clearErrors();
@@ -86,7 +89,7 @@ public abstract class AbstractParameterization implements Parameterization {
public synchronized void clearErrors() {
// Do NOT use errors.clear(), since we might have an error report
// referencing the collection!
- errors = new java.util.Vector<ParameterException>();
+ errors = new ArrayList<ParameterException>();
}
/**
@@ -95,11 +98,11 @@ public abstract class AbstractParameterization implements Parameterization {
* @throws RuntimeException if any error has occurred.
*/
// TODO: make a multi-exception class?
- public void failOnErrors() throws RuntimeException {
+ public void failOnErrors() throws AbortException {
final int numerror = getErrors().size();
if(numerror > 0) {
logAndClearReportedErrors();
- throw new RuntimeException(numerror + " errors occurred during parameterization.");
+ throw new AbortException(numerror + " errors occurred during parameterization.");
}
}
@@ -117,9 +120,9 @@ public abstract class AbstractParameterization implements Parameterization {
}
@Override
- public final boolean grab(Parameter<?, ?> opt) {
+ public final boolean grab(Parameter<?> opt) {
if(opt.isDefined()) {
- logger.warning("Option " + opt.getName() + " is already set!");
+ LOG.warning("Option " + opt.getName() + " is already set!");
}
try {
if(setValueForOption(opt)) {
@@ -146,12 +149,15 @@ public abstract class AbstractParameterization implements Parameterization {
* @throws ParameterException on assignment errors.
*/
@Override
- public abstract boolean setValueForOption(Parameter<?, ?> opt) throws ParameterException;
+ public abstract boolean setValueForOption(Parameter<?> opt) throws ParameterException;
- /** Upon destruction, report any errors that weren't handled yet. */
+ /** Upon destruction, report any errors that weren't handled yet.
+ *
+ * @throws Throwable Errors */
@Override
- public void finalize() {
+ public void finalize() throws Throwable {
failOnErrors();
+ super.finalize();
}
@Override
@@ -172,7 +178,7 @@ public abstract class AbstractParameterization implements Parameterization {
return ClassGenericsUtil.tryInstantiate(r, c, this);
}
catch(Exception e) {
- logger.exception(e);
+ LOG.exception(e);
reportError(new InternalParameterizationErrors("Error instantiating internal class: "+c.getName(), e));
return null;
}
@@ -184,7 +190,7 @@ public abstract class AbstractParameterization implements Parameterization {
return ClassGenericsUtil.tryInstantiate(c, c, this);
}
catch(Exception e) {
- logger.exception(e);
+ LOG.exception(e);
reportError(new InternalParameterizationErrors("Error instantiating internal class: "+c.getName(), e));
return null;
}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameterization/ChainedParameterization.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameterization/ChainedParameterization.java
index f62b8128..a2fd168e 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameterization/ChainedParameterization.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameterization/ChainedParameterization.java
@@ -23,7 +23,8 @@ package de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.util.Vector;
+import java.util.ArrayList;
+import java.util.List;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.ParameterException;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.Parameter;
@@ -43,7 +44,7 @@ public class ChainedParameterization extends AbstractParameterization {
/**
* Keep the list of parameterizations.
*/
- private Vector<Parameterization> chain = new Vector<Parameterization>();
+ private List<Parameterization> chain = new ArrayList<Parameterization>();
/**
* Error target
@@ -73,7 +74,7 @@ public class ChainedParameterization extends AbstractParameterization {
}
@Override
- public boolean setValueForOption(Parameter<?,?> opt) throws ParameterException {
+ public boolean setValueForOption(Parameter<?> opt) throws ParameterException {
for(Parameterization p : chain) {
if(p.setValueForOption(opt)) {
return true;
@@ -102,10 +103,9 @@ public class ChainedParameterization extends AbstractParameterization {
this.errorTarget = config;
}
- /** {@inheritDoc} */
@Override
public void reportError(ParameterException e) {
- if (this.errorTarget == this) {
+ if (this.equals(this.errorTarget)) {
super.reportError(e);
} else {
this.errorTarget.reportError(e);
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameterization/EmptyParameterization.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameterization/EmptyParameterization.java
index 1fbd26b6..3391ff4a 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameterization/EmptyParameterization.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameterization/EmptyParameterization.java
@@ -23,7 +23,6 @@ package de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.ParameterException;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.Parameter;
/**
@@ -38,7 +37,7 @@ public class EmptyParameterization extends AbstractParameterization {
}
@Override
- public boolean setValueForOption(Parameter<?,?> opt) throws ParameterException {
+ public boolean setValueForOption(Parameter<?> opt) {
// Always return false, we don't have extra parameters,
// This will cause {@link AbstractParameterization} to use the default values
return false;
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameterization/ListParameterization.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameterization/ListParameterization.java
index 937bb966..7d728280 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameterization/ListParameterization.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameterization/ListParameterization.java
@@ -90,7 +90,7 @@ public class ListParameterization extends AbstractParameterization {
* @param flag Flag to add, if set
*/
public void forwardOption(Flag flag) {
- if (flag.isDefined() && flag.getValue()) {
+ if (flag.isDefined() && flag.getValue().booleanValue()) {
addFlag(flag.getOptionID());
}
}
@@ -100,14 +100,14 @@ public class ListParameterization extends AbstractParameterization {
*
* @param param Parameter to add
*/
- public void forwardOption(Parameter<?,?> param) {
+ public void forwardOption(Parameter<?> param) {
if (param.isDefined()) {
addParameter(param.getOptionID(), param.getValue());
}
}
@Override
- public boolean setValueForOption(Parameter<?,?> opt) throws ParameterException {
+ public boolean setValueForOption(Parameter<?> opt) throws ParameterException {
Iterator<Pair<OptionID, Object>> iter = parameters.iterator();
while(iter.hasNext()) {
Pair<OptionID, Object> pair = iter.next();
@@ -144,10 +144,10 @@ public class ListParameterization extends AbstractParameterization {
@Override
public String toString() {
- StringBuffer buf = new StringBuffer();
+ StringBuilder buf = new StringBuilder();
for (Pair<OptionID, Object> pair : parameters) {
- buf.append("-").append(pair.getFirst().toString()).append(" ");
- buf.append(pair.getSecond().toString()).append(" ");
+ buf.append('-').append(pair.getFirst().toString()).append(' ');
+ buf.append(pair.getSecond().toString()).append(' ');
}
return buf.toString();
}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameterization/MergedParameterization.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameterization/MergedParameterization.java
index 8bba1c2a..580a3371 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameterization/MergedParameterization.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameterization/MergedParameterization.java
@@ -24,7 +24,8 @@ package de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization;
*/
import java.util.Collection;
-import java.util.Vector;
+import java.util.ArrayList;
+import java.util.List;
import de.lmu.ifi.dbs.elki.utilities.ClassGenericsUtil;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.InternalParameterizationErrors;
@@ -57,7 +58,7 @@ public class MergedParameterization implements Parameterization {
/**
* Parameters to rewind.
*/
- final private java.util.Vector<Pair<OptionID, Object>> used;
+ final private List<Pair<OptionID, Object>> used;
/**
* Constructor.
@@ -68,7 +69,7 @@ public class MergedParameterization implements Parameterization {
super();
this.inner = child;
this.current = new ListParameterization();
- this.used = new java.util.Vector<Pair<OptionID, Object>>();
+ this.used = new ArrayList<Pair<OptionID, Object>>();
}
/**
@@ -78,7 +79,7 @@ public class MergedParameterization implements Parameterization {
* @param current Current parameterization to re-used
* @param used Used parameters list.
*/
- private MergedParameterization(Parameterization inner, ListParameterization current, Vector<Pair<OptionID, Object>> used) {
+ private MergedParameterization(Parameterization inner, ListParameterization current, List<Pair<OptionID, Object>> used) {
super();
this.inner = inner;
this.current = current;
@@ -93,12 +94,12 @@ public class MergedParameterization implements Parameterization {
for(Pair<OptionID, Object> pair : used) {
current.addParameter(pair.first, pair.second);
}
- used.removeAllElements();
+ used.clear();
}
}
@Override
- public boolean setValueForOption(Parameter<?, ?> opt) throws ParameterException {
+ public boolean setValueForOption(Parameter<?> opt) throws ParameterException {
try {
if(current.setValueForOption(opt)) {
used.add(new Pair<OptionID, Object>(opt.getOptionID(), opt.getValue()));
@@ -139,7 +140,7 @@ public class MergedParameterization implements Parameterization {
}
@Override
- public boolean grab(Parameter<?, ?> opt) {
+ public boolean grab(Parameter<?> opt) {
try {
if (setValueForOption(opt)) {
return true;
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameterization/Parameterization.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameterization/Parameterization.java
index f03463f8..5cdff013 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameterization/Parameterization.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameterization/Parameterization.java
@@ -58,7 +58,7 @@ public interface Parameterization {
* @param opt Option to add
* @return if the value is available (= readable)
*/
- public boolean grab(Parameter<?,?> opt);
+ public boolean grab(Parameter<?> opt);
/**
* Assign a value for an option, but not using default values and throwing
@@ -68,7 +68,7 @@ public interface Parameterization {
* @return Success code
* @throws ParameterException on assignment errors.
*/
- public boolean setValueForOption(Parameter<?,?> opt) throws ParameterException;
+ public boolean setValueForOption(Parameter<?> opt) throws ParameterException;
/**
* Get the configuration errors thrown in {@link #grab}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameterization/SerializedParameterization.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameterization/SerializedParameterization.java
index 73abbca0..b88b7dc4 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameterization/SerializedParameterization.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameterization/SerializedParameterization.java
@@ -108,7 +108,7 @@ public class SerializedParameterization extends AbstractParameterization {
}
@Override
- public boolean setValueForOption(Parameter<?,?> opt) throws ParameterException {
+ public boolean setValueForOption(Parameter<?> opt) throws ParameterException {
Iterator<String> piter = parameters.iterator();
while(piter.hasNext()) {
String cur = piter.next();
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameterization/TrackParameters.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameterization/TrackParameters.java
index 157efa32..424f54ee 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameterization/TrackParameters.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameterization/TrackParameters.java
@@ -23,11 +23,11 @@ package de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.Vector;
import de.lmu.ifi.dbs.elki.logging.LoggingUtil;
import de.lmu.ifi.dbs.elki.utilities.ClassGenericsUtil;
@@ -54,7 +54,7 @@ public class TrackParameters implements Parameterization {
/**
* Tracking storage
*/
- java.util.Vector<Pair<Object, Parameter<?, ?>>> options = new java.util.Vector<Pair<Object, Parameter<?, ?>>>();
+ List<Pair<Object, Parameter<?>>> options = new ArrayList<Pair<Object, Parameter<?>>>();
/**
* Tree information: parent links
@@ -85,13 +85,13 @@ public class TrackParameters implements Parameterization {
/**
* Internal constructor, for nested tracking.
*
- * @param inner
- * @param option
- * @param options
- * @param parents
- * @param children
+ * @param inner Inner parameterization
+ * @param option Option
+ * @param options List of options
+ * @param parents Parent map
+ * @param children Child map
*/
- private TrackParameters(Parameterization inner, Object option, Vector<Pair<Object, Parameter<?, ?>>> options, Map<Object, Object> parents, Map<Object, List<Object>> children) {
+ private TrackParameters(Parameterization inner, Object option, List<Pair<Object, Parameter<?>>> options, Map<Object, Object> parents, Map<Object, List<Object>> children) {
super();
this.inner = inner.descend(option);
this.cur = option;
@@ -111,9 +111,9 @@ public class TrackParameters implements Parameterization {
}
@Override
- public boolean grab(Parameter<?, ?> opt) {
+ public boolean grab(Parameter<?> opt) {
registerChild(opt);
- options.add(new Pair<Object, Parameter<?, ?>>(cur, opt));
+ options.add(new Pair<Object, Parameter<?>>(cur, opt));
return inner.grab(opt);
}
@@ -128,9 +128,9 @@ public class TrackParameters implements Parameterization {
}
@Override
- public boolean setValueForOption(Parameter<?, ?> opt) throws ParameterException {
+ public boolean setValueForOption(Parameter<?> opt) throws ParameterException {
registerChild(opt);
- options.add(new Pair<Object, Parameter<?, ?>>(cur, opt));
+ options.add(new Pair<Object, Parameter<?>>(cur, opt));
return inner.setValueForOption(opt);
}
@@ -139,7 +139,7 @@ public class TrackParameters implements Parameterization {
*
* @return Parameters seen
*/
- public Collection<Pair<Object, Parameter<?, ?>>> getAllParameters() {
+ public Collection<Pair<Object, Parameter<?>>> getAllParameters() {
return options;
}
@@ -149,8 +149,8 @@ public class TrackParameters implements Parameterization {
* @return Parameters given
*/
public Collection<Pair<OptionID, Object>> getGivenParameters() {
- java.util.Vector<Pair<OptionID, Object>> ret = new java.util.Vector<Pair<OptionID, Object>>();
- for(Pair<Object, Parameter<?, ?>> pair : options) {
+ ArrayList<Pair<OptionID, Object>> ret = new ArrayList<Pair<OptionID, Object>>();
+ for(Pair<Object, Parameter<?>> pair : options) {
if(pair.second.isDefined() && pair.second.getGivenValue() != null) {
ret.add(new Pair<OptionID, Object>(pair.second.getOptionID(), pair.second.getGivenValue()));
}
@@ -158,7 +158,6 @@ public class TrackParameters implements Parameterization {
return ret;
}
- /** {@inheritDoc} */
@Override
public boolean checkConstraint(GlobalParameterConstraint constraint) {
return inner.checkConstraint(constraint);
@@ -181,7 +180,7 @@ public class TrackParameters implements Parameterization {
parents.put(opt, cur);
List<Object> c = children.get(cur);
if(c == null) {
- c = new java.util.Vector<Object>();
+ c = new ArrayList<Object>();
children.put(cur, c);
}
if(!c.contains(opt)) {
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameterization/UnParameterization.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameterization/UnParameterization.java
index 3a25ddd4..a04b3217 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameterization/UnParameterization.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameterization/UnParameterization.java
@@ -23,7 +23,9 @@ package de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+import java.util.ArrayList;
import java.util.Collection;
+import java.util.List;
import de.lmu.ifi.dbs.elki.utilities.ClassGenericsUtil;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.InternalParameterizationErrors;
@@ -43,7 +45,7 @@ public class UnParameterization implements Parameterization {
/**
* Errors
*/
- java.util.Vector<ParameterException> errors = new java.util.Vector<ParameterException>();
+ List<ParameterException> errors = new ArrayList<ParameterException>();
@Override
public boolean hasUnusedParameters() {
@@ -66,7 +68,7 @@ public class UnParameterization implements Parameterization {
}
@Override
- public boolean grab(Parameter<?, ?> opt) {
+ public boolean grab(Parameter<?> opt) {
return false;
}
@@ -76,7 +78,7 @@ public class UnParameterization implements Parameterization {
}
@Override
- public boolean setValueForOption(Parameter<?, ?> opt) throws ParameterException {
+ public boolean setValueForOption(Parameter<?> opt) {
return false;
}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/AbstractParameter.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/AbstractParameter.java
new file mode 100644
index 00000000..ce4b094d
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/AbstractParameter.java
@@ -0,0 +1,353 @@
+package de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.security.InvalidParameterException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import de.lmu.ifi.dbs.elki.logging.LoggingUtil;
+import de.lmu.ifi.dbs.elki.utilities.FormatUtil;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.ParameterException;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.UnspecifiedParameterException;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.ParameterConstraint;
+
+/**
+ * Abstract class for specifying a parameter.
+ *
+ * A parameter is defined as an option having a specific value.
+ *
+ * See the {@link de.lmu.ifi.dbs.elki.utilities.optionhandling} package for
+ * documentation!
+ *
+ * @author Steffi Wanka
+ * @author Erich Schubert
+ *
+ * @apiviz.composedOf OptionID
+ * @apiviz.uses ParameterConstraint
+ *
+ * @param <T> the type of a possible value (i.e., the type of the option)
+ */
+public abstract class AbstractParameter<T> implements Parameter<T> {
+ /**
+ * The default value of the parameter (may be null).
+ */
+ protected T defaultValue = null;
+
+ /**
+ * Specifies if the default value of this parameter was taken as parameter
+ * value.
+ */
+ private boolean defaultValueTaken = false;
+
+ /**
+ * Specifies if this parameter is an optional parameter.
+ */
+ protected boolean optionalParameter = false;
+
+ /**
+ * Holds parameter constraints for this parameter.
+ */
+ protected List<ParameterConstraint<? super T>> constraints;
+
+ /**
+ * The option name.
+ */
+ protected final OptionID optionid;
+
+ /**
+ * The short description of the option. An extended description is provided by
+ * the method {@link #getFullDescription()}
+ */
+ protected String shortDescription;
+
+ /**
+ * The value last passed to this option.
+ */
+ protected T givenValue = null;
+
+ /**
+ * The value of this option.
+ */
+ private T value;
+
+ /**
+ * Constructs a parameter with the given optionID, constraints, and default
+ * value.
+ *
+ * @param optionID the unique id of this parameter
+ * @param defaultValue the default value of this parameter (may be null)
+ */
+ public AbstractParameter(OptionID optionID, T defaultValue) {
+ this.optionid = optionID;
+ this.shortDescription = optionID.getDescription();
+ this.optionalParameter = true;
+ this.defaultValue = defaultValue;
+ }
+
+ /**
+ * Constructs a parameter with the given optionID, constraints, and optional
+ * flag.
+ *
+ * @param optionID the unique id of this parameter
+ * @param optional specifies if this parameter is an optional parameter
+ */
+ public AbstractParameter(OptionID optionID, boolean optional) {
+ this.optionid = optionID;
+ this.shortDescription = optionID.getDescription();
+ this.optionalParameter = optional;
+ this.defaultValue = null;
+ }
+
+ /**
+ * Constructs a parameter with the given optionID, and constraints.
+ *
+ * @param optionID the unique id of this parameter
+ */
+ public AbstractParameter(OptionID optionID) {
+ this(optionID, false);
+ }
+
+ @Override
+ public void setDefaultValue(T defaultValue) {
+ this.defaultValue = defaultValue;
+ this.optionalParameter = true;
+ }
+
+ @Override
+ public boolean hasDefaultValue() {
+ return !(defaultValue == null);
+ }
+
+ // TODO: can we do this more elegantly?
+ @Override
+ public void useDefaultValue() {
+ setValueInternal(defaultValue);
+ defaultValueTaken = true;
+ }
+
+ @Override
+ public boolean tryDefaultValue() throws UnspecifiedParameterException {
+ // Assume default value instead.
+ if (hasDefaultValue()) {
+ useDefaultValue();
+ return true;
+ } else if (isOptional()) {
+ // Optional is fine, but not successful
+ return false;
+ } else {
+ throw new UnspecifiedParameterException(this);
+ }
+ }
+
+ @Override
+ public void setOptional(boolean opt) {
+ this.optionalParameter = opt;
+ }
+
+ @Override
+ public boolean isOptional() {
+ return this.optionalParameter;
+ }
+
+ @Override
+ public boolean tookDefaultValue() {
+ return defaultValueTaken;
+ }
+
+ @Override
+ public boolean isDefined() {
+ return (this.value != null);
+ }
+
+ @Override
+ public T getDefaultValue() {
+ return defaultValue;
+ }
+
+ @Override
+ public boolean hasValuesDescription() {
+ return false;
+ }
+
+ @Override
+ public String getValuesDescription() {
+ return "";
+ }
+
+ @Override
+ public String getFullDescription() {
+ StringBuilder description = new StringBuilder();
+ // description.append(getParameterType()).append(" ");
+ description.append(shortDescription);
+ description.append(FormatUtil.NEWLINE);
+ if (hasValuesDescription()) {
+ final String valuesDescription = getValuesDescription();
+ description.append(valuesDescription);
+ if (!valuesDescription.endsWith(FormatUtil.NEWLINE)) {
+ description.append(FormatUtil.NEWLINE);
+ }
+ }
+ if (hasDefaultValue()) {
+ description.append("Default: ");
+ description.append(getDefaultValueAsString());
+ description.append(FormatUtil.NEWLINE);
+ }
+ if (constraints != null && !constraints.isEmpty()) {
+ if (constraints.size() == 1) {
+ description.append("Constraint: ");
+ } else if (constraints.size() > 1) {
+ description.append("Constraints: ");
+ }
+ for (int i = 0; i < constraints.size(); i++) {
+ ParameterConstraint<? super T> constraint = constraints.get(i);
+ if (i > 0) {
+ description.append(", ");
+ }
+ description.append(constraint.getDescription(getName()));
+ if (i == constraints.size() - 1) {
+ description.append('.');
+ }
+ }
+ description.append(FormatUtil.NEWLINE);
+ }
+ return description.toString();
+ }
+
+ /**
+ * Validate a value after parsing (e.g. do constrain checks!)
+ *
+ * @param obj Object to validate
+ * @return true iff the object is valid for this parameter.
+ * @throws ParameterException when the object is not valid.
+ */
+ protected boolean validate(T obj) throws ParameterException {
+ if (constraints != null) {
+ for (ParameterConstraint<? super T> cons : this.constraints) {
+ cons.test(obj);
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public OptionID getOptionID() {
+ return optionid;
+ }
+
+ @Override
+ public String getName() {
+ return optionid.getName();
+ }
+
+ @Override
+ public String getShortDescription() {
+ return shortDescription;
+ }
+
+ @Override
+ public void setShortDescription(String description) {
+ this.shortDescription = description;
+ }
+
+ @Override
+ public void setValue(Object obj) throws ParameterException {
+ T val = parseValue(obj);
+ if (validate(val)) {
+ setValueInternal(val);
+ } else {
+ throw new InvalidParameterException("Value for option \"" + getName() + "\" did not validate: " + obj.toString());
+ }
+ }
+
+ /**
+ * Internal setter for the value.
+ *
+ * @param val Value
+ */
+ protected final void setValueInternal(T val) {
+ this.value = this.givenValue = val;
+ }
+
+ @Override
+ public final T getValue() {
+ if (this.value == null) {
+ LoggingUtil.warning("Programming error: Parameter#getValue() called for unset parameter \"" + this.optionid.getName() + "\"", new Throwable());
+ }
+ return this.value;
+ }
+
+ @Override
+ public Object getGivenValue() {
+ return this.givenValue;
+ }
+
+ @Override
+ public final boolean isValid(Object obj) throws ParameterException {
+ T val = parseValue(obj);
+ return validate(val);
+ }
+
+ @Override
+ public abstract String getSyntax();
+
+ /**
+ * Parse a given value into the destination type.
+ *
+ * @param obj Object to parse (may be a string representation!)
+ * @return Parsed object
+ * @throws ParameterException when the object cannot be parsed.
+ */
+ protected abstract T parseValue(Object obj) throws ParameterException;
+
+ @Override
+ public abstract String getValueAsString();
+
+ @Override
+ public String getDefaultValueAsString() {
+ return getDefaultValue().toString();
+ }
+
+ @Override
+ public void addConstraint(ParameterConstraint<? super T> constraint) {
+ if (constraints == null) {
+ this.constraints = new ArrayList<ParameterConstraint<? super T>>(1);
+ }
+ constraints.add(constraint);
+ }
+
+ /**
+ * Add a collection of constraints.
+ *
+ * @param cs Constraints to add
+ */
+ public void addConstraints(Collection<? extends ParameterConstraint<? super T>> cs) {
+ if (constraints == null) {
+ this.constraints = new ArrayList<ParameterConstraint<? super T>>(cs.size());
+ }
+ constraints.addAll(cs);
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/ClassListParameter.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/ClassListParameter.java
index 85851e5b..f87e3973 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/ClassListParameter.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/ClassListParameter.java
@@ -31,7 +31,6 @@ import de.lmu.ifi.dbs.elki.utilities.FormatUtil;
import de.lmu.ifi.dbs.elki.utilities.InspectionUtil;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.ParameterException;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.UnspecifiedParameterException;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.UnusedParameterException;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.WrongParameterValueException;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
@@ -85,10 +84,9 @@ public class ClassListParameter<C> extends ListParameter<Class<? extends C>> {
this.restrictionClass = (Class<C>) restrictionClass;
}
- /** {@inheritDoc} */
@Override
public String getValueAsString() {
- StringBuffer buf = new StringBuffer();
+ StringBuilder buf = new StringBuilder();
final String defPackage = restrictionClass.getPackage().getName() + ".";
for(Class<? extends C> c : getValue()) {
if(buf.length() > 0) {
@@ -103,7 +101,6 @@ public class ClassListParameter<C> extends ListParameter<Class<? extends C>> {
return buf.toString();
}
- /** {@inheritDoc} */
@SuppressWarnings("unchecked")
@Override
protected List<Class<? extends C>> parseValue(Object obj) throws ParameterException {
@@ -137,7 +134,7 @@ public class ClassListParameter<C> extends ListParameter<Class<? extends C>> {
String[] classes = SPLIT.split((String) obj);
// TODO: allow empty lists (and list constraints) to enforce length?
if(classes.length == 0) {
- throw new UnspecifiedParameterException("Wrong parameter format! Given list of classes for parameter \"" + getName() + "\" is either empty or has the wrong format!");
+ throw new WrongParameterValueException("Wrong parameter format! Given list of classes for parameter \"" + getName() + "\" is either empty or has the wrong format!");
}
List<Class<? extends C>> cls = new ArrayList<Class<? extends C>>(classes.length);
@@ -169,7 +166,6 @@ public class ClassListParameter<C> extends ListParameter<Class<? extends C>> {
throw new WrongParameterValueException("Wrong parameter format! Parameter \"" + getName() + "\" requires a list of Class values!");
}
- /** {@inheritDoc} */
@Override
protected boolean validate(List<Class<? extends C>> obj) throws ParameterException {
for(Class<? extends C> cls : obj) {
@@ -282,7 +278,7 @@ public class ClassListParameter<C> extends ListParameter<Class<? extends C>> {
/**
* This class sometimes provides a list of value descriptions.
*
- * @see de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.Parameter#hasValuesDescription()
+ * @see de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.AbstractParameter#hasValuesDescription()
*/
@Override
public boolean hasValuesDescription() {
@@ -292,7 +288,7 @@ public class ClassListParameter<C> extends ListParameter<Class<? extends C>> {
/**
* Return a description of known valid classes.
*
- * @see de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.Parameter#getValuesDescription()
+ * @see de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.AbstractParameter#getValuesDescription()
*/
@Override
public String getValuesDescription() {
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/ClassParameter.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/ClassParameter.java
index 6b056c03..6d63f9b5 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/ClassParameter.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/ClassParameter.java
@@ -49,7 +49,7 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameteriz
*/
// TODO: add additional constructors with parameter constraints.
// TODO: turn restrictionClass into a constraint?
-public class ClassParameter<C> extends Parameter<Class<?>, Class<? extends C>> {
+public class ClassParameter<C> extends AbstractParameter<Class<? extends C>> {
/**
* Class loader.
*/
@@ -83,7 +83,7 @@ public class ClassParameter<C> extends Parameter<Class<?>, Class<? extends C>> {
// * ClassParameter<Foo<Bar>>(optionID, (Class<Foo<Bar>>) Foo.class) is an
// invalid cast.
this.restrictionClass = (Class<C>) restrictionClass;
- if(restrictionClass == null) {
+ if (restrictionClass == null) {
LoggingUtil.warning("Restriction class 'null' for parameter '" + optionID + "'", new Throwable());
}
}
@@ -106,7 +106,7 @@ public class ClassParameter<C> extends Parameter<Class<?>, Class<? extends C>> {
// * ClassParameter<Foo<Bar>>(optionID, (Class<Foo<Bar>>) Foo.class) is an
// invalid cast.
this.restrictionClass = (Class<C>) restrictionClass;
- if(restrictionClass == null) {
+ if (restrictionClass == null) {
LoggingUtil.warning("Restriction class 'null' for parameter '" + optionID + "'", new Throwable());
}
}
@@ -122,43 +122,38 @@ public class ClassParameter<C> extends Parameter<Class<?>, Class<? extends C>> {
this(optionID, restrictionClass, false);
}
- /** {@inheritDoc} */
@SuppressWarnings("unchecked")
@Override
protected Class<? extends C> parseValue(Object obj) throws ParameterException {
- if(obj == null) {
- throw new UnspecifiedParameterException("Parameter Error.\n" + "No value for parameter \"" + getName() + "\" " + "given.");
+ if (obj == null) {
+ throw new UnspecifiedParameterException(this);
}
- if(obj instanceof Class<?>) {
+ if (obj instanceof Class<?>) {
return (Class<? extends C>) obj;
}
- if(obj instanceof String) {
+ if (obj instanceof String) {
String value = (String) obj;
try {
// Try exact class factory first.
try {
return (Class<? extends C>) loader.loadClass(value + FACTORY_POSTFIX);
- }
- catch(ClassNotFoundException e) {
+ } catch (ClassNotFoundException e) {
// Ignore, retry
}
try {
return (Class<? extends C>) loader.loadClass(value);
- }
- catch(ClassNotFoundException e) {
+ } catch (ClassNotFoundException e) {
// Ignore, retry
}
// Try factory for guessed name next
try {
return (Class<? extends C>) loader.loadClass(restrictionClass.getPackage().getName() + "." + value + FACTORY_POSTFIX);
- }
- catch(ClassNotFoundException e) {
+ } catch (ClassNotFoundException e) {
// Ignore, retry
}
// Last try: guessed name prefix only
return (Class<? extends C>) loader.loadClass(restrictionClass.getPackage().getName() + "." + value);
- }
- catch(ClassNotFoundException e) {
+ } catch (ClassNotFoundException e) {
throw new WrongParameterValueException(this, value, "Given class \"" + value + "\" not found.", e);
}
}
@@ -171,13 +166,13 @@ public class ClassParameter<C> extends Parameter<Class<?>, Class<? extends C>> {
*/
@Override
public boolean validate(Class<? extends C> obj) throws ParameterException {
- if(obj == null) {
- throw new UnspecifiedParameterException("Parameter Error.\n" + "No value for parameter \"" + getName() + "\" " + "given.");
+ if (obj == null) {
+ throw new UnspecifiedParameterException(this);
}
- if(!restrictionClass.isAssignableFrom(obj)) {
+ if (!restrictionClass.isAssignableFrom(obj)) {
throw new WrongParameterValueException(this, obj.getName(), "Given class not a subclass / implementation of " + restrictionClass.getName());
}
- if(!super.validate(obj)) {
+ if (!super.validate(obj)) {
return false;
}
return true;
@@ -196,7 +191,7 @@ public class ClassParameter<C> extends Parameter<Class<?>, Class<? extends C>> {
/**
* This class sometimes provides a list of value descriptions.
*
- * @see de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.Parameter#hasValuesDescription()
+ * @see de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.AbstractParameter#hasValuesDescription()
*/
@Override
public boolean hasValuesDescription() {
@@ -206,11 +201,11 @@ public class ClassParameter<C> extends Parameter<Class<?>, Class<? extends C>> {
/**
* Return a description of known valid classes.
*
- * @see de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.Parameter#getValuesDescription()
+ * @see de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.AbstractParameter#getValuesDescription()
*/
@Override
public String getValuesDescription() {
- if(restrictionClass != null && restrictionClass != Object.class) {
+ if (restrictionClass != null && restrictionClass != Object.class) {
return restrictionString();
}
return "";
@@ -234,28 +229,22 @@ public class ClassParameter<C> extends Parameter<Class<?>, Class<? extends C>> {
*/
public C instantiateClass(Parameterization config) {
try {
- if(getValue() == null /* && !optionalParameter */) {
+ if (getValue() == null /* && !optionalParameter */) {
throw new UnusedParameterException("Value of parameter " + getName() + " has not been specified.");
}
C instance;
try {
config = config.descend(this);
instance = ClassGenericsUtil.tryInstantiate(restrictionClass, getValue(), config);
- }
- catch(InvocationTargetException e) {
- // inner exception during instantiation. Log, so we don't lose it!
- LoggingUtil.exception(e);
+ } catch (InvocationTargetException e) {
throw new WrongParameterValueException(this, getValue().getCanonicalName(), "Error instantiating class.", e);
- }
- catch(NoSuchMethodException e) {
+ } catch (NoSuchMethodException e) {
throw new WrongParameterValueException(this, getValue().getCanonicalName(), "Error instantiating class - no usable public constructor.");
- }
- catch(Exception e) {
+ } catch (Exception e) {
throw new WrongParameterValueException(this, getValue().getCanonicalName(), "Error instantiating class.", e);
}
return instance;
- }
- catch(ParameterException e) {
+ } catch (ParameterException e) {
config.reportError(e);
return null;
}
@@ -288,20 +277,19 @@ public class ClassParameter<C> extends Parameter<Class<?>, Class<? extends C>> {
*/
public String restrictionString() {
StringBuilder info = new StringBuilder();
- if(restrictionClass.isInterface()) {
+ if (restrictionClass.isInterface()) {
info.append("Implementing ");
- }
- else {
+ } else {
info.append("Extending ");
}
info.append(restrictionClass.getName());
info.append(FormatUtil.NEWLINE);
List<Class<?>> known = getKnownImplementations();
- if(!known.isEmpty()) {
+ if (!known.isEmpty()) {
info.append("Known classes (default package " + restrictionClass.getPackage().getName() + "):");
info.append(FormatUtil.NEWLINE);
- for(Class<?> c : known) {
+ for (Class<?> c : known) {
info.append("->" + FormatUtil.NONBREAKING_SPACE);
info.append(canonicalClassName(c, getRestrictionClass()));
info.append(FormatUtil.NEWLINE);
@@ -321,13 +309,13 @@ public class ClassParameter<C> extends Parameter<Class<?>, Class<? extends C>> {
*/
public static String canonicalClassName(Class<?> c, Package pkg, String postfix) {
String name = c.getName();
- if(pkg != null) {
+ if (pkg != null) {
String prefix = pkg.getName() + ".";
- if(name.startsWith(prefix)) {
+ if (name.startsWith(prefix)) {
name = name.substring(prefix.length());
}
}
- if(postfix != null && name.endsWith(postfix)) {
+ if (postfix != null && name.endsWith(postfix)) {
name = name.substring(0, name.length() - postfix.length());
}
return name;
@@ -341,7 +329,7 @@ public class ClassParameter<C> extends Parameter<Class<?>, Class<? extends C>> {
* @return Simplified class name.
*/
public static String canonicalClassName(Class<?> c, Class<?> parent) {
- if(parent == null) {
+ if (parent == null) {
return canonicalClassName(c, null, FACTORY_POSTFIX);
}
return canonicalClassName(c, parent.getPackage(), FACTORY_POSTFIX);
@@ -351,4 +339,4 @@ public class ClassParameter<C> extends Parameter<Class<?>, Class<? extends C>> {
public String getDefaultValueAsString() {
return canonicalClassName(getDefaultValue(), getRestrictionClass());
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/DistanceParameter.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/DistanceParameter.java
index bb9dc60c..f9943610 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/DistanceParameter.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/DistanceParameter.java
@@ -23,13 +23,10 @@ package de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.util.List;
-
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.WrongParameterValueException;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.ParameterConstraint;
/**
* Parameter class for a parameter specifying a double value.
@@ -39,177 +36,11 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.ParameterConstra
*
* @param <D> Distance type
*/
-public class DistanceParameter<D extends Distance<D>> extends Parameter<D, D> {
+public class DistanceParameter<D extends Distance<D>> extends AbstractParameter<D> {
/**
* Distance type
*/
D dist;
-
- /**
- * Constructs a double parameter with the given optionID, parameter
- * constraints, and default value.
- *
- * @param optionID the unique optionID
- * @param dist distance factory
- * @param cons a list of parameter constraints for this double parameter
- * @param defaultValue the default value for this double parameter
- */
- public DistanceParameter(OptionID optionID, D dist, List<ParameterConstraint<D>> cons, D defaultValue) {
- super(optionID, cons, defaultValue);
- this.dist = dist;
- }
-
- /**
- * Constructs a double parameter with the given optionID, parameter
- * constraints, and default value.
- *
- * @param optionID the unique optionID
- * @param dist distance factory
- * @param cons a list of parameter constraints for this double parameter
- * @param defaultValue the default value for this double parameter
- */
- public DistanceParameter(OptionID optionID, DistanceFunction<?, D> dist, List<ParameterConstraint<D>> cons, D defaultValue) {
- super(optionID, cons, defaultValue);
- this.dist = (dist != null) ? dist.getDistanceFactory() : null;
- }
-
- /**
- * Constructs a double parameter with the given optionID, parameter
- * constraints, and optional flag.
- *
- * @param optionID the unique optionID
- * @param dist distance factory
- * @param cons a list of parameter constraints for this double parameter
- * @param optional specifies whether this parameter is an optional parameter
- */
- public DistanceParameter(OptionID optionID, D dist, List<ParameterConstraint<D>> cons, boolean optional) {
- this(optionID, dist, cons);
- setOptional(optional);
- }
-
- /**
- * Constructs a double parameter with the given optionID, parameter
- * constraints, and optional flag.
- *
- * @param optionID the unique optionID
- * @param dist distance factory
- * @param cons a list of parameter constraints for this double parameter
- * @param optional specifies whether this parameter is an optional parameter
- */
- public DistanceParameter(OptionID optionID, DistanceFunction<?, D> dist, List<ParameterConstraint<D>> cons, boolean optional) {
- this(optionID, dist, cons);
- setOptional(optional);
- }
-
- /**
- * Constructs a double parameter with the given optionID, and parameter
- * constraints.
- *
- * @param optionID the unique optionID
- * @param dist distance factory
- * @param constraints a list of parameter constraints for this double
- * parameter
- */
- public DistanceParameter(OptionID optionID, D dist, List<ParameterConstraint<D>> constraints) {
- super(optionID, constraints);
- this.dist = dist;
- }
-
- /**
- * Constructs a double parameter with the given optionID, and parameter
- * constraints.
- *
- * @param optionID the unique optionID
- * @param dist distance factory
- * @param constraints a list of parameter constraints for this double
- * parameter
- */
- public DistanceParameter(OptionID optionID, DistanceFunction<?, D> dist, List<ParameterConstraint<D>> constraints) {
- super(optionID, constraints);
- this.dist = (dist != null) ? dist.getDistanceFactory() : null;
- }
-
- /**
- * Constructs a double parameter with the given optionID, parameter
- * constraint, and default value.
- *
- * @param optionID the unique id of this parameter
- * @param dist distance factory
- * @param constraint the constraint of this parameter
- * @param defaultValue the default value for this parameter
- */
- public DistanceParameter(OptionID optionID, D dist, ParameterConstraint<D> constraint, D defaultValue) {
- super(optionID, constraint, defaultValue);
- this.dist = dist;
- }
-
- /**
- * Constructs a double parameter with the given optionID, parameter
- * constraint, and default value.
- *
- * @param optionID the unique id of this parameter
- * @param dist distance factory
- * @param constraint the constraint of this parameter
- * @param defaultValue the default value for this parameter
- */
- public DistanceParameter(OptionID optionID, DistanceFunction<?, D> dist, ParameterConstraint<D> constraint, D defaultValue) {
- super(optionID, constraint, defaultValue);
- this.dist = (dist != null) ? dist.getDistanceFactory() : null;
- }
-
- /**
- * Constructs a double parameter with the given optionID, parameter
- * constraint, and optional flag.
- *
- * @param optionID the unique id of this parameter
- * @param dist distance factory
- * @param constraint the constraint of this parameter
- * @param optional specifies whether this parameter is an optional parameter
- */
- public DistanceParameter(OptionID optionID, D dist, ParameterConstraint<D> constraint, boolean optional) {
- super(optionID, constraint, optional);
- this.dist = dist;
- }
-
- /**
- * Constructs a double parameter with the given optionID, parameter
- * constraint, and optional flag.
- *
- * @param optionID the unique id of this parameter
- * @param dist distance factory
- * @param constraint the constraint of this parameter
- * @param optional specifies whether this parameter is an optional parameter
- */
- public DistanceParameter(OptionID optionID, DistanceFunction<?, D> dist, ParameterConstraint<D> constraint, boolean optional) {
- super(optionID, constraint, optional);
- this.dist = (dist != null) ? dist.getDistanceFactory() : null;
- }
-
- /**
- * Constructs a double parameter with the given optionID, and parameter
- * constraint.
- *
- * @param optionID the unique id of this parameter
- * @param dist distance factory
- * @param constraint the constraint of this parameter
- */
- public DistanceParameter(OptionID optionID, D dist, ParameterConstraint<D> constraint) {
- super(optionID, constraint);
- this.dist = dist;
- }
-
- /**
- * Constructs a double parameter with the given optionID, and parameter
- * constraint.
- *
- * @param optionID the unique id of this parameter
- * @param dist distance factory
- * @param constraint the constraint of this parameter
- */
- public DistanceParameter(OptionID optionID, DistanceFunction<?, D> dist, ParameterConstraint<D> constraint) {
- super(optionID, constraint);
- this.dist = (dist != null) ? dist.getDistanceFactory() : null;
- }
/**
* Constructs a double parameter with the given optionID and default value.
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/DoubleListParameter.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/DoubleListParameter.java
index 07b10b83..885a3bfc 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/DoubleListParameter.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/DoubleListParameter.java
@@ -30,7 +30,6 @@ import de.lmu.ifi.dbs.elki.utilities.FormatUtil;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.ParameterException;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.WrongParameterValueException;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.ParameterConstraint;
/**
* Parameter class for a parameter specifying a list of double values.
@@ -40,72 +39,6 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.ParameterConstra
*/
public class DoubleListParameter extends ListParameter<Double> {
/**
- * Constructs a list parameter with the given optionID.
- *
- * @param optionID Option ID
- * @param constraints Constraints
- * @param defaultValue Default value
- */
- public DoubleListParameter(OptionID optionID, List<ParameterConstraint<List<Double>>> constraints, List<Double> defaultValue) {
- super(optionID, constraints, defaultValue);
- }
-
- /**
- * Constructs a list parameter with the given optionID.
- *
- * @param optionID Option ID
- * @param constraints Constraints
- * @param optional Optional flag
- */
- public DoubleListParameter(OptionID optionID, List<ParameterConstraint<List<Double>>> constraints, boolean optional) {
- super(optionID, constraints, optional);
- }
-
- /**
- * Constructs a list parameter with the given optionID.
- *
- * @param optionID Option ID
- * @param constraints Constraints
- */
- /*
- * public DoubleListParameter(OptionID optionID,
- * List<ParameterConstraint<List<Double>>> constraints) { super(optionID,
- * constraints); }
- */
-
- /**
- * Constructs a list parameter with the given optionID.
- *
- * @param optionID Option ID
- * @param constraint Constraint
- * @param defaultValue Default value
- */
- public DoubleListParameter(OptionID optionID, ParameterConstraint<List<Double>> constraint, List<Double> defaultValue) {
- super(optionID, constraint, defaultValue);
- }
-
- /**
- * Constructs a list parameter with the given optionID.
- *
- * @param optionID Option ID
- * @param constraint Constraint
- * @param optional Optional flag
- */
- public DoubleListParameter(OptionID optionID, ParameterConstraint<List<Double>> constraint, boolean optional) {
- super(optionID, constraint, optional);
- }
-
- /**
- * Constructs a list parameter with the given optionID.
- *
- * @param optionID Option ID
- * @param constraint Constraint
- */
- public DoubleListParameter(OptionID optionID, ParameterConstraint<List<Double>> constraint) {
- super(optionID, constraint);
- }
-
- /**
* Constructs a list parameter with the given optionID and optional flag.
*
* @param optionID Option ID
@@ -126,7 +59,7 @@ public class DoubleListParameter extends ListParameter<Double> {
@Override
public String getValueAsString() {
- return FormatUtil.format(getValue().toArray(new Double[0]), LIST_SEP, FormatUtil.NF);
+ return FormatUtil.format(getValue().toArray(new Double[getValue().size()]), LIST_SEP, FormatUtil.NF);
}
@SuppressWarnings("unchecked")
@@ -150,7 +83,7 @@ public class DoubleListParameter extends ListParameter<Double> {
String[] values = SPLIT.split((String) obj);
ArrayList<Double> doubleValue = new ArrayList<Double>(values.length);
for(String val : values) {
- doubleValue.add(Double.parseDouble(val));
+ doubleValue.add(Double.valueOf(val));
}
return doubleValue;
}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/DoubleParameter.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/DoubleParameter.java
index 9ebb61ee..4aee3511 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/DoubleParameter.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/DoubleParameter.java
@@ -23,8 +23,6 @@ package de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.util.List;
-
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.WrongParameterValueException;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.ParameterConstraint;
@@ -38,63 +36,17 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.ParameterConstra
public class DoubleParameter extends NumberParameter<Double> {
/**
* Constructs a double parameter with the given optionID, parameter
- * constraints, and default value.
- *
- * @param optionID the unique optionID
- * @param cons a list of parameter constraints for this double parameter
- * @param defaultValue the default value for this double parameter
- */
- public DoubleParameter(OptionID optionID, List<ParameterConstraint<Number>> cons, Double defaultValue) {
- super(optionID, cons, defaultValue);
- }
-
- /**
- * Constructs a double parameter with the given optionID, parameter
- * constraints, and optional flag.
- *
- * @param optionID the unique optionID
- * @param cons a list of parameter constraints for this double parameter
- * @param optional specifies whether this parameter is an optional parameter
- */
- public DoubleParameter(OptionID optionID, List<ParameterConstraint<Number>> cons, boolean optional) {
- this(optionID, cons);
- setOptional(optional);
- }
-
- /**
- * Constructs a double parameter with the given optionID, and parameter
- * constraints.
- *
- * @param optionID the unique optionID
- * @param constraints a list of parameter constraints for this double
- * parameter
- */
- public DoubleParameter(OptionID optionID, List<ParameterConstraint<Number>> constraints) {
- super(optionID, constraints);
- }
-
- /**
- * Constructs a double parameter with the given optionID, parameter
* constraint, and default value.
*
* @param optionID the unique id of this parameter
- * @param constraint the constraint of this parameter
* @param defaultValue the default value for this parameter
- */
- public DoubleParameter(OptionID optionID, ParameterConstraint<Number> constraint, Double defaultValue) {
- super(optionID, constraint, defaultValue);
- }
-
- /**
- * Constructs a double parameter with the given optionID, parameter
- * constraint, and optional flag.
- *
- * @param optionID the unique id of this parameter
* @param constraint the constraint of this parameter
- * @param optional specifies whether this parameter is an optional parameter
+ * @deprecated Use {@link #addConstraint} instead.
*/
- public DoubleParameter(OptionID optionID, ParameterConstraint<Number> constraint, boolean optional) {
- super(optionID, constraint, optional);
+ @Deprecated
+ public DoubleParameter(OptionID optionID, double defaultValue, ParameterConstraint<Number> constraint) {
+ super(optionID, defaultValue);
+ addConstraint(constraint);
}
/**
@@ -103,9 +55,12 @@ public class DoubleParameter extends NumberParameter<Double> {
*
* @param optionID the unique id of this parameter
* @param constraint the constraint of this parameter
+ * @deprecated Use {@link #addConstraint} instead.
*/
+ @Deprecated
public DoubleParameter(OptionID optionID, ParameterConstraint<Number> constraint) {
- super(optionID, constraint);
+ super(optionID);
+ addConstraint(constraint);
}
/**
@@ -114,18 +69,22 @@ public class DoubleParameter extends NumberParameter<Double> {
* @param optionID the unique optionID
* @param defaultValue the default value for this double parameter
*/
- public DoubleParameter(OptionID optionID, Double defaultValue) {
+ public DoubleParameter(OptionID optionID, double defaultValue) {
super(optionID, defaultValue);
}
/**
- * Constructs a double parameter with the given optionID and optional flag.
+ * Constructs a double parameter with the given optionID and default value.
*
- * @param optionID the unique id of this parameter
- * @param optional specifies whether this parameter is an optional parameter
+ * @param optionID the unique optionID
+ * @param optional Flag to indicate that the parameter is optional
+ *
+ * @deprecated Use {@link #setOptional} instead.
*/
+ @Deprecated
public DoubleParameter(OptionID optionID, boolean optional) {
- super(optionID, optional);
+ super(optionID);
+ setOptional(optional);
}
/**
@@ -137,53 +96,26 @@ public class DoubleParameter extends NumberParameter<Double> {
super(optionID);
}
- /** {@inheritDoc} */
@Override
public String getValueAsString() {
- return Double.toString(getValue());
+ return getValue().toString();
}
- /** {@inheritDoc} */
@Override
protected Double parseValue(Object obj) throws WrongParameterValueException {
- if(obj instanceof Double) {
+ if (obj instanceof Double) {
return (Double) obj;
}
try {
- return Double.parseDouble(obj.toString());
- }
- catch(NullPointerException e) {
+ return Double.valueOf(obj.toString());
+ } catch (NullPointerException e) {
throw new WrongParameterValueException("Wrong parameter format! Parameter \"" + getName() + "\" requires a double value, read: " + obj + "!\n");
- }
- catch(NumberFormatException e) {
+ } catch (NumberFormatException e) {
throw new WrongParameterValueException("Wrong parameter format! Parameter \"" + getName() + "\" requires a double value, read: " + obj + "!\n");
}
}
/**
- * Indicates whether some other object is "equal to" this one.
- *
- * @param obj the reference object with which to compare.
- * @return <code>true</code> if this double parameter has the same value as
- * the specified object, <code>false</code> otherwise.
- */
- // TODO: comparing the parameters doesn't make sense. REMOVE.
- /*@Override
- public boolean equals(Object obj) {
- if(obj == this) {
- return true;
- }
- if(!(obj instanceof DoubleParameter)) {
- return false;
- }
- DoubleParameter oth = (DoubleParameter) obj;
- if(this.getValue() == null) {
- return (oth.getValue() == null);
- }
- return this.getValue().equals(oth.getValue());
- }*/
-
- /**
* Returns a string representation of the parameter's type.
*
* @return &quot;&lt;double&gt;&quot;
@@ -192,4 +124,13 @@ public class DoubleParameter extends NumberParameter<Double> {
public String getSyntax() {
return "<double>";
}
+
+ /**
+ * Get the parameter value as double.
+ *
+ * @return double value
+ */
+ public double doubleValue() {
+ return getValue().doubleValue();
+ }
}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/EnumParameter.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/EnumParameter.java
index afab86c8..c2e1c733 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/EnumParameter.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/EnumParameter.java
@@ -62,7 +62,7 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.WrongParameterValueException
*
* @param <E> Enum type
*/
-public class EnumParameter<E extends Enum<E>> extends Parameter<Enum<E>, E> {
+public class EnumParameter<E extends Enum<E>> extends AbstractParameter<E> {
/**
* Reference to the actual enum type, for T.valueOf().
@@ -112,7 +112,7 @@ public class EnumParameter<E extends Enum<E>> extends Parameter<Enum<E>, E> {
@Override
protected E parseValue(Object obj) throws ParameterException {
if(obj == null) {
- throw new UnspecifiedParameterException("Parameter \"" + getName() + "\": Null value given!");
+ throw new UnspecifiedParameterException(this);
}
if(obj instanceof String) {
try {
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/FileListParameter.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/FileListParameter.java
index 1b72df80..203712aa 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/FileListParameter.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/FileListParameter.java
@@ -75,10 +75,9 @@ public class FileListParameter extends ListParameter<File> {
// TODO: Add remaining constructors.
- /** {@inheritDoc} */
@Override
public String getValueAsString() {
- StringBuffer buf = new StringBuffer();
+ StringBuilder buf = new StringBuilder();
List<File> val = getValue();
Iterator<File> veciter = val.iterator();
while(veciter.hasNext()) {
@@ -90,7 +89,6 @@ public class FileListParameter extends ListParameter<File> {
return buf.toString();
}
- /** {@inheritDoc} */
@SuppressWarnings("unchecked")
@Override
protected List<File> parseValue(Object obj) throws ParameterException {
@@ -119,7 +117,6 @@ public class FileListParameter extends ListParameter<File> {
throw new WrongParameterValueException("Wrong parameter format! Parameter \"" + getName() + "\" requires a list of file values!");
}
- /** {@inheritDoc} */
@Override
protected boolean validate(List<File> obj) throws ParameterException {
if(!super.validate(obj)) {
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/FileParameter.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/FileParameter.java
index 1e04c7ae..2cc5ab37 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/FileParameter.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/FileParameter.java
@@ -38,7 +38,7 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.WrongParameterValueException
* @author Erich Schubert
*/
// TODO: turn FileType into a Constraint?
-public class FileParameter extends Parameter<File, File> {
+public class FileParameter extends AbstractParameter<File> {
/**
* Available file types: {@link #INPUT_FILE} denotes an input file,
* {@link #OUTPUT_FILE} denotes an output file.
@@ -86,7 +86,6 @@ public class FileParameter extends Parameter<File, File> {
setOptional(optional);
}
- /** {@inheritDoc} */
@Override
public String getValueAsString() {
try {
@@ -97,11 +96,10 @@ public class FileParameter extends Parameter<File, File> {
}
}
- /** {@inheritDoc} */
@Override
protected File parseValue(Object obj) throws ParameterException {
if(obj == null) {
- throw new UnspecifiedParameterException("Parameter \"" + getName() + "\": No filename given!");
+ throw new UnspecifiedParameterException(this);
}
if(obj instanceof File) {
return (File) obj;
@@ -109,10 +107,9 @@ public class FileParameter extends Parameter<File, File> {
if(obj instanceof String) {
return new File((String) obj);
}
- throw new UnspecifiedParameterException("Parameter \"" + getName() + "\": Unsupported value given!");
+ throw new WrongParameterValueException("Parameter \"" + getName() + "\": Unsupported value given!");
}
- /** {@inheritDoc} */
@Override
protected boolean validate(File obj) throws ParameterException {
if(!super.validate(obj)) {
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/Flag.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/Flag.java
index e2ce63c2..90230077 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/Flag.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/Flag.java
@@ -37,7 +37,7 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.WrongParameterValueException
* @author Steffi Wanka
* @author Erich Schubert
*/
-public class Flag extends Parameter<Boolean, Boolean> {
+public class Flag extends AbstractParameter<Boolean> {
/**
* Constant indicating that the flag is set.
*/
@@ -58,25 +58,25 @@ public class Flag extends Parameter<Boolean, Boolean> {
public Flag(OptionID optionID) {
super(optionID);
setOptional(true);
- setDefaultValue(false);
+ setDefaultValue(Boolean.FALSE);
}
@Override
protected Boolean parseValue(Object obj) throws ParameterException {
if(SET.equals(obj)) {
- return true;
+ return Boolean.TRUE;
}
if(NOT_SET.equals(obj)) {
- return false;
+ return Boolean.FALSE;
}
if(obj instanceof Boolean) {
return (Boolean) obj;
}
if(obj != null && SET.equals(obj.toString())) {
- return true;
+ return Boolean.TRUE;
}
if(obj != null && NOT_SET.equals(obj.toString())) {
- return false;
+ return Boolean.FALSE;
}
throw new WrongParameterValueException("Wrong value for flag \"" + getName() + "\". Allowed values:\n" + SET + " or " + NOT_SET);
}
@@ -89,15 +89,11 @@ public class Flag extends Parameter<Boolean, Boolean> {
return "<|" + SET + "|" + NOT_SET + ">";
}
- /** {@inheritDoc} */
@Override
public String getValueAsString() {
- return getValue() ? SET : NOT_SET;
+ return getValue().booleanValue() ? SET : NOT_SET;
}
- /**
- * {@inheritDoc}
- */
@Override
protected boolean validate(Boolean obj) throws ParameterException {
if(obj == null) {
@@ -114,7 +110,7 @@ public class Flag extends Parameter<Boolean, Boolean> {
*/
public void setValue(boolean val) {
try {
- super.setValue(val);
+ super.setValue(Boolean.valueOf(val));
}
catch(ParameterException e) {
// We're pretty sure that any Boolean is okay, so this should never be
@@ -129,7 +125,7 @@ public class Flag extends Parameter<Boolean, Boolean> {
* @return true when defined and true.
*/
public boolean isTrue() {
- return isDefined() && getValue();
+ return isDefined() && getValue().booleanValue();
}
/**
@@ -138,6 +134,6 @@ public class Flag extends Parameter<Boolean, Boolean> {
* @return true when defined and true.
*/
public boolean isFalse() {
- return isDefined() && !getValue();
+ return isDefined() && !getValue().booleanValue();
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/IntListParameter.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/IntListParameter.java
index d7b5d570..f61f8842 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/IntListParameter.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/IntListParameter.java
@@ -30,7 +30,6 @@ import java.util.List;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.ParameterException;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.WrongParameterValueException;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.ParameterConstraint;
/**
* Parameter class for a parameter specifying a list of integer values.
@@ -43,80 +42,6 @@ public class IntListParameter extends ListParameter<Integer> {
* Constructs an integer list parameter
*
* @param optionID the unique id of this parameter
- * @param constraints the constraints of this parameter, may be null
- * @param defaultValue the default value
- */
- public IntListParameter(OptionID optionID, List<ParameterConstraint<List<Integer>>> constraints, List<Integer> defaultValue) {
- super(optionID, constraints, defaultValue);
- }
-
- /**
- * Constructs an integer list parameter
- *
- * @param optionID the unique id of this parameter
- * @param constraints the constraints of this parameter, may be null
- * @param optional specifies if this parameter is an optional parameter
- */
- public IntListParameter(OptionID optionID, List<ParameterConstraint<List<Integer>>> constraints, boolean optional) {
- super(optionID, constraints, optional);
- }
-
- /**
- * Constructs an integer list parameter
- *
- * @param optionID the unique id of this parameter
- * @param constraints the constraints of this parameter, may be null
- */
- /*public IntListParameter(OptionID optionID, List<ParameterConstraint<List<Integer>>> constraints) {
- super(optionID, constraints);
- } */
-
- /**
- * Constructs an integer list parameter
- *
- * @param optionID the unique id of this parameter
- * @param constraint the constraint of this parameter, may be null
- * @param defaultValue the default value
- */
- public IntListParameter(OptionID optionID, ParameterConstraint<List<Integer>> constraint, List<Integer> defaultValue) {
- super(optionID, constraint, defaultValue);
- }
-
- /**
- * Constructs an integer list parameter
- *
- * @param optionID the unique id of this parameter
- * @param constraint the constraint of this parameter, may be null
- * @param optional specifies if this parameter is an optional parameter
- */
- public IntListParameter(OptionID optionID, ParameterConstraint<List<Integer>> constraint, boolean optional) {
- super(optionID, constraint, optional);
- }
-
- /**
- * Constructs an integer list parameter
- *
- * @param optionID the unique id of this parameter
- * @param constraint the constraint of this parameter, may be null
- */
- public IntListParameter(OptionID optionID, ParameterConstraint<List<Integer>> constraint) {
- super(optionID, constraint);
- }
-
- /**
- * Constructs an integer list parameter
- *
- * @param optionID the unique id of this parameter
- * @param defaultValue the default value
- */
- /*public IntListParameter(OptionID optionID, List<Integer> defaultValue) {
- super(optionID, defaultValue);
- }*/
-
- /**
- * Constructs an integer list parameter
- *
- * @param optionID the unique id of this parameter
* @param optional specifies if this parameter is an optional parameter
*/
public IntListParameter(OptionID optionID, boolean optional) {
@@ -132,14 +57,13 @@ public class IntListParameter extends ListParameter<Integer> {
super(optionID);
}
- /** {@inheritDoc} */
@Override
public String getValueAsString() {
- StringBuffer buf = new StringBuffer();
+ StringBuilder buf = new StringBuilder();
List<Integer> val = getValue();
Iterator<Integer> veciter = val.iterator();
while(veciter.hasNext()) {
- buf.append(Integer.toString(veciter.next()));
+ buf.append(veciter.next().toString());
if (veciter.hasNext()) {
buf.append(LIST_SEP);
}
@@ -147,7 +71,6 @@ public class IntListParameter extends ListParameter<Integer> {
return buf.toString();
}
- /** {@inheritDoc} */
@SuppressWarnings("unchecked")
@Override
protected List<Integer> parseValue(Object obj) throws ParameterException {
@@ -169,7 +92,7 @@ public class IntListParameter extends ListParameter<Integer> {
String[] values = SPLIT.split((String) obj);
ArrayList<Integer> intValue = new ArrayList<Integer>(values.length);
for(String val : values) {
- intValue.add(Integer.parseInt(val));
+ intValue.add(Integer.valueOf(val));
}
return intValue;
}
@@ -177,19 +100,6 @@ public class IntListParameter extends ListParameter<Integer> {
}
/**
- * Sets the default value of this parameter.
- *
- * @param allListDefaultValue default value for all list elements of this
- * parameter
- */
- // unused?
- /*public void setDefaultValue(int allListDefaultValue) {
- for(int i = 0; i < defaultValue.size(); i++) {
- defaultValue.set(i, allListDefaultValue);
- }
- }*/
-
- /**
* Returns a string representation of the parameter's type.
*
* @return &quot;&lt;int_1,...,int_n&gt;&quot;
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/IntParameter.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/IntParameter.java
index 4e494fda..22823e16 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/IntParameter.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/IntParameter.java
@@ -23,8 +23,6 @@ package de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.util.List;
-
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.ParameterException;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.WrongParameterValueException;
@@ -42,46 +40,14 @@ public class IntParameter extends NumberParameter<Integer> {
* constraint, and default value.
*
* @param optionID optionID the unique id of the option
- * @param constraints the constraint for this integer parameter
* @param defaultValue the default value
- */
- public IntParameter(OptionID optionID, List<ParameterConstraint<Number>> constraints, Integer defaultValue) {
- super(optionID, constraints, defaultValue);
- }
-
- /**
- * Constructs an integer parameter with the given optionID, parameter
- * constraint, and optional flag.
- *
- * @param optionID optionID the unique id of the option
- * @param constraints the constraint for this integer parameter
- * @param optional specifies if this parameter is an optional parameter
- */
- public IntParameter(OptionID optionID, List<ParameterConstraint<Number>> constraints, boolean optional) {
- super(optionID, constraints, optional);
- }
-
- /**
- * Constructs an integer parameter with the given optionID, and parameter
- * constraint.
- *
- * @param optionID optionID the unique id of the option
- * @param constraints the constraint for this integer parameter
- */
- public IntParameter(OptionID optionID, List<ParameterConstraint<Number>> constraints) {
- super(optionID, constraints);
- }
-
- /**
- * Constructs an integer parameter with the given optionID, parameter
- * constraint, and default value.
- *
- * @param optionID optionID the unique id of the option
* @param constraint the constraint for this integer parameter
- * @param defaultValue the default value
+ * @deprecated Use {@link #addConstraint} instead.
*/
- public IntParameter(OptionID optionID, ParameterConstraint<Number> constraint, Integer defaultValue) {
- super(optionID, constraint, defaultValue);
+ @Deprecated
+ public IntParameter(OptionID optionID, int defaultValue, ParameterConstraint<Number> constraint) {
+ super(optionID, Integer.valueOf(defaultValue));
+ addConstraint(constraint);
}
/**
@@ -91,9 +57,12 @@ public class IntParameter extends NumberParameter<Integer> {
* @param optionID optionID the unique id of the option
* @param constraint the constraint for this integer parameter
* @param optional specifies if this parameter is an optional parameter
+ * @deprecated Use {@link #addConstraint} instead.
*/
+ @Deprecated
public IntParameter(OptionID optionID, ParameterConstraint<Number> constraint, boolean optional) {
- super(optionID, constraint, optional);
+ super(optionID, optional);
+ addConstraint(constraint);
}
/**
@@ -102,9 +71,12 @@ public class IntParameter extends NumberParameter<Integer> {
*
* @param optionID optionID the unique id of the option
* @param constraint the constraint for this integer parameter
+ * @deprecated Use {@link #addConstraint} instead.
*/
+ @Deprecated
public IntParameter(OptionID optionID, ParameterConstraint<Number> constraint) {
- super(optionID, constraint);
+ super(optionID);
+ addConstraint(constraint);
}
/**
@@ -113,8 +85,8 @@ public class IntParameter extends NumberParameter<Integer> {
* @param optionID optionID the unique id of the option
* @param defaultValue the default value
*/
- public IntParameter(OptionID optionID, Integer defaultValue) {
- super(optionID, defaultValue);
+ public IntParameter(OptionID optionID, int defaultValue) {
+ super(optionID, Integer.valueOf(defaultValue));
}
/**
@@ -122,7 +94,9 @@ public class IntParameter extends NumberParameter<Integer> {
*
* @param optionID optionID the unique id of the option
* @param optional specifies if this parameter is an optional parameter
+ * @deprecated Use {@link #setOptional} instead.
*/
+ @Deprecated
public IntParameter(OptionID optionID, boolean optional) {
super(optionID, optional);
}
@@ -136,25 +110,21 @@ public class IntParameter extends NumberParameter<Integer> {
super(optionID);
}
- /** {@inheritDoc} */
@Override
public String getValueAsString() {
- return Integer.toString(getValue());
+ return getValue().toString();
}
- /** {@inheritDoc} */
@Override
protected Integer parseValue(Object obj) throws ParameterException {
- if(obj instanceof Integer) {
+ if (obj instanceof Integer) {
return (Integer) obj;
}
try {
- return Integer.parseInt(obj.toString());
- }
- catch(NullPointerException e) {
+ return Integer.valueOf(obj.toString());
+ } catch (NullPointerException e) {
throw new WrongParameterValueException("Wrong parameter format! Parameter \"" + getName() + "\" requires an integer value, read: " + obj + "!\n");
- }
- catch(NumberFormatException e) {
+ } catch (NumberFormatException e) {
throw new WrongParameterValueException("Wrong parameter format! Parameter \"" + getName() + "\" requires an integer value, read: " + obj + "!\n");
}
}
@@ -168,4 +138,13 @@ public class IntParameter extends NumberParameter<Integer> {
public String getSyntax() {
return "<int>";
}
+
+ /**
+ * Get the parameter value as integer
+ *
+ * @return Parameter value
+ */
+ public int intValue() {
+ return getValue().intValue();
+ }
}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/ListParameter.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/ListParameter.java
index ad0ded5c..e71a744e 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/ListParameter.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/ListParameter.java
@@ -23,20 +23,20 @@ package de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.ParameterConstraint;
-
import java.util.List;
import java.util.regex.Pattern;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
+
/**
* Abstract parameter class defining a parameter for a list of objects.
*
* @author Steffi Wanka
* @author Erich Schubert
+ *
* @param <T> List type
*/
-public abstract class ListParameter<T> extends Parameter<List<T>, List<T>> {
+public abstract class ListParameter<T> extends AbstractParameter<List<T>> {
/**
* A pattern defining a &quot,&quot.
*/
@@ -51,7 +51,7 @@ public abstract class ListParameter<T> extends Parameter<List<T>, List<T>> {
* A pattern defining a &quot:&quot.
*/
public static final Pattern VECTOR_SPLIT = Pattern.compile(":");
-
+
/**
* Vector separator character - &quot;:&quot;
*/
@@ -61,79 +61,11 @@ public abstract class ListParameter<T> extends Parameter<List<T>, List<T>> {
* Constructs a list parameter with the given optionID.
*
* @param optionID the unique id of this parameter
- * @param constraints the constraints of this parameter, may be null
* @param defaultValue the default value of this parameter (may be null)
*/
- public ListParameter(OptionID optionID, List<ParameterConstraint<List<T>>> constraints, List<T> defaultValue) {
- super(optionID, constraints, defaultValue);
- }
-
- /**
- * Constructs a list parameter with the given optionID.
- *
- * @param optionID the unique id of this parameter
- * @param constraints the constraints of this parameter, may be null
- * @param optional specifies if this parameter is an optional parameter
- */
- public ListParameter(OptionID optionID, List<ParameterConstraint<List<T>>> constraints, boolean optional) {
- super(optionID, constraints, optional);
- }
-
- /**
- * Constructs a list parameter with the given optionID.
- *
- * @param optionID the unique id of this parameter
- * @param constraints the constraint of this parameter
- */
- // NOTE: we cannot have this, because it has the same erasure as optionID, defaults!
- // Use optional=false!
- /*public ListParameter(OptionID optionID, List<ParameterConstraint<List<T>>> constraints) {
- super(optionID, constraints);
- }*/
-
- /**
- * Constructs a list parameter with the given optionID.
- *
- * @param optionID the unique id of this parameter
- * @param constraint the constraint of this parameter, may be null
- * @param defaultValue the default value of this parameter (may be null)
- */
- public ListParameter(OptionID optionID, ParameterConstraint<List<T>> constraint, List<T> defaultValue) {
- super(optionID, constraint, defaultValue);
- }
-
- /**
- * Constructs a list parameter with the given optionID.
- *
- * @param optionID the unique id of this parameter
- * @param constraint the constraint of this parameter, may be null
- * @param optional specifies if this parameter is an optional parameter
- */
- public ListParameter(OptionID optionID, ParameterConstraint<List<T>> constraint, boolean optional) {
- super(optionID, constraint, optional);
- }
-
- /**
- * Constructs a list parameter with the given optionID.
- *
- * @param optionID the unique id of this parameter
- * @param constraint the constraint of this parameter
- */
- public ListParameter(OptionID optionID, ParameterConstraint<List<T>> constraint) {
- super(optionID, constraint);
- }
-
- /**
- * Constructs a list parameter with the given optionID.
- *
- * @param optionID the unique id of this parameter
- * @param defaultValue the default value of this parameter (may be null)
- */
- // NOTE: we cannot have this, because it has the same erasure as optionID, defaults!
- // Use full constructor, constraints = null!
- /*public ListParameter(OptionID optionID, List<T> defaultValue) {
+ public ListParameter(OptionID optionID, List<T> defaultValue) {
super(optionID, defaultValue);
- }*/
+ }
/**
* Constructs a list parameter with the given optionID and optional flag.
@@ -160,7 +92,7 @@ public abstract class ListParameter<T> extends Parameter<List<T>, List<T>> {
* @return the size of this list parameter.
*/
public int getListSize() {
- if(getValue() == null && isOptional()) {
+ if (getValue() == null && isOptional()) {
return 0;
}
@@ -173,19 +105,19 @@ public abstract class ListParameter<T> extends Parameter<List<T>, List<T>> {
*/
// TODO: keep? remove?
protected String asString() {
- if(getValue() == null) {
+ if (getValue() == null) {
return "";
}
StringBuilder buffer = new StringBuilder();
- buffer.append("[");
+ buffer.append('[');
- for(int i = 0; i < getValue().size(); i++) {
+ for (int i = 0; i < getValue().size(); i++) {
buffer.append(getValue().get(i).toString());
- if(i != getValue().size() - 1) {
- buffer.append(",");
+ if (i != getValue().size() - 1) {
+ buffer.append(',');
}
}
- buffer.append("]");
+ buffer.append(']');
return buffer.toString();
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/LongParameter.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/LongParameter.java
index 6f1a7beb..ac8bd62c 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/LongParameter.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/LongParameter.java
@@ -23,8 +23,6 @@ package de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.util.List;
-
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.ParameterException;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.WrongParameterValueException;
@@ -42,69 +40,14 @@ public class LongParameter extends NumberParameter<Long> {
* and default value.
*
* @param optionID the unique OptionID for this parameter
- * @param constraints the parameter constraints for this long parameter
- * @param defaultValue the default value
- */
- public LongParameter(OptionID optionID, List<ParameterConstraint<Number>> constraints, long defaultValue) {
- super(optionID, constraints, defaultValue);
- }
-
- /**
- * Constructs a long parameter with the given optionID, and parameter
- * constraint.
- *
- * @param optionID the unique OptionID for this parameter
- * @param constraints the parameter constraints for this long parameter
- * @param optional optional flag
- */
- public LongParameter(OptionID optionID, List<ParameterConstraint<Number>> constraints, boolean optional) {
- super(optionID, constraints, optional);
- }
-
- /**
- * Constructs a long parameter with the given optionID, and parameter
- * constraint.
- *
- * @param optionID the unique OptionID for this parameter
- * @param constraints the parameter constraints for this long parameter
- */
- public LongParameter(OptionID optionID, List<ParameterConstraint<Number>> constraints) {
- super(optionID, constraints);
- }
-
- /**
- * Constructs a long parameter with the given optionID, parameter constraint
- * and default value.
- *
- * @param optionID the unique OptionID for this parameter
* @param constraint the parameter constraint for this long parameter
* @param defaultValue the default value
+ * @deprecated Use {@link #addConstraint} instead!
*/
+ @Deprecated
public LongParameter(OptionID optionID, ParameterConstraint<Number> constraint, long defaultValue) {
- super(optionID, constraint, defaultValue);
- }
-
- /**
- * Constructs a long parameter with the given optionID, and parameter
- * constraint.
- *
- * @param optionID the unique OptionID for this parameter
- * @param constraint the parameter constraint for this long parameter
- * @param optional optional flag
- */
- public LongParameter(OptionID optionID, ParameterConstraint<Number> constraint, boolean optional) {
- super(optionID, constraint, optional);
- }
-
- /**
- * Constructs a long parameter with the given optionID, and parameter
- * constraint.
- *
- * @param optionID the unique OptionID for this parameter
- * @param constraint the parameter constraint for this long parameter
- */
- public LongParameter(OptionID optionID, ParameterConstraint<Number> constraint) {
- super(optionID, constraint);
+ super(optionID, Long.valueOf(defaultValue));
+ addConstraint(constraint);
}
/**
@@ -114,17 +57,7 @@ public class LongParameter extends NumberParameter<Long> {
* @param defaultValue the default value
*/
public LongParameter(OptionID optionID, long defaultValue) {
- super(optionID, defaultValue);
- }
-
- /**
- * Constructs a long parameter with the given optionID.
- *
- * @param optionID the unique OptionID for this parameter
- * @param optional optional flag
- */
- public LongParameter(OptionID optionID, boolean optional) {
- super(optionID, optional);
+ super(optionID, Long.valueOf(defaultValue));
}
/**
@@ -136,29 +69,27 @@ public class LongParameter extends NumberParameter<Long> {
super(optionID);
}
- /** {@inheritDoc} */
@Override
public String getValueAsString() {
- return Long.toString(getValue());
+ return getValue().toString();
}
- /** {@inheritDoc} */
@Override
protected Long parseValue(Object obj) throws ParameterException {
if(obj instanceof Long) {
return (Long) obj;
}
- if(obj instanceof Integer) {
- return new Long((Integer) obj);
+ if(obj instanceof Number) {
+ return Long.valueOf(((Number) obj).longValue());
}
try {
- return Long.parseLong(obj.toString());
+ return Long.valueOf(obj.toString());
}
catch(NullPointerException e) {
- throw new WrongParameterValueException("Wrong parameter format! Parameter \"" + getName() + "\" requires a double value, read: " + obj + "!\n");
+ throw new WrongParameterValueException("Wrong parameter format! Parameter \"" + getName() + "\" requires a long value, read: " + obj + "!\n");
}
catch(NumberFormatException e) {
- throw new WrongParameterValueException("Wrong parameter format! Parameter \"" + getName() + "\" requires a double value, read: " + obj + "!\n");
+ throw new WrongParameterValueException("Wrong parameter format! Parameter \"" + getName() + "\" requires a long value, read: " + obj + "!\n");
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/NumberParameter.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/NumberParameter.java
index 3a1ab946..8928c292 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/NumberParameter.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/NumberParameter.java
@@ -24,87 +24,16 @@ package de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters;
*/
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.ParameterConstraint;
-
-import java.util.List;
/**
* Abstract class for defining a number parameter.
*
* @author Steffi Wanka
* @author Erich Schubert
+ *
* @param <T> the type of a possible value (i.e., the type of the option)
*/
-public abstract class NumberParameter<T extends Number> extends Parameter<Number, T> {
- /**
- * Constructs a number parameter with the given optionID, constraint, and
- * optional flag.
- *
- * @param optionID the unique id of this parameter
- * @param constraints the constraints of this parameter
- * @param defaultValue the default value for this parameter
- */
- public NumberParameter(OptionID optionID, List<ParameterConstraint<Number>> constraints, T defaultValue) {
- super(optionID, constraints, defaultValue);
- }
-
- /**
- * Constructs a number parameter with the given optionID, constraint, and
- * optional flag.
- *
- * @param optionID the unique id of this parameter
- * @param constraints the constraint of this parameter
- * @param optional specifies if this parameter is an optional parameter
- */
- public NumberParameter(OptionID optionID, List<ParameterConstraint<Number>> constraints, boolean optional) {
- super(optionID, constraints, optional);
- }
-
- /**
- * Constructs a number parameter with the given optionID, and constraint.
- *
- * @param optionID the unique id of this parameter
- * @param constraints the constraints of this parameter, may be empty if there
- * are no constraints
- */
- public NumberParameter(OptionID optionID, List<ParameterConstraint<Number>> constraints) {
- super(optionID, constraints);
- }
-
- /**
- * Constructs a number parameter with the given optionID, constraint, and
- * optional flag.
- *
- * @param optionID the unique id of this parameter
- * @param constraint the constraint of this parameter
- * @param defaultValue the default value for this parameter
- */
- public NumberParameter(OptionID optionID, ParameterConstraint<Number> constraint, T defaultValue) {
- super(optionID, constraint, defaultValue);
- }
-
- /**
- * Constructs a number parameter with the given optionID, constraint, and
- * optional flag.
- *
- * @param optionID the unique id of this parameter
- * @param constraint the constraint of this parameter
- * @param optional specifies if this parameter is an optional parameter
- */
- public NumberParameter(OptionID optionID, ParameterConstraint<Number> constraint, boolean optional) {
- super(optionID, constraint, optional);
- }
-
- /**
- * Constructs a number parameter with the given optionID, and constraint.
- *
- * @param optionID the unique id of this parameter
- * @param constraint the constraint of this parameter
- */
- public NumberParameter(OptionID optionID, ParameterConstraint<Number> constraint) {
- super(optionID, constraint);
- }
-
+public abstract class NumberParameter<T extends Number> extends AbstractParameter<T> {
/**
* Constructs a number parameter with the given optionID and default Value.
*
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/ObjectListParameter.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/ObjectListParameter.java
index cfd74d9a..85a32085 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/ObjectListParameter.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/ObjectListParameter.java
@@ -68,18 +68,16 @@ public class ObjectListParameter<C> extends ClassListParameter<C> {
super(optionID, restrictionClass);
}
- /** {@inheritDoc} */
@Override
public String getSyntax() {
return "<object_1|class_1,...,object_n|class_n>";
}
- /** {@inheritDoc} */
@SuppressWarnings("unchecked")
@Override
protected List<Class<? extends C>> parseValue(Object obj) throws ParameterException {
if(obj == null) {
- throw new UnspecifiedParameterException("Parameter Error.\n" + "No value for parameter \"" + getName() + "\" " + "given.");
+ throw new UnspecifiedParameterException(this);
}
if (List.class.isInstance(obj)) {
List<?> l = (List<?>) obj;
@@ -116,7 +114,6 @@ public class ObjectListParameter<C> extends ClassListParameter<C> {
return super.parseValue(obj);
}
- /** {@inheritDoc} */
@Override
public List<C> instantiateClasses(Parameterization config) {
if (instances == null) {
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/ObjectParameter.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/ObjectParameter.java
index 0a1a6b27..eff80954 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/ObjectParameter.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/ObjectParameter.java
@@ -99,12 +99,11 @@ public class ObjectParameter<C> extends ClassParameter<C> {
super(optionID, restrictionClass);
}
- /** {@inheritDoc} */
@SuppressWarnings("unchecked")
@Override
protected Class<? extends C> parseValue(Object obj) throws ParameterException {
if(obj == null) {
- throw new UnspecifiedParameterException("Parameter Error.\n" + "No value for parameter \"" + getName() + "\" " + "given.");
+ throw new UnspecifiedParameterException(this);
}
// does the given objects class fit?
if(restrictionClass.isInstance(obj)) {
@@ -113,7 +112,6 @@ public class ObjectParameter<C> extends ClassParameter<C> {
return super.parseValue(obj);
}
- /** {@inheritDoc} */
@SuppressWarnings("unchecked")
@Override
public void setValue(Object obj) throws ParameterException {
@@ -154,7 +152,6 @@ public class ObjectParameter<C> extends ClassParameter<C> {
return instance = super.instantiateClass(config);
}
- /** {@inheritDoc} */
@Override
public Object getGivenValue() {
if(instance != null) {
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/Parameter.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/Parameter.java
index 8502bbec..6dadbf8f 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/Parameter.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/Parameter.java
@@ -1,5 +1,10 @@
package de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.ParameterException;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.UnspecifiedParameterException;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.ParameterConstraint;
+
/*
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
@@ -23,20 +28,8 @@ package de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.security.InvalidParameterException;
-import java.util.List;
-import java.util.Vector;
-
-import de.lmu.ifi.dbs.elki.logging.LoggingUtil;
-import de.lmu.ifi.dbs.elki.utilities.FormatUtil;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.ParameterException;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.UnspecifiedParameterException;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.WrongParameterValueException;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.ParameterConstraint;
-
/**
- * Abstract class for specifying a parameter.
+ * Interface for the parameter of a class.
*
* A parameter is defined as an option having a specific value.
*
@@ -50,202 +43,28 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.ParameterConstra
* @apiviz.uses ParameterConstraint
*
* @param <T> the type of a possible value (i.e., the type of the option)
- * @param <S> the supertype for constraints
*/
-public abstract class Parameter<S, T extends S> {
- /**
- * The default value of the parameter (may be null).
- */
- protected T defaultValue = null;
-
- /**
- * Specifies if the default value of this parameter was taken as parameter
- * value.
- */
- private boolean defaultValueTaken = false;
-
- /**
- * Specifies if this parameter is an optional parameter.
- */
- protected boolean optionalParameter = false;
-
- /**
- * Holds parameter constraints for this parameter.
- */
- protected final List<ParameterConstraint<S>> constraints;
-
- /**
- * The option name.
- */
- protected final OptionID optionid;
-
- /**
- * The short description of the option. An extended description is provided by
- * the method {@link #getFullDescription()}
- */
- protected String shortDescription;
-
- /**
- * The value last passed to this option.
- */
- protected T givenValue = null;
-
- /**
- * The value of this option.
- */
- private T value;
-
- /**
- * Constructs a parameter with the given optionID, constraints, and default
- * value.
- *
- * @param optionID the unique id of this parameter
- * @param constraints the constraints of this parameter, may be empty if there
- * are no constraints
- * @param defaultValue the default value of this parameter (may be null)
- */
- public Parameter(OptionID optionID, List<ParameterConstraint<S>> constraints, T defaultValue) {
- this.optionid = optionID;
- this.shortDescription = optionID.getDescription();
- this.optionalParameter = true;
- this.defaultValue = defaultValue;
- this.constraints = (constraints != null) ? constraints : new Vector<ParameterConstraint<S>>();
- }
-
- /**
- * Constructs a parameter with the given optionID, constraints, and optional
- * flag.
- *
- * @param optionID the unique id of this parameter
- * @param constraints the constraints of this parameter, may be empty if there
- * are no constraints
- * @param optional specifies if this parameter is an optional parameter
- */
- public Parameter(OptionID optionID, List<ParameterConstraint<S>> constraints, boolean optional) {
- this.optionid = optionID;
- this.shortDescription = optionID.getDescription();
- this.optionalParameter = optional;
- this.defaultValue = null;
- this.constraints = (constraints != null) ? constraints : new Vector<ParameterConstraint<S>>();
- }
-
- /**
- * Constructs a parameter with the given optionID, and constraints.
- *
- * @param optionID the unique id of this parameter
- * @param constraints the constraints of this parameter, may be empty if there
- * are no constraints
- */
- public Parameter(OptionID optionID, List<ParameterConstraint<S>> constraints) {
- this(optionID, constraints, false);
- }
-
- /**
- * Constructs a parameter with the given optionID, constraint, and default
- * value.
- *
- * @param optionID the unique id of this parameter
- * @param constraint the constraint of this parameter
- * @param defaultValue the default value of this parameter (may be null)
- */
- public Parameter(OptionID optionID, ParameterConstraint<S> constraint, T defaultValue) {
- this(optionID, makeConstraintsVector(constraint), defaultValue);
- }
-
- /**
- * Constructs a parameter with the given optionID, constraint, and optional
- * flag.
- *
- * @param optionID the unique id of this parameter
- * @param constraint the constraint of this parameter
- * @param optional specifies if this parameter is an optional parameter
- */
- public Parameter(OptionID optionID, ParameterConstraint<S> constraint, boolean optional) {
- this(optionID, makeConstraintsVector(constraint), optional);
- }
-
- /**
- * Constructs a parameter with the given optionID, and constraint.
- *
- * @param optionID the unique id of this parameter
- * @param constraint the constraint of this parameter
- */
- public Parameter(OptionID optionID, ParameterConstraint<S> constraint) {
- this(optionID, constraint, false);
- }
-
- /**
- * Constructs a parameter with the given optionID and default value.
- *
- * @param optionID the unique id of the option
- * @param defaultValue default value.
- */
- public Parameter(OptionID optionID, T defaultValue) {
- this(optionID, (Vector<ParameterConstraint<S>>) null, defaultValue);
- }
-
- /**
- * Constructs a parameter with the given optionID and optional flag.
- *
- * @param optionID the unique id of the option
- * @param optional optional flag
- */
- public Parameter(OptionID optionID, boolean optional) {
- this(optionID, (Vector<ParameterConstraint<S>>) null, optional);
- }
-
- /**
- * Constructs a parameter with the given optionID.
- *
- * @param optionID the unique id of the option
- */
- public Parameter(OptionID optionID) {
- this(optionID, (Vector<ParameterConstraint<S>>) null, false);
- }
-
- /**
- * Wrap a single constraint into a vector of constraints.
- *
- * @param <S> Type
- * @param constraint Constraint, may be {@code null}
- * @return Vector containing the constraint (if not null)
- */
- private static <S> List<ParameterConstraint<S>> makeConstraintsVector(ParameterConstraint<S> constraint) {
- Vector<ParameterConstraint<S>> constraints = new Vector<ParameterConstraint<S>>((constraint == null) ? 0 : 1);
- if(constraint != null) {
- constraints.add(constraint);
- }
- return constraints;
- }
-
+public interface Parameter<T> {
/**
* Sets the default value of this parameter.
*
* @param defaultValue default value of this parameter
*/
- public void setDefaultValue(T defaultValue) {
- this.defaultValue = defaultValue;
- this.optionalParameter = true;
- }
+ public abstract void setDefaultValue(T defaultValue);
/**
* Checks if this parameter has a default value.
*
* @return true, if this parameter has a default value, false otherwise
*/
- public boolean hasDefaultValue() {
- return !(defaultValue == null);
- }
+ public abstract boolean hasDefaultValue();
/**
* Sets the default value of this parameter as the actual value of this
* parameter.
*/
// TODO: can we do this more elegantly?
- public void useDefaultValue() {
- setValueInternal(defaultValue);
- defaultValueTaken = true;
- }
+ public abstract void useDefaultValue();
/**
* Handle default values for a parameter.
@@ -255,38 +74,21 @@ public abstract class Parameter<S, T extends S> {
* required parameter!
* @throws UnspecifiedParameterException If the parameter requires a value
*/
- public boolean tryDefaultValue() throws UnspecifiedParameterException {
- // Assume default value instead.
- if(hasDefaultValue()) {
- useDefaultValue();
- return true;
- }
- else if(isOptional()) {
- // Optional is fine, but not successful
- return false;
- }
- else {
- throw new UnspecifiedParameterException(this);
- }
- }
+ public abstract boolean tryDefaultValue() throws UnspecifiedParameterException;
/**
* Specifies if this parameter is an optional parameter.
*
* @param opt true if this parameter is optional, false otherwise
*/
- public void setOptional(boolean opt) {
- this.optionalParameter = opt;
- }
+ public abstract void setOptional(boolean opt);
/**
* Checks if this parameter is an optional parameter.
*
* @return true if this parameter is optional, false otherwise
*/
- public boolean isOptional() {
- return this.optionalParameter;
- }
+ public abstract boolean isOptional();
/**
* Checks if the default value of this parameter was taken as the actual
@@ -295,18 +97,14 @@ public abstract class Parameter<S, T extends S> {
* @return true, if the default value was taken as actual parameter value,
* false otherwise
*/
- public boolean tookDefaultValue() {
- return defaultValueTaken;
- }
+ public abstract boolean tookDefaultValue();
/**
* Returns true if the value of the option is defined, false otherwise.
*
* @return true if the value of the option is defined, false otherwise.
*/
- public boolean isDefined() {
- return (this.value != null);
- }
+ public abstract boolean isDefined();
/**
* Returns the default value of the parameter.
@@ -317,27 +115,21 @@ public abstract class Parameter<S, T extends S> {
* has no default value.
*/
// TODO: change this to return a string value?
- public T getDefaultValue() {
- return defaultValue;
- }
+ public abstract T getDefaultValue();
/**
* Whether this class has a list of default values.
*
* @return whether the class has a description of valid values.
*/
- public boolean hasValuesDescription() {
- return false;
- }
+ public abstract boolean hasValuesDescription();
/**
* Return a string explaining valid values.
*
* @return String explaining valid values (e.g. a class list)
*/
- public String getValuesDescription() {
- return "";
- }
+ public abstract String getValuesDescription();
/**
* Returns the extended description of the option which includes the option's
@@ -345,99 +137,35 @@ public abstract class Parameter<S, T extends S> {
*
* @return the option's description.
*/
- public String getFullDescription() {
- StringBuffer description = new StringBuffer();
- // description.append(getParameterType()).append(" ");
- description.append(shortDescription);
- description.append(FormatUtil.NEWLINE);
- if(hasValuesDescription()) {
- final String valuesDescription = getValuesDescription();
- description.append(valuesDescription);
- if(!valuesDescription.endsWith(FormatUtil.NEWLINE)) {
- description.append(FormatUtil.NEWLINE);
- }
- }
- if(hasDefaultValue()) {
- description.append("Default: ");
- description.append(getDefaultValueAsString());
- description.append(FormatUtil.NEWLINE);
- }
- if(!constraints.isEmpty()) {
- if(constraints.size() == 1) {
- description.append("Constraint: ");
- }
- else if(constraints.size() > 1) {
- description.append("Constraints: ");
- }
- for(int i = 0; i < constraints.size(); i++) {
- ParameterConstraint<S> constraint = constraints.get(i);
- if(i > 0) {
- description.append(", ");
- }
- description.append(constraint.getDescription(getName()));
- if(i == constraints.size() - 1) {
- description.append(".");
- }
- }
- description.append(FormatUtil.NEWLINE);
- }
- return description.toString();
- }
-
- /**
- * Validate a value after parsing (e.g. do constrain checks!)
- *
- * @param obj Object to validate
- * @return true iff the object is valid for this parameter.
- * @throws ParameterException when the object is not valid.
- */
- protected boolean validate(T obj) throws ParameterException {
- try {
- for(ParameterConstraint<S> cons : this.constraints) {
- cons.test(obj);
- }
- }
- catch(ParameterException e) {
- throw new WrongParameterValueException("Specified parameter value for parameter \"" + getName() + "\" breaches parameter constraint.\n" + e.getMessage());
- }
- return true;
- }
+ public abstract String getFullDescription();
/**
* Return the OptionID of this option.
*
* @return Option ID
*/
- public OptionID getOptionID() {
- return optionid;
- }
+ public abstract OptionID getOptionID();
/**
* Returns the name of the option.
*
* @return the option's name.
*/
- public String getName() {
- return optionid.getName();
- }
+ public abstract String getName();
/**
* Returns the short description of the option.
*
* @return the option's short description.
*/
- public String getShortDescription() {
- return shortDescription;
- }
+ public abstract String getShortDescription();
/**
* Sets the short description of the option.
*
* @param description the short description to be set
*/
- public void setShortDescription(String description) {
- this.shortDescription = description;
- }
+ public abstract void setShortDescription(String description);
/**
* Sets the value of the option.
@@ -446,48 +174,26 @@ public abstract class Parameter<S, T extends S> {
* @throws ParameterException if the given value is not a valid value for this
* option.
*/
- public void setValue(Object obj) throws ParameterException {
- T val = parseValue(obj);
- if(validate(val)) {
- setValueInternal(val);
- }
- else {
- throw new InvalidParameterException("Value for option \"" + getName() + "\" did not validate: " + obj.toString());
- }
- }
-
- /**
- * Internal setter for the value.
- *
- * @param val Value
- */
- protected final void setValueInternal(T val) {
- this.value = this.givenValue = val;
- }
+ public abstract void setValue(Object obj) throws ParameterException;
/**
* Returns the value of the option.
*
- * You should use either {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization#grab}
- * or {@link #isDefined} to test if getValue() will return a well-defined value.
+ * You should use either
+ * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization#grab}
+ * or {@link #isDefined} to test if getValue() will return a well-defined
+ * value.
*
* @return the option's value.
*/
- public final T getValue() {
- if(this.value == null) {
- LoggingUtil.warning("Programming error: Parameter#getValue() called for unset parameter \"" + this.optionid.getName() + "\"", new Throwable());
- }
- return this.value;
- }
+ public abstract T getValue();
/**
* Get the last given value. May return {@code null}
*
* @return Given value
*/
- public Object getGivenValue() {
- return this.givenValue;
- }
+ public abstract Object getGivenValue();
/**
* Checks if the given argument is valid for this option.
@@ -497,10 +203,7 @@ public abstract class Parameter<S, T extends S> {
* @throws ParameterException if the given value is not a valid value for this
* option.
*/
- public final boolean isValid(Object obj) throws ParameterException {
- T val = parseValue(obj);
- return validate(val);
- }
+ public abstract boolean isValid(Object obj) throws ParameterException;
/**
* Returns a string representation of the parameter's type (e.g. an
@@ -512,15 +215,6 @@ public abstract class Parameter<S, T extends S> {
public abstract String getSyntax();
/**
- * Parse a given value into the destination type.
- *
- * @param obj Object to parse (may be a string representation!)
- * @return Parsed object
- * @throws ParameterException when the object cannot be parsed.
- */
- protected abstract T parseValue(Object obj) throws ParameterException;
-
- /**
* Get the value as string. May return {@code null}
*
* @return Value as string
@@ -532,16 +226,12 @@ public abstract class Parameter<S, T extends S> {
*
* @return default value
*/
- public String getDefaultValueAsString() {
- return getDefaultValue().toString();
- }
+ public abstract String getDefaultValueAsString();
/**
* Add an additional constraint.
*
* @param constraint Constraint to add.
*/
- public void addConstraint(ParameterConstraint<S> constraint) {
- constraints.add(constraint);
- }
-} \ No newline at end of file
+ public abstract void addConstraint(ParameterConstraint<? super T> constraint);
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/PatternParameter.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/PatternParameter.java
index 1f54c5d2..05c99d2b 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/PatternParameter.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/PatternParameter.java
@@ -23,7 +23,6 @@ package de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.util.List;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
@@ -31,7 +30,6 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.ParameterException;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.UnspecifiedParameterException;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.WrongParameterValueException;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.ParameterConstraint;
/**
* Parameter class for a parameter specifying a pattern.
@@ -39,101 +37,7 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.ParameterConstra
* @author Steffi Wanka
* @author Erich Schubert
*/
-public class PatternParameter extends Parameter<Pattern, Pattern> {
- /**
- * Constructs a pattern parameter with the given optionID, constraints and
- * default value.
- *
- * @param optionID the unique id of the parameter
- * @param constraint parameter constraint
- * @param defaultValue the default value of the parameter
- */
- public PatternParameter(OptionID optionID, List<ParameterConstraint<Pattern>> constraint, Pattern defaultValue) {
- super(optionID, constraint, defaultValue);
- }
-
- /**
- * Constructs a pattern parameter with the given optionID, constraints and
- * default value.
- *
- * @param optionID the unique id of the parameter
- * @param constraint parameter constraint
- * @param defaultValue the default value of the parameter
- */
- public PatternParameter(OptionID optionID, List<ParameterConstraint<Pattern>> constraint, String defaultValue) {
- super(optionID, constraint, Pattern.compile(defaultValue, Pattern.CASE_INSENSITIVE));
- }
-
- /**
- * Constructs a pattern parameter with the given optionID, constraints and
- * default value.
- *
- * @param optionID the unique id of the parameter
- * @param constraints parameter constraint
- * @param optional Flag to signal an optional parameter.
- */
- public PatternParameter(OptionID optionID, List<ParameterConstraint<Pattern>> constraints, boolean optional) {
- super(optionID, constraints, optional);
- }
-
- /**
- * Constructs a pattern parameter with the given optionID, constraints and
- * default value.
- *
- * @param optionID the unique id of the parameter
- * @param constraints parameter constraint
- */
- public PatternParameter(OptionID optionID, List<ParameterConstraint<Pattern>> constraints) {
- super(optionID, constraints);
- }
-
- /**
- * Constructs a pattern parameter with the given optionID, constraints and
- * default value.
- *
- * @param optionID the unique id of the parameter
- * @param constraint parameter constraint
- * @param defaultValue the default value of the parameter
- */
- public PatternParameter(OptionID optionID, ParameterConstraint<Pattern> constraint, Pattern defaultValue) {
- super(optionID, constraint, defaultValue);
- }
-
- /**
- * Constructs a pattern parameter with the given optionID, constraints and
- * default value.
- *
- * @param optionID the unique id of the parameter
- * @param constraint parameter constraint
- * @param defaultValue the default value of the parameter
- */
- public PatternParameter(OptionID optionID, ParameterConstraint<Pattern> constraint, String defaultValue) {
- super(optionID, constraint, Pattern.compile(defaultValue, Pattern.CASE_INSENSITIVE));
- }
-
- /**
- * Constructs a pattern parameter with the given optionID, constraints and
- * default value.
- *
- * @param optionID the unique id of the parameter
- * @param constraint parameter constraint
- * @param optional Flag to signal an optional parameter.
- */
- public PatternParameter(OptionID optionID, ParameterConstraint<Pattern> constraint, boolean optional) {
- super(optionID, constraint, optional);
- }
-
- /**
- * Constructs a pattern parameter with the given optionID, constraints and
- * default value.
- *
- * @param optionID the unique id of the parameter
- * @param constraint parameter constraint
- */
- public PatternParameter(OptionID optionID, ParameterConstraint<Pattern> constraint) {
- super(optionID, constraint);
- }
-
+public class PatternParameter extends AbstractParameter<Pattern> {
/**
* Constructs a pattern parameter with the given optionID, and default value.
*
@@ -158,16 +62,6 @@ public class PatternParameter extends Parameter<Pattern, Pattern> {
* Constructs a pattern parameter with the given optionID.
*
* @param optionID the unique id of the parameter
- * @param optional Flag to signal an optional parameter.
- */
- public PatternParameter(OptionID optionID, boolean optional) {
- super(optionID, optional);
- }
-
- /**
- * Constructs a pattern parameter with the given optionID.
- *
- * @param optionID the unique id of the parameter
*/
public PatternParameter(OptionID optionID) {
super(optionID);
@@ -181,7 +75,7 @@ public class PatternParameter extends Parameter<Pattern, Pattern> {
@Override
protected Pattern parseValue(Object obj) throws ParameterException {
if(obj == null) {
- throw new UnspecifiedParameterException("Parameter \"" + getName() + "\": Null value given!");
+ throw new UnspecifiedParameterException(this);
}
if(obj instanceof Pattern) {
return (Pattern) obj;
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/RandomParameter.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/RandomParameter.java
new file mode 100644
index 00000000..34d01de5
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/RandomParameter.java
@@ -0,0 +1,147 @@
+package de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import de.lmu.ifi.dbs.elki.utilities.RandomFactory;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.ParameterException;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.WrongParameterValueException;
+
+/**
+ * Parameter for random generators and/or random seeds.
+ *
+ * @author Erich Schubert
+ */
+public class RandomParameter extends AbstractParameter<RandomFactory> {
+ /**
+ * Seed value, if used
+ */
+ Long seed = null;
+
+ /**
+ * Constructor without default.
+ *
+ * @param optionID Option ID
+ */
+ public RandomParameter(OptionID optionID) {
+ super(optionID, RandomFactory.DEFAULT);
+ }
+
+ /**
+ * Constructor with default value. The default value may be {@code null},
+ * which means a new random will be generated.
+ *
+ * @param optionID Option ID
+ * @param defaultValue Default value. If {@code null}, a new random object
+ * will be created.
+ */
+ public RandomParameter(OptionID optionID, RandomFactory defaultValue) {
+ super(optionID, defaultValue);
+ }
+
+ /**
+ * Constructor with default seed value.
+ *
+ * @param optionID Option ID
+ * @param seed Default seed.
+ */
+ public RandomParameter(OptionID optionID, long seed) {
+ super(optionID, true);
+ this.seed = Long.valueOf(seed);
+ }
+
+ @Override
+ public String getSyntax() {
+ return "<long|Random>";
+ }
+
+ @Override
+ public void setValue(Object obj) throws ParameterException {
+ // This is a bit hackish. Set both seed and random (via super.setValue())
+ if (obj instanceof RandomFactory) {
+ seed = null;
+ } else if (obj instanceof Long) {
+ seed = (Long) obj;
+ obj = RandomFactory.get(seed);
+ } else if (obj instanceof Number) {
+ seed = Long.valueOf(((Number) obj).longValue());
+ obj = RandomFactory.get(seed);
+ } else if ("global random".equals(obj)) {
+ obj = RandomFactory.DEFAULT;
+ } else {
+ try {
+ seed = Long.valueOf(obj.toString());
+ obj = RandomFactory.get(seed);
+ } catch (NullPointerException e) {
+ throw new WrongParameterValueException("Wrong parameter format! Parameter \"" + getName() + "\" requires a long seed value or a random generator factory, read: " + obj + "!\n");
+ } catch (NumberFormatException e) {
+ throw new WrongParameterValueException("Wrong parameter format! Parameter \"" + getName() + "\" requires a long seed value or a random generator factory, read: " + obj + "!\n");
+ }
+ }
+ super.setValue(obj);
+ }
+
+ @Override
+ protected RandomFactory parseValue(Object obj) throws ParameterException {
+ if (obj instanceof RandomFactory) {
+ return (RandomFactory) obj;
+ }
+ if (obj instanceof Long) {
+ return RandomFactory.get((Long) obj);
+ }
+ if (obj instanceof Number) {
+ return RandomFactory.get(Long.valueOf(((Number) obj).longValue()));
+ }
+ try {
+ return RandomFactory.get(Long.valueOf(obj.toString()));
+ } catch (NullPointerException e) {
+ throw new WrongParameterValueException("Wrong parameter format! Parameter \"" + getName() + "\" requires a long seed value or a random generator factory, read: " + obj + "!\n");
+ } catch (NumberFormatException e) {
+ throw new WrongParameterValueException("Wrong parameter format! Parameter \"" + getName() + "\" requires a long seed value or a random generator factory, read: " + obj + "!\n");
+ }
+ }
+
+ @Override
+ public Object getGivenValue() {
+ Object r = super.getGivenValue();
+ if (r != null && seed != null) {
+ super.givenValue = RandomFactory.get(seed);
+ r = super.givenValue;
+ }
+ return r;
+ }
+
+ @Override
+ public String getValueAsString() {
+ return (seed != null) ? seed.toString() : "null";
+ }
+
+ @Override
+ public String getDefaultValueAsString() {
+ if (defaultValue == RandomFactory.DEFAULT) {
+ return "global random";
+ }
+ return super.getDefaultValueAsString();
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/StringParameter.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/StringParameter.java
index ea1c5a2c..06789dba 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/StringParameter.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/StringParameter.java
@@ -23,8 +23,6 @@ package de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.util.List;
-
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.ParameterException;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.UnspecifiedParameterException;
@@ -37,7 +35,7 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.ParameterConstra
* @author Steffi Wanka
* @author Erich Schubert
*/
-public class StringParameter extends Parameter<String, String> {
+public class StringParameter extends AbstractParameter<String> {
/**
* Constructs a string parameter with the given optionID, constraints and
* default value.
@@ -45,44 +43,13 @@ public class StringParameter extends Parameter<String, String> {
* @param optionID the unique id of the parameter
* @param constraint parameter constraint
* @param defaultValue the default value of the parameter
- */
- public StringParameter(OptionID optionID, List<ParameterConstraint<String>> constraint, String defaultValue) {
- super(optionID, constraint, defaultValue);
- }
-
- /**
- * Constructs a string parameter with the given optionID, constraints and
- * default value.
- *
- * @param optionID the unique id of the parameter
- * @param constraints parameter constraint
- * @param optional Flag to signal an optional parameter.
- */
- public StringParameter(OptionID optionID, List<ParameterConstraint<String>> constraints, boolean optional) {
- super(optionID, constraints, optional);
- }
-
- /**
- * Constructs a string parameter with the given optionID, constraints and
- * default value.
- *
- * @param optionID the unique id of the parameter
- * @param constraints parameter constraint
- */
- public StringParameter(OptionID optionID, List<ParameterConstraint<String>> constraints) {
- super(optionID, constraints);
- }
-
- /**
- * Constructs a string parameter with the given optionID, constraints and
- * default value.
*
- * @param optionID the unique id of the parameter
- * @param constraint parameter constraint
- * @param defaultValue the default value of the parameter
+ * @deprecated Use {@link #addConstraint} instead!
*/
+ @Deprecated
public StringParameter(OptionID optionID, ParameterConstraint<String> constraint, String defaultValue) {
- super(optionID, constraint, defaultValue);
+ super(optionID, defaultValue);
+ addConstraint(constraint);
}
/**
@@ -91,21 +58,13 @@ public class StringParameter extends Parameter<String, String> {
*
* @param optionID the unique id of the parameter
* @param constraint parameter constraint
- * @param optional Flag to signal an optional parameter.
- */
- public StringParameter(OptionID optionID, ParameterConstraint<String> constraint, boolean optional) {
- super(optionID, constraint, optional);
- }
-
- /**
- * Constructs a string parameter with the given optionID, constraints and
- * default value.
*
- * @param optionID the unique id of the parameter
- * @param constraint parameter constraint
+ * @deprecated Use {@link #addConstraint} instead!
*/
+ @Deprecated
public StringParameter(OptionID optionID, ParameterConstraint<String> constraint) {
- super(optionID, constraint);
+ super(optionID);
+ addConstraint(constraint);
}
/**
@@ -122,38 +81,26 @@ public class StringParameter extends Parameter<String, String> {
* Constructs a string parameter with the given optionID.
*
* @param optionID the unique id of the parameter
- * @param optional Flag to signal an optional parameter.
- */
- public StringParameter(OptionID optionID, boolean optional) {
- super(optionID, optional);
- }
-
- /**
- * Constructs a string parameter with the given optionID.
- *
- * @param optionID the unique id of the parameter
*/
public StringParameter(OptionID optionID) {
super(optionID);
}
-
- /** {@inheritDoc} */
+
@Override
public String getValueAsString() {
return getValue();
}
- /** {@inheritDoc} */
@Override
protected String parseValue(Object obj) throws ParameterException {
if(obj == null) {
- throw new UnspecifiedParameterException("Parameter \"" + getName() + "\": Null value given!");
+ throw new UnspecifiedParameterException(this);
}
- if (obj instanceof String) {
+ if(obj instanceof String) {
return (String) obj;
}
// TODO: allow anything convertible by toString()?
- throw new WrongParameterValueException("String parameter "+getName()+" is not a string.");
+ throw new WrongParameterValueException("String parameter " + getName() + " is not a string.");
}
/**
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/VectorListParameter.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/VectorListParameter.java
index 0e6612b6..a9d01f22 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/VectorListParameter.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/VectorListParameter.java
@@ -48,7 +48,8 @@ public class VectorListParameter extends ListParameter<List<Double>> {
* @param defaultValue Default value
*/
public VectorListParameter(OptionID optionID, List<ParameterConstraint<List<List<Double>>>> constraints, List<List<Double>> defaultValue) {
- super(optionID, constraints, defaultValue);
+ super(optionID, defaultValue);
+ addConstraints(constraints);
}
/**
@@ -59,30 +60,20 @@ public class VectorListParameter extends ListParameter<List<Double>> {
* @param optional Optional flag
*/
public VectorListParameter(OptionID optionID, List<ParameterConstraint<List<List<Double>>>> constraints, boolean optional) {
- super(optionID, constraints, optional);
+ super(optionID, optional);
+ addConstraints(constraints);
}
/**
* Constructs a vector list parameter with the given name and description.
*
* @param optionID Option ID
- * @param constraints Constraints
- */
- // Indiscernible from optionID, defaults
- /*
- * public VectorListParameter(OptionID optionID, List<ParameterConstraint<?
- * super List<List<Double>>>> constraints) { super(optionID, constraints); }
- */
-
- /**
- * Constructs a vector list parameter with the given name and description.
- *
- * @param optionID Option ID
* @param constraint Constraint
* @param defaultValue Default value
*/
public VectorListParameter(OptionID optionID, ParameterConstraint<List<List<Double>>> constraint, List<List<Double>> defaultValue) {
- super(optionID, constraint, defaultValue);
+ super(optionID, defaultValue);
+ addConstraint(constraint);
}
/**
@@ -93,7 +84,8 @@ public class VectorListParameter extends ListParameter<List<Double>> {
* @param optional Optional flag
*/
public VectorListParameter(OptionID optionID, ParameterConstraint<List<List<Double>>> constraint, boolean optional) {
- super(optionID, constraint, optional);
+ super(optionID, optional);
+ addConstraint(constraint);
}
/**
@@ -103,7 +95,8 @@ public class VectorListParameter extends ListParameter<List<Double>> {
* @param constraint Constraint
*/
public VectorListParameter(OptionID optionID, ParameterConstraint<List<List<Double>>> constraint) {
- super(optionID, constraint);
+ super(optionID);
+ addConstraint(constraint);
}
/**
@@ -137,17 +130,16 @@ public class VectorListParameter extends ListParameter<List<Double>> {
super(optionID);
}
- /** {@inheritDoc} */
@Override
public String getValueAsString() {
- StringBuffer buf = new StringBuffer();
+ StringBuilder buf = new StringBuilder();
List<List<Double>> val = getValue();
Iterator<List<Double>> valiter = val.iterator();
while(valiter.hasNext()) {
List<Double> vec = valiter.next();
Iterator<Double> veciter = vec.iterator();
while(veciter.hasNext()) {
- buf.append(Double.toString(veciter.next()));
+ buf.append(veciter.next().toString());
if (veciter.hasNext()) {
buf.append(LIST_SEP);
}
@@ -160,7 +152,6 @@ public class VectorListParameter extends ListParameter<List<Double>> {
return buf.toString();
}
- /** {@inheritDoc} */
@SuppressWarnings("unchecked")
@Override
protected List<List<Double>> parseValue(Object obj) throws ParameterException {
@@ -185,7 +176,7 @@ public class VectorListParameter extends ListParameter<List<Double>> {
if(obj instanceof String) {
String[] vectors = VECTOR_SPLIT.split((String) obj);
if(vectors.length == 0) {
- throw new UnspecifiedParameterException("Wrong parameter format! Given list of vectors for parameter \"" + getName() + "\" is empty!");
+ throw new WrongParameterValueException("Wrong parameter format! Given list of vectors for parameter \"" + getName() + "\" is empty!");
}
ArrayList<List<Double>> vecs = new ArrayList<List<Double>>();
@@ -194,8 +185,7 @@ public class VectorListParameter extends ListParameter<List<Double>> {
ArrayList<Double> vectorCoord = new ArrayList<Double>();
for(String coordinate : coordinates) {
try {
- Double.parseDouble(coordinate);
- vectorCoord.add(Double.parseDouble(coordinate));
+ vectorCoord.add(Double.valueOf(coordinate));
}
catch(NumberFormatException e) {
throw new WrongParameterValueException("Wrong parameter format! Coordinates of vector \"" + vector + "\" are not valid!");
@@ -209,23 +199,6 @@ public class VectorListParameter extends ListParameter<List<Double>> {
}
/**
- * Returns an array containing the individual vector sizes of this vector list
- * parameter.
- *
- * @return the individual vector sizes
- */
- // unused?
- /*
- * public int[] vectorSizes() {
- *
- * int[] sizes = new int[getListSize()];
- *
- * int i = 0; for(List<?> vecs : value) { sizes[i] = vecs.size(); i++; }
- *
- * return sizes; }
- */
-
- /**
* Returns a string representation of the parameter's type.
*
* @return
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/package-info.java b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/package-info.java
index 5fcaa0c0..b6ff5216 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/package-info.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/optionhandling/parameters/package-info.java
@@ -3,6 +3,10 @@
*
* See the {@link de.lmu.ifi.dbs.elki.utilities.optionhandling} package for documentation!
*
+ * @apiviz.exclude elki.utilities.*
+ * @apiviz.exclude gui.configurator.*
+ * @apiviz.exclude AbstractParameterizer
+ * @apiviz.exclude constraints.ParameterConstraint
*/
/*
This file is part of ELKI:
@@ -26,4 +30,4 @@ GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters; \ No newline at end of file
+package de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters;
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/pairs/CTriple.java b/src/de/lmu/ifi/dbs/elki/utilities/pairs/CTriple.java
index 225fb0b9..2a07fc28 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/pairs/CTriple.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/pairs/CTriple.java
@@ -103,18 +103,4 @@ public final class CTriple<FIRST extends Comparable<? super FIRST>, SECOND exten
}
return 0;
}
-
- /**
- * Array constructor for generics
- *
- * @param <F> First type
- * @param <S> Second type
- * @param <T> Third type
- * @param size Size of array to be constructed.
- * @return New array of requested size
- */
- public static final <F extends Comparable<F>, S extends Comparable<S>, T extends Comparable<T>> CTriple<F, S, T>[] newArray(int size) {
- Class<CTriple<F, S, T>> tripcls = ClassGenericsUtil.uglyCastIntoSubclass(CTriple.class);
- return ClassGenericsUtil.newArrayOfNull(size, tripcls);
- }
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/pairs/DoubleDoublePair.java b/src/de/lmu/ifi/dbs/elki/utilities/pairs/DoubleDoublePair.java
index c24ee8f3..143c8014 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/pairs/DoubleDoublePair.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/pairs/DoubleDoublePair.java
@@ -130,7 +130,7 @@ public class DoubleDoublePair implements Comparable<DoubleDoublePair>, PairInter
@Override
@Deprecated
public final Double getFirst() {
- return first;
+ return Double.valueOf(first);
}
/**
@@ -148,7 +148,7 @@ public class DoubleDoublePair implements Comparable<DoubleDoublePair>, PairInter
@Override
@Deprecated
public final Double getSecond() {
- return second;
+ return Double.valueOf(second);
}
/**
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/pairs/DoubleIntPair.java b/src/de/lmu/ifi/dbs/elki/utilities/pairs/DoubleIntPair.java
index 7bd22095..17698454 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/pairs/DoubleIntPair.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/pairs/DoubleIntPair.java
@@ -127,7 +127,7 @@ public class DoubleIntPair implements Comparable<DoubleIntPair>, PairInterface<D
@Override
@Deprecated
public final Double getFirst() {
- return first;
+ return Double.valueOf(first);
}
/**
@@ -145,7 +145,7 @@ public class DoubleIntPair implements Comparable<DoubleIntPair>, PairInterface<D
@Override
@Deprecated
public final Integer getSecond() {
- return second;
+ return Integer.valueOf(second);
}
/**
@@ -158,7 +158,7 @@ public class DoubleIntPair implements Comparable<DoubleIntPair>, PairInterface<D
}
/**
- * Comparator to compare by second component only
+ * Comparator to compare by first component only
*/
public static final Comparator<DoubleIntPair> BYFIRST_COMPARATOR = new Comparator<DoubleIntPair>() {
@Override
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/pairs/DoubleObjPair.java b/src/de/lmu/ifi/dbs/elki/utilities/pairs/DoubleObjPair.java
index aa4d4b36..3bffd979 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/pairs/DoubleObjPair.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/pairs/DoubleObjPair.java
@@ -59,7 +59,7 @@ public class DoubleObjPair<O> implements PairInterface<Double, O>, Comparable<Do
@Override
@Deprecated
public Double getFirst() {
- return first;
+ return Double.valueOf(first);
}
@Override
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/pairs/IntDoublePair.java b/src/de/lmu/ifi/dbs/elki/utilities/pairs/IntDoublePair.java
index d0f356f8..368134b0 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/pairs/IntDoublePair.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/pairs/IntDoublePair.java
@@ -127,7 +127,7 @@ public class IntDoublePair implements Comparable<IntDoublePair>, PairInterface<I
@Override
@Deprecated
public final Integer getFirst() {
- return first;
+ return Integer.valueOf(first);
}
/**
@@ -145,7 +145,7 @@ public class IntDoublePair implements Comparable<IntDoublePair>, PairInterface<I
@Override
@Deprecated
public final Double getSecond() {
- return second;
+ return Double.valueOf(second);
}
/**
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/pairs/IntIntPair.java b/src/de/lmu/ifi/dbs/elki/utilities/pairs/IntIntPair.java
index 96108239..108afdef 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/pairs/IntIntPair.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/pairs/IntIntPair.java
@@ -124,7 +124,7 @@ public class IntIntPair implements Comparable<IntIntPair>, PairInterface<Integer
@Override
@Deprecated
public final Integer getFirst() {
- return first;
+ return Integer.valueOf(first);
}
/**
@@ -142,7 +142,7 @@ public class IntIntPair implements Comparable<IntIntPair>, PairInterface<Integer
@Override
@Deprecated
public final Integer getSecond() {
- return second;
+ return Integer.valueOf(second);
}
/**
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/pairs/PairUtil.java b/src/de/lmu/ifi/dbs/elki/utilities/pairs/PairUtil.java
index 21917332..01b27ca0 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/pairs/PairUtil.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/pairs/PairUtil.java
@@ -148,7 +148,7 @@ public final class PairUtil {
* @param <FIRST> First type
* @param <SECOND> Second type
*/
- public final static class CompareNatural<FIRST extends Comparable<? super FIRST>, SECOND extends Comparable<? super SECOND>> implements Comparator<Pair<? extends FIRST, ? extends SECOND>> {
+ public static final class CompareNatural<FIRST extends Comparable<? super FIRST>, SECOND extends Comparable<? super SECOND>> implements Comparator<Pair<? extends FIRST, ? extends SECOND>> {
/**
* Compare by first, then by second.
*
@@ -194,7 +194,7 @@ public final class PairUtil {
* @param <FIRST> First type
* @param <SECOND> Second type
*/
- public final static class CompareNaturalFirst<FIRST extends Comparable<? super FIRST>, SECOND> implements Comparator<Pair<? extends FIRST, ? extends SECOND>> {
+ public static final class CompareNaturalFirst<FIRST extends Comparable<? super FIRST>, SECOND> implements Comparator<Pair<? extends FIRST, ? extends SECOND>> {
/**
* Compare by first component natural ordering
*
@@ -227,7 +227,7 @@ public final class PairUtil {
* @param <FIRST> First type
* @param <SECOND> Second type
*/
- public final static class CompareNaturalSecond<FIRST, SECOND extends Comparable<? super SECOND>> implements Comparator<Pair<? extends FIRST, ? extends SECOND>> {
+ public static final class CompareNaturalSecond<FIRST, SECOND extends Comparable<? super SECOND>> implements Comparator<Pair<? extends FIRST, ? extends SECOND>> {
/**
* Compare by second components natural ordering
*
@@ -260,7 +260,7 @@ public final class PairUtil {
* @param <FIRST> First type
* @param <SECOND> Second type
*/
- public final static class CompareNaturalSwapped<FIRST extends Comparable<? super FIRST>, SECOND extends Comparable<? super SECOND>> implements Comparator<Pair<? extends FIRST, ? extends SECOND>> {
+ public static final class CompareNaturalSwapped<FIRST extends Comparable<? super FIRST>, SECOND extends Comparable<? super SECOND>> implements Comparator<Pair<? extends FIRST, ? extends SECOND>> {
/**
* Compare by second component, using the ComparableSwapped interface.
*
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/referencepoints/AxisBasedReferencePoints.java b/src/de/lmu/ifi/dbs/elki/utilities/referencepoints/AxisBasedReferencePoints.java
index 3ec38f58..26a4c7ed 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/referencepoints/AxisBasedReferencePoints.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/referencepoints/AxisBasedReferencePoints.java
@@ -28,6 +28,7 @@ import java.util.Collection;
import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
@@ -46,7 +47,7 @@ import de.lmu.ifi.dbs.elki.utilities.pairs.Pair;
*
* @param <V> Vector type
*/
-public class AxisBasedReferencePoints<V extends NumberVector<V, ?>> implements ReferencePointsHeuristic<V> {
+public class AxisBasedReferencePoints<V extends NumberVector<?>> implements ReferencePointsHeuristic<V> {
/**
* Parameter to specify the extra scaling of the space, to allow
* out-of-data-space reference points.
@@ -54,7 +55,7 @@ public class AxisBasedReferencePoints<V extends NumberVector<V, ?>> implements R
* Key: {@code -axisref.scale}
* </p>
*/
- public static final OptionID SPACE_SCALE_ID = OptionID.getOrCreateOptionID("axisref.scale", "Scale the data space extension by the given factor.");
+ public static final OptionID SPACE_SCALE_ID = new OptionID("axisref.scale", "Scale the data space extension by the given factor.");
/**
* Holds the value of {@link #SPACE_SCALE_ID}.
@@ -64,7 +65,7 @@ public class AxisBasedReferencePoints<V extends NumberVector<V, ?>> implements R
/**
* Constructor.
*
- * @param spacescale
+ * @param spacescale Extra scaling
*/
public AxisBasedReferencePoints(double spacescale) {
super();
@@ -75,38 +76,37 @@ public class AxisBasedReferencePoints<V extends NumberVector<V, ?>> implements R
public <T extends V> Collection<V> getReferencePoints(Relation<T> db) {
Relation<V> database = DatabaseUtil.relationUglyVectorCast(db);
Pair<V, V> minmax = DatabaseUtil.computeMinMax(database);
- V factory = DatabaseUtil.assumeVectorField(database).getFactory();
+ NumberVector.Factory<V, ?> factory = RelationUtil.getNumberVectorFactory(database);
- int dim = DatabaseUtil.dimensionality(db);
+ int dim = RelationUtil.dimensionality(db);
// Compute mean and extend from minmax.
double[] mean = new double[dim];
double[] delta = new double[dim];
- for(int d = 0; d < dim; d++) {
- mean[d] = (minmax.first.doubleValue(d + 1) + minmax.second.doubleValue(d + 1)) / 2;
- delta[d] = spacescale * (minmax.second.doubleValue(d + 1) - mean[d]);
+ for (int d = 0; d < dim; d++) {
+ mean[d] = (minmax.first.doubleValue(d) + minmax.second.doubleValue(d)) * .5;
+ delta[d] = spacescale * (minmax.second.doubleValue(d) - mean[d]);
}
ArrayList<V> result = new ArrayList<V>(2 + dim);
double[] vec = new double[dim];
// Use min and max
- for(int d = 0; d < dim; d++) {
+ for (int d = 0; d < dim; d++) {
vec[d] = mean[d] - delta[d];
}
result.add(factory.newNumberVector(vec));
- for(int d = 0; d < dim; d++) {
+ for (int d = 0; d < dim; d++) {
vec[d] = mean[d] + delta[d];
}
result.add(factory.newNumberVector(vec));
// Plus axis end points:
- for(int i = 0; i < dim; i++) {
- for(int d = 0; d < dim; d++) {
- if(d != i) {
+ for (int i = 0; i < dim; i++) {
+ for (int d = 0; d < dim; d++) {
+ if (d != i) {
vec[d] = mean[d] - delta[d];
- }
- else {
+ } else {
vec[d] = mean[d] + delta[d];
}
}
@@ -123,7 +123,7 @@ public class AxisBasedReferencePoints<V extends NumberVector<V, ?>> implements R
*
* @apiviz.exclude
*/
- public static class Parameterizer<V extends NumberVector<V, ?>> extends AbstractParameterizer {
+ public static class Parameterizer<V extends NumberVector<?>> extends AbstractParameterizer {
/**
* Holds the value of {@link #SPACE_SCALE_ID}.
*/
@@ -132,8 +132,9 @@ public class AxisBasedReferencePoints<V extends NumberVector<V, ?>> implements R
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- DoubleParameter spacescaleP = new DoubleParameter(SPACE_SCALE_ID, new GreaterEqualConstraint(0.0), 1.0);
- if(config.grab(spacescaleP)) {
+ DoubleParameter spacescaleP = new DoubleParameter(SPACE_SCALE_ID, 1.0);
+ spacescaleP.addConstraint(new GreaterEqualConstraint(0.0));
+ if (config.grab(spacescaleP)) {
spacescale = spacescaleP.getValue();
}
}
@@ -143,4 +144,4 @@ public class AxisBasedReferencePoints<V extends NumberVector<V, ?>> implements R
return new AxisBasedReferencePoints<V>(spacescale);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/referencepoints/FullDatabaseReferencePoints.java b/src/de/lmu/ifi/dbs/elki/utilities/referencepoints/FullDatabaseReferencePoints.java
index d427e35a..335a63f8 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/referencepoints/FullDatabaseReferencePoints.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/referencepoints/FullDatabaseReferencePoints.java
@@ -36,7 +36,7 @@ import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
*
* @param <O> Object type.
*/
-public class FullDatabaseReferencePoints<O extends NumberVector<? extends O, ?>> implements ReferencePointsHeuristic<O> {
+public class FullDatabaseReferencePoints<O extends NumberVector<?>> implements ReferencePointsHeuristic<O> {
/**
* Constructor, Parameterizable style.
*/
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/referencepoints/GridBasedReferencePoints.java b/src/de/lmu/ifi/dbs/elki/utilities/referencepoints/GridBasedReferencePoints.java
index 09d36cd8..60e47872 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/referencepoints/GridBasedReferencePoints.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/referencepoints/GridBasedReferencePoints.java
@@ -28,6 +28,7 @@ import java.util.Collection;
import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
@@ -44,7 +45,7 @@ import de.lmu.ifi.dbs.elki.utilities.pairs.Pair;
*
* @param <V> Object type
*/
-public class GridBasedReferencePoints<V extends NumberVector<V, ?>> implements ReferencePointsHeuristic<V> {
+public class GridBasedReferencePoints<V extends NumberVector<?>> implements ReferencePointsHeuristic<V> {
// TODO: add "grid sampling" option.
/**
@@ -53,7 +54,7 @@ public class GridBasedReferencePoints<V extends NumberVector<V, ?>> implements R
* Key: {@code -grid.size}
* </p>
*/
- public static final OptionID GRID_ID = OptionID.getOrCreateOptionID("grid.size", "The number of partitions in each dimension. Points will be placed on the edges of the grid, except for a grid size of 0, where only the mean is generated as reference point.");
+ public static final OptionID GRID_ID = new OptionID("grid.size", "The number of partitions in each dimension. Points will be placed on the edges of the grid, except for a grid size of 0, where only the mean is generated as reference point.");
/**
* Parameter to specify the extra scaling of the space, to allow
@@ -62,7 +63,7 @@ public class GridBasedReferencePoints<V extends NumberVector<V, ?>> implements R
* Key: {@code -grid.oversize}
* </p>
*/
- public static final OptionID GRID_SCALE_ID = OptionID.getOrCreateOptionID("grid.scale", "Scale the grid by the given factor. This can be used to obtain reference points outside the used data space.");
+ public static final OptionID GRID_SCALE_ID = new OptionID("grid.scale", "Scale the grid by the given factor. This can be used to obtain reference points outside the used data space.");
/**
* Holds the value of {@link #GRID_ID}.
@@ -77,8 +78,8 @@ public class GridBasedReferencePoints<V extends NumberVector<V, ?>> implements R
/**
* Constructor.
*
- * @param gridres
- * @param gridscale
+ * @param gridres Grid resolution
+ * @param gridscale Grid scaling
*/
public GridBasedReferencePoints(int gridres, double gridscale) {
super();
@@ -90,29 +91,29 @@ public class GridBasedReferencePoints<V extends NumberVector<V, ?>> implements R
public <T extends V> Collection<V> getReferencePoints(Relation<T> db) {
Relation<V> database = DatabaseUtil.relationUglyVectorCast(db);
Pair<V, V> minmax = DatabaseUtil.computeMinMax(database);
- V factory = DatabaseUtil.assumeVectorField(database).getFactory();
+ NumberVector.Factory<V, ?> factory = RelationUtil.getNumberVectorFactory(database);
- int dim = DatabaseUtil.dimensionality(db);
+ int dim = RelationUtil.dimensionality(db);
// Compute mean from minmax.
double[] mean = new double[dim];
- for(int d = 0; d < dim; d++) {
- mean[d] = (minmax.first.doubleValue(d + 1) + minmax.second.doubleValue(d + 1)) / 2;
+ for (int d = 0; d < dim; d++) {
+ mean[d] = (minmax.first.doubleValue(d) + minmax.second.doubleValue(d)) * .5;
}
int gridpoints = Math.max(1, (int) Math.pow(gridres + 1, dim));
ArrayList<V> result = new ArrayList<V>(gridpoints);
double[] delta = new double[dim];
- if(gridres > 0) {
+ if (gridres > 0) {
double halfgrid = gridres / 2.0;
- for(int d = 0; d < dim; d++) {
- delta[d] = (minmax.second.doubleValue(d + 1) - minmax.first.doubleValue(d + 1)) / gridres;
+ for (int d = 0; d < dim; d++) {
+ delta[d] = (minmax.second.doubleValue(d) - minmax.first.doubleValue(d)) / gridres;
}
double[] vec = new double[dim];
- for(int i = 0; i < gridpoints; i++) {
+ for (int i = 0; i < gridpoints; i++) {
int acc = i;
- for(int d = 0; d < dim; d++) {
+ for (int d = 0; d < dim; d++) {
int coord = acc % (gridres + 1);
acc = acc / (gridres + 1);
vec[d] = mean[d] + (coord - halfgrid) * delta[d] * gridscale;
@@ -121,8 +122,7 @@ public class GridBasedReferencePoints<V extends NumberVector<V, ?>> implements R
// logger.debug("New reference point: " + FormatUtil.format(vec));
result.add(newp);
}
- }
- else {
+ } else {
result.add(factory.newNumberVector(mean));
// logger.debug("New reference point: " + FormatUtil.format(mean));
}
@@ -137,7 +137,7 @@ public class GridBasedReferencePoints<V extends NumberVector<V, ?>> implements R
*
* @apiviz.exclude
*/
- public static class Parameterizer<V extends NumberVector<V, ?>> extends AbstractParameterizer {
+ public static class Parameterizer<V extends NumberVector<?>> extends AbstractParameterizer {
/**
* Holds the value of {@link #GRID_ID}.
*/
@@ -151,14 +151,16 @@ public class GridBasedReferencePoints<V extends NumberVector<V, ?>> implements R
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- IntParameter GRID_PARAM = new IntParameter(GRID_ID, new GreaterEqualConstraint(0), 1);
- if(config.grab(GRID_PARAM)) {
- gridres = GRID_PARAM.getValue();
+ IntParameter gridP = new IntParameter(GRID_ID, 1);
+ gridP.addConstraint(new GreaterEqualConstraint(0));
+ if (config.grab(gridP)) {
+ gridres = gridP.getValue();
}
- DoubleParameter GRID_SCALE_PARAM = new DoubleParameter(GRID_SCALE_ID, new GreaterEqualConstraint(0.0), 1.0);
- if(config.grab(GRID_SCALE_PARAM)) {
- gridscale = GRID_SCALE_PARAM.getValue();
+ DoubleParameter gridscaleP = new DoubleParameter(GRID_SCALE_ID, 1.0);
+ gridscaleP.addConstraint(new GreaterEqualConstraint(0.0));
+ if (config.grab(gridscaleP)) {
+ gridscale = gridscaleP.getValue();
}
}
@@ -167,4 +169,4 @@ public class GridBasedReferencePoints<V extends NumberVector<V, ?>> implements R
return new GridBasedReferencePoints<V>(gridres, gridscale);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/referencepoints/RandomGeneratedReferencePoints.java b/src/de/lmu/ifi/dbs/elki/utilities/referencepoints/RandomGeneratedReferencePoints.java
index 6a56bfcd..71e8e4c8 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/referencepoints/RandomGeneratedReferencePoints.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/referencepoints/RandomGeneratedReferencePoints.java
@@ -28,6 +28,7 @@ import java.util.Collection;
import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
@@ -45,14 +46,14 @@ import de.lmu.ifi.dbs.elki.utilities.pairs.Pair;
* @param <V> Object type
*/
// TODO: Erich: use reproducible random
-public class RandomGeneratedReferencePoints<V extends NumberVector<V, ?>> implements ReferencePointsHeuristic<V> {
+public class RandomGeneratedReferencePoints<V extends NumberVector<?>> implements ReferencePointsHeuristic<V> {
/**
* Parameter to specify the number of requested reference points.
* <p>
* Key: {@code -generate.n}
* </p>
*/
- public static final OptionID N_ID = OptionID.getOrCreateOptionID("generate.n", "The number of reference points to be generated.");
+ public static final OptionID N_ID = new OptionID("generate.n", "The number of reference points to be generated.");
/**
* Parameter for additional scaling of the space, to allow out-of-space
@@ -61,7 +62,7 @@ public class RandomGeneratedReferencePoints<V extends NumberVector<V, ?>> implem
* Key: {@code -generate.scale}
* </p>
*/
- public static final OptionID SCALE_ID = OptionID.getOrCreateOptionID("generate.scale", "Scale the grid by the given factor. This can be used to obtain reference points outside the used data space.");
+ public static final OptionID SCALE_ID = new OptionID("generate.scale", "Scale the grid by the given factor. This can be used to obtain reference points outside the used data space.");
/**
* Holds the value of {@link #N_ID}.
@@ -76,8 +77,8 @@ public class RandomGeneratedReferencePoints<V extends NumberVector<V, ?>> implem
/**
* Constructor.
*
- * @param samplesize
- * @param scale
+ * @param samplesize Size of desired sample set
+ * @param scale Scaling factor
*/
public RandomGeneratedReferencePoints(int samplesize, double scale) {
super();
@@ -89,22 +90,22 @@ public class RandomGeneratedReferencePoints<V extends NumberVector<V, ?>> implem
public <T extends V> Collection<V> getReferencePoints(Relation<T> db) {
Relation<V> database = DatabaseUtil.relationUglyVectorCast(db);
Pair<V, V> minmax = DatabaseUtil.computeMinMax(database);
- V factory = DatabaseUtil.assumeVectorField(database).getFactory();
+ NumberVector.Factory<V, ?> factory = RelationUtil.getNumberVectorFactory(database);
- int dim = DatabaseUtil.dimensionality(db);
+ int dim = RelationUtil.dimensionality(db);
// Compute mean from minmax.
double[] mean = new double[dim];
double[] delta = new double[dim];
- for(int d = 0; d < dim; d++) {
- mean[d] = (minmax.first.doubleValue(d + 1) + minmax.second.doubleValue(d + 1)) / 2;
+ for (int d = 0; d < dim; d++) {
+ mean[d] = (minmax.first.doubleValue(d + 1) + minmax.second.doubleValue(d + 1)) * .5;
delta[d] = (minmax.second.doubleValue(d + 1) - minmax.first.doubleValue(d + 1));
}
ArrayList<V> result = new ArrayList<V>(samplesize);
double[] vec = new double[dim];
- for(int i = 0; i < samplesize; i++) {
- for(int d = 0; d < dim; d++) {
+ for (int i = 0; i < samplesize; i++) {
+ for (int d = 0; d < dim; d++) {
vec[d] = mean[d] + (Math.random() - 0.5) * scale * delta[d];
}
V newp = factory.newNumberVector(vec);
@@ -122,7 +123,7 @@ public class RandomGeneratedReferencePoints<V extends NumberVector<V, ?>> implem
*
* @apiviz.exclude
*/
- public static class Parameterizer<V extends NumberVector<V, ?>> extends AbstractParameterizer {
+ public static class Parameterizer<V extends NumberVector<?>> extends AbstractParameterizer {
/**
* Holds the value of {@link #N_ID}.
*/
@@ -137,13 +138,15 @@ public class RandomGeneratedReferencePoints<V extends NumberVector<V, ?>> implem
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- IntParameter samplesizeP = new IntParameter(N_ID, new GreaterConstraint(0));
- if(config.grab(samplesizeP)) {
+ IntParameter samplesizeP = new IntParameter(N_ID);
+ samplesizeP.addConstraint(new GreaterConstraint(0));
+ if (config.grab(samplesizeP)) {
samplesize = samplesizeP.getValue();
}
- DoubleParameter scaleP = new DoubleParameter(SCALE_ID, new GreaterConstraint(0.0), 1.0);
- if(config.grab(scaleP)) {
+ DoubleParameter scaleP = new DoubleParameter(SCALE_ID, 1.0);
+ scaleP.addConstraint(new GreaterConstraint(0.0));
+ if (config.grab(scaleP)) {
scale = scaleP.getValue();
}
}
@@ -153,4 +156,4 @@ public class RandomGeneratedReferencePoints<V extends NumberVector<V, ?>> implem
return new RandomGeneratedReferencePoints<V>(samplesize, scale);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/referencepoints/RandomSampleReferencePoints.java b/src/de/lmu/ifi/dbs/elki/utilities/referencepoints/RandomSampleReferencePoints.java
index 8f6df4db..2cb778b3 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/referencepoints/RandomSampleReferencePoints.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/referencepoints/RandomSampleReferencePoints.java
@@ -30,7 +30,6 @@ import java.util.HashSet;
import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.database.ids.ArrayDBIDs;
import de.lmu.ifi.dbs.elki.database.ids.ArrayModifiableDBIDs;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
@@ -48,7 +47,7 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
*
* @param <V> Vector type
*/
-public class RandomSampleReferencePoints<V extends NumberVector<? extends V, ?>> implements ReferencePointsHeuristic<V> {
+public class RandomSampleReferencePoints<V extends NumberVector<?>> implements ReferencePointsHeuristic<V> {
// TODO: use reproducible Random
/**
@@ -57,12 +56,12 @@ public class RandomSampleReferencePoints<V extends NumberVector<? extends V, ?>>
* Key: {@code -sample.n}
* </p>
*/
- public static final OptionID N_ID = OptionID.getOrCreateOptionID("sample.n", "The number of samples to draw.");
+ public static final OptionID N_ID = new OptionID("sample.n", "The number of samples to draw.");
/**
- * Constant used in choosing optimal table sizes
+ * Constant used in choosing optimal table sizes.
*/
- private static final double log4 = Math.log(4);
+ private static final double LOG4 = Math.log(4);
/**
* Holds the value of {@link #N_ID}.
@@ -72,7 +71,7 @@ public class RandomSampleReferencePoints<V extends NumberVector<? extends V, ?>>
/**
* Constructor.
*
- * @param samplesize
+ * @param samplesize Sampling size
*/
public RandomSampleReferencePoints(int samplesize) {
super();
@@ -86,8 +85,7 @@ public class RandomSampleReferencePoints<V extends NumberVector<? extends V, ?>>
ArrayList<V> selection = new ArrayList<V>(db.size());
for(DBIDIter iditer = db.iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
- selection.add(db.get(id));
+ selection.add(db.get(iditer));
}
return selection;
}
@@ -101,7 +99,7 @@ public class RandomSampleReferencePoints<V extends NumberVector<? extends V, ?>>
// If the hashmap is likely to become too big, lazy-shuffle a list instead.
int setsize = 21;
if(samplesize > 5) {
- setsize += 2 << (int) Math.ceil(Math.log(samplesize * 3) / log4);
+ setsize += 2 << (int) Math.ceil(Math.log(samplesize * 3) / LOG4);
}
// logger.debug("Setsize: "+setsize);
ArrayDBIDs ids = DBIDUtil.ensureArray(db.getDBIDs());
@@ -143,7 +141,7 @@ public class RandomSampleReferencePoints<V extends NumberVector<? extends V, ?>>
*
* @apiviz.exclude
*/
- public static class Parameterizer<V extends NumberVector<? extends V, ?>> extends AbstractParameterizer {
+ public static class Parameterizer<V extends NumberVector<?>> extends AbstractParameterizer {
/**
* Holds the value of {@link #N_ID}.
*/
@@ -152,9 +150,10 @@ public class RandomSampleReferencePoints<V extends NumberVector<? extends V, ?>>
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- IntParameter samplesizeP = new IntParameter(N_ID, new GreaterConstraint(0));
+ IntParameter samplesizeP = new IntParameter(N_ID);
+ samplesizeP.addConstraint(new GreaterConstraint(0));
if(config.grab(samplesizeP)) {
- samplesize = samplesizeP.getValue();
+ samplesize = samplesizeP.intValue();
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/referencepoints/StarBasedReferencePoints.java b/src/de/lmu/ifi/dbs/elki/utilities/referencepoints/StarBasedReferencePoints.java
index 47c7298b..0e562770 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/referencepoints/StarBasedReferencePoints.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/referencepoints/StarBasedReferencePoints.java
@@ -27,9 +27,9 @@ import java.util.ArrayList;
import java.util.Collection;
import de.lmu.ifi.dbs.elki.data.NumberVector;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
@@ -45,14 +45,14 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.Flag;
*
* @param <V> Object type
*/
-public class StarBasedReferencePoints<V extends NumberVector<V, ?>> implements ReferencePointsHeuristic<V> {
+public class StarBasedReferencePoints<V extends NumberVector<?>> implements ReferencePointsHeuristic<V> {
/**
* Parameter to specify the grid resolution.
* <p>
* Key: {@code -star.nocenter}
* </p>
*/
- public static final OptionID NOCENTER_ID = OptionID.getOrCreateOptionID("star.nocenter", "Do not use the center as extra reference point.");
+ public static final OptionID NOCENTER_ID = new OptionID("star.nocenter", "Do not use the center as extra reference point.");
/**
* Parameter to specify the extra scaling of the space, to allow
@@ -61,7 +61,7 @@ public class StarBasedReferencePoints<V extends NumberVector<V, ?>> implements R
* Key: {@code -star.scale}
* </p>
*/
- public static final OptionID SCALE_ID = OptionID.getOrCreateOptionID("star.scale", "Scale the reference points by the given factor. This can be used to obtain reference points outside the used data space.");
+ public static final OptionID SCALE_ID = new OptionID("star.scale", "Scale the reference points by the given factor. This can be used to obtain reference points outside the used data space.");
/**
* Holds the value of {@link #NOCENTER_ID}.
@@ -76,8 +76,8 @@ public class StarBasedReferencePoints<V extends NumberVector<V, ?>> implements R
/**
* Constructor.
*
- * @param nocenter
- * @param scale
+ * @param nocenter Do not include center point
+ * @param scale Scaling factor
*/
public StarBasedReferencePoints(boolean nocenter, double scale) {
super();
@@ -88,23 +88,22 @@ public class StarBasedReferencePoints<V extends NumberVector<V, ?>> implements R
@Override
public <T extends V> Collection<V> getReferencePoints(Relation<T> db) {
Relation<V> database = DatabaseUtil.relationUglyVectorCast(db);
- V factory = DatabaseUtil.assumeVectorField(database).getFactory();
+ NumberVector.Factory<V, ?> factory = RelationUtil.getNumberVectorFactory(database);
- int dim = DatabaseUtil.dimensionality(db);
+ int dim = RelationUtil.dimensionality(db);
// Compute minimum, maximum and centroid
double[] centroid = new double[dim];
double[] min = new double[dim];
double[] max = new double[dim];
- for(int d = 0; d < dim; d++) {
+ for (int d = 0; d < dim; d++) {
centroid[d] = 0;
min[d] = Double.MAX_VALUE;
max[d] = -Double.MAX_VALUE;
}
- for(DBIDIter iditer = database.iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID objID = iditer.getDBID();
- V obj = database.get(objID);
- for(int d = 0; d < dim; d++) {
+ for (DBIDIter iditer = database.iterDBIDs(); iditer.valid(); iditer.advance()) {
+ V obj = database.get(iditer);
+ for (int d = 0; d < dim; d++) {
double val = obj.doubleValue(d + 1);
centroid[d] += val;
min[d] = Math.min(min[d], val);
@@ -112,21 +111,21 @@ public class StarBasedReferencePoints<V extends NumberVector<V, ?>> implements R
}
}
// finish centroid, scale min, max
- for(int d = 0; d < dim; d++) {
+ for (int d = 0; d < dim; d++) {
centroid[d] = centroid[d] / database.size();
min[d] = (min[d] - centroid[d]) * scale + centroid[d];
max[d] = (max[d] - centroid[d]) * scale + centroid[d];
}
ArrayList<V> result = new ArrayList<V>(2 * dim + 1);
- if(!nocenter) {
+ if (!nocenter) {
result.add(factory.newNumberVector(centroid));
}
// Plus axis end points through centroid
double[] vec = new double[dim];
- for(int i = 0; i < dim; i++) {
- for(int d = 0; d < dim; d++) {
- if(d != i) {
+ for (int i = 0; i < dim; i++) {
+ for (int d = 0; d < dim; d++) {
+ if (d != i) {
vec[d] = centroid[d];
}
}
@@ -146,7 +145,7 @@ public class StarBasedReferencePoints<V extends NumberVector<V, ?>> implements R
*
* @apiviz.exclude
*/
- public static class Parameterizer<V extends NumberVector<V, ?>> extends AbstractParameterizer {
+ public static class Parameterizer<V extends NumberVector<?>> extends AbstractParameterizer {
/**
* Holds the value of {@link #NOCENTER_ID}.
*/
@@ -161,12 +160,13 @@ public class StarBasedReferencePoints<V extends NumberVector<V, ?>> implements R
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
Flag nocenterF = new Flag(NOCENTER_ID);
- if(config.grab(nocenterF)) {
+ if (config.grab(nocenterF)) {
nocenter = nocenterF.getValue();
}
- DoubleParameter scaleP = new DoubleParameter(SCALE_ID, new GreaterEqualConstraint(0.0), 1.0);
- if(config.grab(scaleP)) {
+ DoubleParameter scaleP = new DoubleParameter(SCALE_ID, 1.0);
+ scaleP.addConstraint(new GreaterEqualConstraint(0.0));
+ if (config.grab(scaleP)) {
scale = scaleP.getValue();
}
}
@@ -176,4 +176,4 @@ public class StarBasedReferencePoints<V extends NumberVector<V, ?>> implements R
return new StarBasedReferencePoints<V>(nocenter, scale);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/scaling/ClipScaling.java b/src/de/lmu/ifi/dbs/elki/utilities/scaling/ClipScaling.java
index 97a43f37..aad0b63a 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/scaling/ClipScaling.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/scaling/ClipScaling.java
@@ -42,7 +42,7 @@ public class ClipScaling implements StaticScalingFunction {
* Key: {@code -clipscale.min}
* </p>
*/
- public static final OptionID MIN_ID = OptionID.getOrCreateOptionID("clipscale.min", "Minimum value to allow.");
+ public static final OptionID MIN_ID = new OptionID("clipscale.min", "Minimum value to allow.");
/**
* Parameter to specify the maximum value
@@ -50,7 +50,7 @@ public class ClipScaling implements StaticScalingFunction {
* Key: {@code -clipscale.max}
* </p>
*/
- public static final OptionID MAX_ID = OptionID.getOrCreateOptionID("clipscale.max", "Maximum value to allow.");
+ public static final OptionID MAX_ID = new OptionID("clipscale.max", "Maximum value to allow.");
/**
* Field storing the minimum to use
@@ -109,11 +109,13 @@ public class ClipScaling implements StaticScalingFunction {
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- DoubleParameter minP = new DoubleParameter(MIN_ID, true);
+ DoubleParameter minP = new DoubleParameter(MIN_ID);
+ minP.setOptional(true);
if(config.grab(minP)) {
min = minP.getValue();
}
- DoubleParameter maxP = new DoubleParameter(MAX_ID, true);
+ DoubleParameter maxP = new DoubleParameter(MAX_ID);
+ maxP.setOptional(true);
if (config.grab(maxP)) {
max = maxP.getValue();
}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/scaling/GammaScaling.java b/src/de/lmu/ifi/dbs/elki/utilities/scaling/GammaScaling.java
index 719a72c3..b23b9400 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/scaling/GammaScaling.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/scaling/GammaScaling.java
@@ -38,7 +38,7 @@ public class GammaScaling implements StaticScalingFunction {
/**
* OptionID for the gamma value.
*/
- public static final OptionID GAMMA_ID = OptionID.getOrCreateOptionID("scaling.gamma", "Gamma value for scaling.");
+ public static final OptionID GAMMA_ID = new OptionID("scaling.gamma", "Gamma value for scaling.");
/**
* Gamma value.
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/HeDESNormalizationOutlierScaling.java b/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/HeDESNormalizationOutlierScaling.java
index 8a152cff..4bf86f06 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/HeDESNormalizationOutlierScaling.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/HeDESNormalizationOutlierScaling.java
@@ -23,8 +23,8 @@ package de.lmu.ifi.dbs.elki.utilities.scaling.outlier;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.math.DoubleMinMax;
import de.lmu.ifi.dbs.elki.math.MeanVariance;
import de.lmu.ifi.dbs.elki.result.outlier.OutlierResult;
@@ -62,9 +62,9 @@ public class HeDESNormalizationOutlierScaling implements OutlierScalingFunction
MeanVariance mv = new MeanVariance();
DoubleMinMax minmax = new DoubleMinMax();
- for(DBIDIter iditer = or.getScores().iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
- double val = or.getScores().get(id);
+ Relation<Double> scores = or.getScores();
+ for(DBIDIter id = scores.iterDBIDs(); id.valid(); id.advance()) {
+ double val = scores.get(id);
if(!Double.isNaN(val) && !Double.isInfinite(val)) {
mv.put(val);
minmax.put(val);
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/MinusLogGammaScaling.java b/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/MinusLogGammaScaling.java
index 860c77d6..f7cf0df0 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/MinusLogGammaScaling.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/MinusLogGammaScaling.java
@@ -23,8 +23,8 @@ package de.lmu.ifi.dbs.elki.utilities.scaling.outlier;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.math.DoubleMinMax;
import de.lmu.ifi.dbs.elki.math.MeanVariance;
import de.lmu.ifi.dbs.elki.math.statistics.distribution.GammaDistribution;
@@ -68,9 +68,9 @@ public class MinusLogGammaScaling extends OutlierGammaScaling {
meta = or.getOutlierMeta();
// Determine Minimum and Maximum.
DoubleMinMax mm = new DoubleMinMax();
- for(DBIDIter iditer = or.getScores().iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
- double score = or.getScores().get(id);
+ Relation<Double> scores = or.getScores();
+ for(DBIDIter id = scores.iterDBIDs(); id.valid(); id.advance()) {
+ double score = scores.get(id);
if(!Double.isNaN(score) && !Double.isInfinite(score)) {
mm.put(score);
}
@@ -79,9 +79,8 @@ public class MinusLogGammaScaling extends OutlierGammaScaling {
mlogmax = -Math.log(mm.getMin() / max);
// with the prescaling, do Gamma Scaling.
MeanVariance mv = new MeanVariance();
- for(DBIDIter iditer = or.getScores().iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
- double score = or.getScores().get(id);
+ for(DBIDIter id = scores.iterDBIDs(); id.valid(); id.advance()) {
+ double score = scores.get(id);
score = preScale(score);
if(!Double.isNaN(score) && !Double.isInfinite(score)) {
mv.put(score);
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/MinusLogStandardDeviationScaling.java b/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/MinusLogStandardDeviationScaling.java
index 4bc10feb..e34355a1 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/MinusLogStandardDeviationScaling.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/MinusLogStandardDeviationScaling.java
@@ -23,9 +23,10 @@ package de.lmu.ifi.dbs.elki.utilities.scaling.outlier;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.math.MathUtil;
+import de.lmu.ifi.dbs.elki.math.Mean;
import de.lmu.ifi.dbs.elki.math.MeanVariance;
import de.lmu.ifi.dbs.elki.math.statistics.distribution.NormalDistribution;
import de.lmu.ifi.dbs.elki.result.outlier.OutlierResult;
@@ -68,9 +69,9 @@ public class MinusLogStandardDeviationScaling extends StandardDeviationScaling {
public void prepare(OutlierResult or) {
if(fixedmean == null) {
MeanVariance mv = new MeanVariance();
- for(DBIDIter iditer = or.getScores().iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
- double val = -Math.log(or.getScores().get(id));
+ Relation<Double> scores = or.getScores();
+ for(DBIDIter id = scores.iterDBIDs(); id.valid(); id.advance()) {
+ double val = -Math.log(scores.get(id));
if(!Double.isNaN(val) && !Double.isInfinite(val)) {
mv.put(val);
}
@@ -80,17 +81,15 @@ public class MinusLogStandardDeviationScaling extends StandardDeviationScaling {
}
else {
mean = fixedmean;
- double sqsum = 0;
- int cnt = 0;
- for(DBIDIter iditer = or.getScores().iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
- double val = -Math.log(or.getScores().get(id));
+ Mean sqsum = new Mean();
+ Relation<Double> scores = or.getScores();
+ for(DBIDIter id = scores.iterDBIDs(); id.valid(); id.advance()) {
+ double val = -Math.log(scores.get(id));
if(!Double.isNaN(val) && !Double.isInfinite(val)) {
- sqsum += (val - mean) * (val - mean);
- cnt += 1;
+ sqsum.put((val - mean) * (val - mean));
}
}
- factor = lambda * Math.sqrt(sqsum / cnt) * MathUtil.SQRT2;
+ factor = lambda * Math.sqrt(sqsum.getMean()) * MathUtil.SQRT2;
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/MixtureModelOutlierScalingFunction.java b/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/MixtureModelOutlierScalingFunction.java
index 52a7f449..e026b211 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/MixtureModelOutlierScalingFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/MixtureModelOutlierScalingFunction.java
@@ -24,9 +24,9 @@ package de.lmu.ifi.dbs.elki.utilities.scaling.outlier;
*/
import de.lmu.ifi.dbs.elki.database.ids.ArrayDBIDs;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
+import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.math.MathUtil;
import de.lmu.ifi.dbs.elki.math.MeanVariance;
@@ -44,7 +44,7 @@ public class MixtureModelOutlierScalingFunction implements OutlierScalingFunctio
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(MixtureModelOutlierScalingFunction.class);
+ private static final Logging LOG = Logging.getLogger(MixtureModelOutlierScalingFunction.class);
/**
* Parameter mu of the gaussian distribution (outliers)
@@ -69,7 +69,7 @@ public class MixtureModelOutlierScalingFunction implements OutlierScalingFunctio
/**
* Precomputed static value
*/
- public final static double ONEBYSQRT2PI = 1.0 / MathUtil.SQRTTWOPI;
+ public static final double ONEBYSQRT2PI = 1.0 / MathUtil.SQRTTWOPI;
/**
* Convergence parameter
@@ -121,14 +121,14 @@ public class MixtureModelOutlierScalingFunction implements OutlierScalingFunctio
public void prepare(OutlierResult or) {
// Initial parameters - are these defaults sounds?
MeanVariance mv = new MeanVariance();
- for(DBIDIter iditer = or.getScores().iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
- double val = or.getScores().get(id);
+ Relation<Double> scores = or.getScores();
+ for(DBIDIter id = scores.iterDBIDs(); id.valid(); id.advance()) {
+ double val = scores.get(id);
if(!Double.isNaN(val) && !Double.isInfinite(val)) {
mv.put(val);
}
}
- double curMu = mv.getMean() * 2;
+ double curMu = mv.getMean() * 2.;
if(curMu == 0) {
curMu = Double.MIN_NORMAL;
}
@@ -159,7 +159,7 @@ public class MixtureModelOutlierScalingFunction implements OutlierScalingFunctio
sqsum += ti * val * val; // (val - curMu) * (val - curMu);
}
if(tisum <= 0.0 || wsum <= 0.0) {
- logger.warning("MixtureModel Outlier Scaling converged to extreme.");
+ LOG.warning("MixtureModel Outlier Scaling converged to extreme.");
break;
}
double newMu = wsum / tisum;
@@ -186,7 +186,7 @@ public class MixtureModelOutlierScalingFunction implements OutlierScalingFunctio
}
}
if(newSigma <= 0.0 || newAlpha <= 0.0) {
- logger.warning("MixtureModel Outlier Scaling converged to extreme.");
+ LOG.warning("MixtureModel Outlier Scaling converged to extreme.");
break;
}
// logger.debugFine("iter #"+iter+" mu = " + newMu + " sigma = " +
@@ -198,7 +198,7 @@ public class MixtureModelOutlierScalingFunction implements OutlierScalingFunctio
iter++;
if(iter > 100) {
- logger.warning("Max iterations met in mixture model fitting.");
+ LOG.warning("Max iterations met in mixture model fitting.");
break;
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/MultiplicativeInverseScaling.java b/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/MultiplicativeInverseScaling.java
index 1ebf24bb..298a5853 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/MultiplicativeInverseScaling.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/MultiplicativeInverseScaling.java
@@ -23,8 +23,8 @@ package de.lmu.ifi.dbs.elki.utilities.scaling.outlier;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.result.outlier.OutlierResult;
import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
@@ -79,9 +79,9 @@ public class MultiplicativeInverseScaling implements OutlierScalingFunction {
*/
private static double getScaleValue(OutlierResult or) {
double max = Double.MIN_VALUE;
- for(DBIDIter iditer = or.getScores().iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
- double val = or.getScores().get(id);
+ Relation<Double> scores = or.getScores();
+ for(DBIDIter id = scores.iterDBIDs(); id.valid(); id.advance()) {
+ double val = scores.get(id);
double inv = Math.abs(1.0 / val);
if(!Double.isInfinite(inv) && !Double.isNaN(inv)) {
max = Math.max(max, inv);
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/OutlierGammaScaling.java b/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/OutlierGammaScaling.java
index 9facaa11..07e58679 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/OutlierGammaScaling.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/OutlierGammaScaling.java
@@ -23,8 +23,8 @@ package de.lmu.ifi.dbs.elki.utilities.scaling.outlier;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.math.MeanVariance;
import de.lmu.ifi.dbs.elki.math.statistics.distribution.GammaDistribution;
import de.lmu.ifi.dbs.elki.result.outlier.OutlierResult;
@@ -50,7 +50,7 @@ public class OutlierGammaScaling implements OutlierScalingFunction {
* -gammascale.normalize
* </pre>
*/
- public static final OptionID NORMALIZE_ID = OptionID.getOrCreateOptionID("gammascale.normalize", "Regularize scores before using Gamma scaling.");
+ public static final OptionID NORMALIZE_ID = new OptionID("gammascale.normalize", "Regularize scores before using Gamma scaling.");
/**
* Gamma parameter k
@@ -101,9 +101,9 @@ public class OutlierGammaScaling implements OutlierScalingFunction {
public void prepare(OutlierResult or) {
meta = or.getOutlierMeta();
MeanVariance mv = new MeanVariance();
- for(DBIDIter iditer = or.getScores().iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
- double score = or.getScores().get(id);
+ Relation<Double> scores = or.getScores();
+ for(DBIDIter id = scores.iterDBIDs(); id.valid(); id.advance()) {
+ double score = scores.get(id);
score = preScale(score);
if(!Double.isNaN(score) && !Double.isInfinite(score)) {
mv.put(score);
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/OutlierLinearScaling.java b/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/OutlierLinearScaling.java
index e7222b96..8f008176 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/OutlierLinearScaling.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/OutlierLinearScaling.java
@@ -25,8 +25,8 @@ package de.lmu.ifi.dbs.elki.utilities.scaling.outlier;
import java.util.ArrayList;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.math.DoubleMinMax;
import de.lmu.ifi.dbs.elki.math.MeanVariance;
import de.lmu.ifi.dbs.elki.result.outlier.OutlierResult;
@@ -54,7 +54,7 @@ public class OutlierLinearScaling implements OutlierScalingFunction {
* Key: {@code -linearscale.min}
* </p>
*/
- public static final OptionID MIN_ID = OptionID.getOrCreateOptionID("linearscale.min", "Fixed minimum to use in linear scaling.");
+ public static final OptionID MIN_ID = new OptionID("linearscale.min", "Fixed minimum to use in linear scaling.");
/**
* Parameter to specify the maximum value.
@@ -62,7 +62,7 @@ public class OutlierLinearScaling implements OutlierScalingFunction {
* Key: {@code -linearscale.max}
* </p>
*/
- public static final OptionID MAX_ID = OptionID.getOrCreateOptionID("linearscale.max", "Fixed maximum to use in linear scaling.");
+ public static final OptionID MAX_ID = new OptionID("linearscale.max", "Fixed maximum to use in linear scaling.");
/**
* Flag to use the mean as minimum for scaling.
@@ -71,7 +71,7 @@ public class OutlierLinearScaling implements OutlierScalingFunction {
* Key: {@code -linearscale.usemean}
* </p>
*/
- public static final OptionID MEAN_ID = OptionID.getOrCreateOptionID("linearscale.usemean", "Use the mean as minimum for scaling.");
+ public static final OptionID MEAN_ID = new OptionID("linearscale.usemean", "Use the mean as minimum for scaling.");
/**
* Flag to use ignore zeros when computing the min and max.
@@ -80,7 +80,7 @@ public class OutlierLinearScaling implements OutlierScalingFunction {
* Key: {@code -linearscale.ignorezero}
* </p>
*/
- public static final OptionID NOZEROS_ID = OptionID.getOrCreateOptionID("linearscale.ignorezero", "Ignore zero entries when computing the minimum and maximum.");
+ public static final OptionID NOZEROS_ID = new OptionID("linearscale.ignorezero", "Ignore zero entries when computing the minimum and maximum.");
/**
* Field storing the Minimum to use
@@ -148,9 +148,9 @@ public class OutlierLinearScaling implements OutlierScalingFunction {
MeanVariance mv = new MeanVariance();
DoubleMinMax mm = (max == null) ? new DoubleMinMax() : null;
boolean skippedzeros = false;
- for(DBIDIter iditer = or.getScores().iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
- double val = or.getScores().get(id);
+ Relation<Double> scores = or.getScores();
+ for(DBIDIter id = scores.iterDBIDs(); id.valid(); id.advance()) {
+ double val = scores.get(id);
if(nozeros && val == 0.0) {
skippedzeros = true;
continue;
@@ -175,9 +175,9 @@ public class OutlierLinearScaling implements OutlierScalingFunction {
if(min == null || max == null) {
boolean skippedzeros = false;
DoubleMinMax mm = new DoubleMinMax();
- for(DBIDIter iditer = or.getScores().iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
- double val = or.getScores().get(id);
+ Relation<Double> scores = or.getScores();
+ for(DBIDIter id = scores.iterDBIDs(); id.valid(); id.advance()) {
+ double val = scores.get(id);
if(nozeros && val == 0.0) {
skippedzeros = true;
continue;
@@ -239,12 +239,14 @@ public class OutlierLinearScaling implements OutlierScalingFunction {
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- DoubleParameter minP = new DoubleParameter(MIN_ID, true);
+ DoubleParameter minP = new DoubleParameter(MIN_ID);
+ minP.setOptional(true);
if(config.grab(minP)) {
min = minP.getValue();
}
- DoubleParameter maxP = new DoubleParameter(MAX_ID, true);
+ DoubleParameter maxP = new DoubleParameter(MAX_ID);
+ maxP.setOptional(true);
if(config.grab(maxP)) {
max = maxP.getValue();
}
@@ -260,7 +262,7 @@ public class OutlierLinearScaling implements OutlierScalingFunction {
}
// Use-Mean and Minimum value must not be set at the same time!
- ArrayList<Parameter<?, ?>> minmean = new ArrayList<Parameter<?, ?>>();
+ ArrayList<Parameter<?>> minmean = new ArrayList<Parameter<?>>();
minmean.add(minP);
minmean.add(meanF);
GlobalParameterConstraint gpc = new OnlyOneIsAllowedToBeSetGlobalConstraint(minmean);
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/OutlierMinusLogScaling.java b/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/OutlierMinusLogScaling.java
index 09d84eb4..45c6928b 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/OutlierMinusLogScaling.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/OutlierMinusLogScaling.java
@@ -23,8 +23,8 @@ package de.lmu.ifi.dbs.elki.utilities.scaling.outlier;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.math.DoubleMinMax;
import de.lmu.ifi.dbs.elki.result.outlier.OutlierResult;
import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
@@ -78,9 +78,9 @@ public class OutlierMinusLogScaling implements OutlierScalingFunction {
@Override
public void prepare(OutlierResult or) {
DoubleMinMax mm = new DoubleMinMax();
- for(DBIDIter iditer = or.getScores().iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
- double val = or.getScores().get(id);
+ Relation<Double> scores = or.getScores();
+ for(DBIDIter id = scores.iterDBIDs(); id.valid(); id.advance()) {
+ double val = scores.get(id);
if(!Double.isNaN(val) && !Double.isInfinite(val)) {
mm.put(val);
}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/OutlierSqrtScaling.java b/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/OutlierSqrtScaling.java
index 3f564fd1..41a4d721 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/OutlierSqrtScaling.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/OutlierSqrtScaling.java
@@ -23,8 +23,8 @@ package de.lmu.ifi.dbs.elki.utilities.scaling.outlier;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.math.DoubleMinMax;
import de.lmu.ifi.dbs.elki.result.outlier.OutlierResult;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
@@ -48,7 +48,7 @@ public class OutlierSqrtScaling implements OutlierScalingFunction {
* Key: {@code -sqrtscale.min}
* </p>
*/
- public static final OptionID MIN_ID = OptionID.getOrCreateOptionID("sqrtscale.min", "Fixed minimum to use in sqrt scaling.");
+ public static final OptionID MIN_ID = new OptionID("sqrtscale.min", "Fixed minimum to use in sqrt scaling.");
/**
* Parameter to specify the fixed maximum to use.
@@ -56,7 +56,7 @@ public class OutlierSqrtScaling implements OutlierScalingFunction {
* Key: {@code -sqrtscale.max}
* </p>
*/
- public static final OptionID MAX_ID = OptionID.getOrCreateOptionID("sqrtscale.max", "Fixed maximum to use in sqrt scaling.");
+ public static final OptionID MAX_ID = new OptionID("sqrtscale.max", "Fixed maximum to use in sqrt scaling.");
/**
* Field storing the minimum value
@@ -98,9 +98,9 @@ public class OutlierSqrtScaling implements OutlierScalingFunction {
public void prepare(OutlierResult or) {
if(min == null || max == null) {
DoubleMinMax mm = new DoubleMinMax();
- for(DBIDIter iditer = or.getScores().iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
- double val = or.getScores().get(id);
+ Relation<Double> scores = or.getScores();
+ for(DBIDIter id = scores.iterDBIDs(); id.valid(); id.advance()) {
+ double val = scores.get(id);
if(!Double.isNaN(val) && !Double.isInfinite(val)) {
mm.put(val);
}
@@ -140,11 +140,13 @@ public class OutlierSqrtScaling implements OutlierScalingFunction {
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- DoubleParameter minP = new DoubleParameter(MIN_ID, true);
+ DoubleParameter minP = new DoubleParameter(MIN_ID);
+ minP.setOptional(true);
if(config.grab(minP)) {
min = minP.getValue();
}
- DoubleParameter maxP = new DoubleParameter(MAX_ID, true);
+ DoubleParameter maxP = new DoubleParameter(MAX_ID);
+ maxP.setOptional(true);
if(config.grab(maxP)) {
max = maxP.getValue();
}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/RankingPseudoOutlierScaling.java b/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/RankingPseudoOutlierScaling.java
index be7bf766..91f66587 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/RankingPseudoOutlierScaling.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/RankingPseudoOutlierScaling.java
@@ -25,8 +25,8 @@ package de.lmu.ifi.dbs.elki.utilities.scaling.outlier;
import java.util.Arrays;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.result.outlier.InvertedOutlierScoreMeta;
import de.lmu.ifi.dbs.elki.result.outlier.OutlierResult;
import de.lmu.ifi.dbs.elki.utilities.exceptions.AbortException;
@@ -52,17 +52,17 @@ public class RankingPseudoOutlierScaling implements OutlierScalingFunction {
@Override
public void prepare(OutlierResult or) {
// collect all outlier scores
- scores = new double[or.getScores().size()];
+ Relation<Double> oscores = or.getScores();
+ scores = new double[oscores.size()];
int pos = 0;
if(or.getOutlierMeta() instanceof InvertedOutlierScoreMeta) {
inverted = true;
}
- for(DBIDIter iditer = or.getScores().iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
- scores[pos] = or.getScores().get(id);
+ for(DBIDIter iditer = oscores.iterDBIDs(); iditer.valid(); iditer.advance()) {
+ scores[pos] = oscores.get(iditer);
pos++;
}
- if(pos != or.getScores().size()) {
+ if(pos != oscores.size()) {
throw new AbortException("Database size is incorrect!");
}
// sort them
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/SigmoidOutlierScalingFunction.java b/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/SigmoidOutlierScalingFunction.java
index 468079a3..16115e92 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/SigmoidOutlierScalingFunction.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/SigmoidOutlierScalingFunction.java
@@ -26,7 +26,6 @@ package de.lmu.ifi.dbs.elki.utilities.scaling.outlier;
import java.util.BitSet;
import de.lmu.ifi.dbs.elki.database.ids.ArrayDBIDs;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
@@ -46,7 +45,7 @@ public class SigmoidOutlierScalingFunction implements OutlierScalingFunction {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(SigmoidOutlierScalingFunction.class);
+ private static final Logging LOG = Logging.getLogger(SigmoidOutlierScalingFunction.class);
/**
* Sigmoid parameter
@@ -62,9 +61,9 @@ public class SigmoidOutlierScalingFunction implements OutlierScalingFunction {
public void prepare(OutlierResult or) {
// Initial parameters - are these defaults sounds?
MeanVariance mv = new MeanVariance();
- for(DBIDIter iditer = or.getScores().iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
- double val = or.getScores().get(id);
+ Relation<Double> scores = or.getScores();
+ for(DBIDIter id = scores.iterDBIDs(); id.valid(); id.advance()) {
+ double val = scores.get(id);
mv.put(val);
}
double a = 1.0;
@@ -109,13 +108,13 @@ public class SigmoidOutlierScalingFunction implements OutlierScalingFunction {
iter++;
if(iter > 100) {
- logger.warning("Max iterations met in sigmoid fitting.");
+ LOG.warning("Max iterations met in sigmoid fitting.");
break;
}
}
Afinal = a;
Bfinal = b;
- logger.debugFine("A = "+Afinal+" B = "+Bfinal);
+ LOG.debugFine("A = "+Afinal+" B = "+Bfinal);
}
/**
@@ -226,12 +225,12 @@ public class SigmoidOutlierScalingFunction implements OutlierScalingFunction {
stepsize /= 2.0;
}
if(stepsize < minstep) {
- logger.debug("Minstep hit.");
+ LOG.debug("Minstep hit.");
break;
}
}
if(it + 1 >= maxiter) {
- logger.debug("Maximum iterations hit.");
+ LOG.debug("Maximum iterations hit.");
break;
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/SqrtStandardDeviationScaling.java b/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/SqrtStandardDeviationScaling.java
index d3048b1d..6c38c781 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/SqrtStandardDeviationScaling.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/SqrtStandardDeviationScaling.java
@@ -23,8 +23,8 @@ package de.lmu.ifi.dbs.elki.utilities.scaling.outlier;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.math.DoubleMinMax;
import de.lmu.ifi.dbs.elki.math.MathUtil;
import de.lmu.ifi.dbs.elki.math.MeanVariance;
@@ -58,7 +58,7 @@ public class SqrtStandardDeviationScaling implements OutlierScalingFunction {
* Key: {@code -sqrtstddevscale.min}
* </p>
*/
- public static final OptionID MIN_ID = OptionID.getOrCreateOptionID("sqrtstddevscale.min", "Fixed minimum to use in sqrt scaling.");
+ public static final OptionID MIN_ID = new OptionID("sqrtstddevscale.min", "Fixed minimum to use in sqrt scaling.");
/**
* Parameter to specify a fixed mean to use.
@@ -66,7 +66,7 @@ public class SqrtStandardDeviationScaling implements OutlierScalingFunction {
* Key: {@code -sqrtstddevscale.mean}
* </p>
*/
- public static final OptionID MEAN_ID = OptionID.getOrCreateOptionID("sqrtstddevscale.mean", "Fixed mean to use in standard deviation scaling.");
+ public static final OptionID MEAN_ID = new OptionID("sqrtstddevscale.mean", "Fixed mean to use in standard deviation scaling.");
/**
* Parameter to specify the lambda value
@@ -74,7 +74,7 @@ public class SqrtStandardDeviationScaling implements OutlierScalingFunction {
* Key: {@code -sqrtstddevscale.lambda}
* </p>
*/
- public static final OptionID LAMBDA_ID = OptionID.getOrCreateOptionID("sqrtstddevscale.lambda", "Significance level to use for error function.");
+ public static final OptionID LAMBDA_ID = new OptionID("sqrtstddevscale.lambda", "Significance level to use for error function.");
/**
* Field storing the lambda value
@@ -127,9 +127,9 @@ public class SqrtStandardDeviationScaling implements OutlierScalingFunction {
public void prepare(OutlierResult or) {
if(min == null) {
DoubleMinMax mm = new DoubleMinMax();
- for(DBIDIter iditer = or.getScores().iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
- double val = or.getScores().get(id);
+ Relation<Double> scores = or.getScores();
+ for(DBIDIter id = scores.iterDBIDs(); id.valid(); id.advance()) {
+ double val = scores.get(id);
if(!Double.isNaN(val) && !Double.isInfinite(val)) {
mm.put(val);
}
@@ -138,9 +138,9 @@ public class SqrtStandardDeviationScaling implements OutlierScalingFunction {
}
if(mean == null) {
MeanVariance mv = new MeanVariance();
- for(DBIDIter iditer = or.getScores().iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
- double val = or.getScores().get(id);
+ Relation<Double> scores = or.getScores();
+ for(DBIDIter id = scores.iterDBIDs(); id.valid(); id.advance()) {
+ double val = scores.get(id);
val = (val <= min) ? 0 : Math.sqrt(val - min);
mv.put(val);
}
@@ -150,9 +150,9 @@ public class SqrtStandardDeviationScaling implements OutlierScalingFunction {
else {
double sqsum = 0;
int cnt = 0;
- for(DBIDIter iditer = or.getScores().iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
- double val = or.getScores().get(id);
+ Relation<Double> scores = or.getScores();
+ for(DBIDIter id = scores.iterDBIDs(); id.valid(); id.advance()) {
+ double val = scores.get(id);
val = (val <= min) ? 0 : Math.sqrt(val - min);
sqsum += (val - mean) * (val - mean);
cnt += 1;
@@ -188,12 +188,14 @@ public class SqrtStandardDeviationScaling implements OutlierScalingFunction {
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- DoubleParameter minP = new DoubleParameter(MIN_ID, true);
+ DoubleParameter minP = new DoubleParameter(MIN_ID);
+ minP.setOptional(true);
if(config.grab(minP)) {
min = minP.getValue();
}
- DoubleParameter meanP = new DoubleParameter(MEAN_ID, true);
+ DoubleParameter meanP = new DoubleParameter(MEAN_ID);
+ meanP.setOptional(true);
if(config.grab(meanP)) {
mean = meanP.getValue();
}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/StandardDeviationScaling.java b/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/StandardDeviationScaling.java
index 7d76a69f..c8ec0905 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/StandardDeviationScaling.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/StandardDeviationScaling.java
@@ -23,9 +23,10 @@ package de.lmu.ifi.dbs.elki.utilities.scaling.outlier;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.math.MathUtil;
+import de.lmu.ifi.dbs.elki.math.Mean;
import de.lmu.ifi.dbs.elki.math.MeanVariance;
import de.lmu.ifi.dbs.elki.math.statistics.distribution.NormalDistribution;
import de.lmu.ifi.dbs.elki.result.outlier.OutlierResult;
@@ -54,7 +55,7 @@ public class StandardDeviationScaling implements OutlierScalingFunction {
* Key: {@code -stddevscale.mean}
* </p>
*/
- public static final OptionID MEAN_ID = OptionID.getOrCreateOptionID("stddevscale.mean", "Fixed mean to use in standard deviation scaling.");
+ public static final OptionID MEAN_ID = new OptionID("stddevscale.mean", "Fixed mean to use in standard deviation scaling.");
/**
* Parameter to specify the lambda value
@@ -62,7 +63,7 @@ public class StandardDeviationScaling implements OutlierScalingFunction {
* Key: {@code -stddevscale.lambda}
* </p>
*/
- public static final OptionID LAMBDA_ID = OptionID.getOrCreateOptionID("stddevscale.lambda", "Significance level to use for error function.");
+ public static final OptionID LAMBDA_ID = new OptionID("stddevscale.lambda", "Significance level to use for error function.");
/**
* Field storing the fixed mean to use
@@ -116,9 +117,9 @@ public class StandardDeviationScaling implements OutlierScalingFunction {
public void prepare(OutlierResult or) {
if(fixedmean == null) {
MeanVariance mv = new MeanVariance();
- for(DBIDIter iditer = or.getScores().iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
- double val = or.getScores().get(id);
+ Relation<Double> scores = or.getScores();
+ for(DBIDIter id = scores.iterDBIDs(); id.valid(); id.advance()) {
+ double val = scores.get(id);
if(!Double.isNaN(val) && !Double.isInfinite(val)) {
mv.put(val);
}
@@ -131,17 +132,15 @@ public class StandardDeviationScaling implements OutlierScalingFunction {
}
else {
mean = fixedmean;
- double sqsum = 0;
- int cnt = 0;
- for(DBIDIter iditer = or.getScores().iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
- double val = or.getScores().get(id);
+ Mean sqsum = new Mean();
+ Relation<Double> scores = or.getScores();
+ for(DBIDIter id = scores.iterDBIDs(); id.valid(); id.advance()) {
+ double val = scores.get(id);
if(!Double.isNaN(val) && !Double.isInfinite(val)) {
- sqsum += (val - mean) * (val - mean);
- cnt += 1;
+ sqsum.put((val - mean) * (val - mean));
}
}
- factor = lambda * Math.sqrt(sqsum / cnt) * MathUtil.SQRT2;
+ factor = lambda * Math.sqrt(sqsum.getMean()) * MathUtil.SQRT2;
if (factor == 0.0) {
factor = Double.MIN_NORMAL;
}
@@ -173,7 +172,8 @@ public class StandardDeviationScaling implements OutlierScalingFunction {
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- DoubleParameter meanP = new DoubleParameter(MEAN_ID, true);
+ DoubleParameter meanP = new DoubleParameter(MEAN_ID);
+ meanP.setOptional(true);
if(config.grab(meanP)) {
fixedmean = meanP.getValue();
}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/TopKOutlierScaling.java b/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/TopKOutlierScaling.java
index c922ab34..f8609f3b 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/TopKOutlierScaling.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/scaling/outlier/TopKOutlierScaling.java
@@ -45,7 +45,7 @@ public class TopKOutlierScaling implements OutlierScalingFunction {
* Key: {@code -topk.k}
* </p>
*/
- public static final OptionID K_ID = OptionID.getOrCreateOptionID("topk.k", "Number of outliers to keep.");
+ public static final OptionID K_ID = new OptionID("topk.k", "Number of outliers to keep.");
/**
* Parameter to specify the lambda value
@@ -53,7 +53,7 @@ public class TopKOutlierScaling implements OutlierScalingFunction {
* Key: {@code -topk.binary}
* </p>
*/
- public static final OptionID BINARY_ID = OptionID.getOrCreateOptionID("topk.binary", "Make the top k a binary scaling.");
+ public static final OptionID BINARY_ID = new OptionID("topk.binary", "Make the top k a binary scaling.");
/**
* Number of outliers to keep.
@@ -99,7 +99,7 @@ public class TopKOutlierScaling implements OutlierScalingFunction {
}
DBIDIter order = or.getOrdering().iter(or.getOrdering().getDBIDs()).iter();
for(int i = 0; i < k && order.valid(); i++, order.advance()) {
- cutoff = or.getScores().get(order.getDBID());
+ cutoff = or.getScores().get(order);
}
max = or.getOutlierMeta().getActualMaximum();
ground = or.getOutlierMeta().getTheoreticalBaseline();
@@ -165,14 +165,15 @@ public class TopKOutlierScaling implements OutlierScalingFunction {
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- IntParameter kP = new IntParameter(K_ID, new GreaterConstraint(1));
+ IntParameter kP = new IntParameter(K_ID);
+ kP.addConstraint(new GreaterConstraint(1));
if(config.grab(kP)) {
- k = kP.getValue();
+ k = kP.intValue();
}
Flag binaryF = new Flag(BINARY_ID);
if(config.grab(binaryF)) {
- binary = binaryF.getValue();
+ binary = binaryF.isTrue();
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/xml/HTMLUtil.java b/src/de/lmu/ifi/dbs/elki/utilities/xml/HTMLUtil.java
index 7e433525..6c65c7f9 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/xml/HTMLUtil.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/xml/HTMLUtil.java
@@ -254,9 +254,8 @@ public final class HTMLUtil {
* @param htmldoc Document to output
* @param out Stream to write to
* @throws IOException thrown on IO errors
- * @throws Error thrown on other errors, such as HTML transformation
*/
- public static void writeXHTML(Document htmldoc, OutputStream out) throws IOException, Error {
+ public static void writeXHTML(Document htmldoc, OutputStream out) throws IOException {
javax.xml.transform.Result result = new StreamResult(out);
// Use a transformer for pretty printing
Transformer xformer;
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/ExportVisualizations.java b/src/de/lmu/ifi/dbs/elki/visualization/ExportVisualizations.java
index da0a5c85..f0a26325 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/ExportVisualizations.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/ExportVisualizations.java
@@ -48,20 +48,21 @@ import de.lmu.ifi.dbs.elki.visualization.gui.overview.PlotItem;
import de.lmu.ifi.dbs.elki.visualization.projector.Projector;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
-import de.lmu.ifi.dbs.elki.visualization.visualizers.VisualizerUtil;
/**
* Class that automatically generates all visualizations and exports them into
* SVG files.
*
* @author Erich Schubert
+ *
+ * @apiviz.composedOf VisualizerParameterizer
*/
// TODO: make more parameterizable, wrt. what to skip
public class ExportVisualizations implements ResultHandler {
/**
* Get a logger for this class.
*/
- protected final static Logging logger = Logging.getLogger(ExportVisualizations.class);
+ private static final Logging LOG = Logging.getLogger(ExportVisualizations.class);
/**
* Parameter to specify the canvas ratio
@@ -72,7 +73,7 @@ public class ExportVisualizations implements ResultHandler {
* Default value: 1.33
* </p>
*/
- public static final OptionID RATIO_ID = OptionID.getOrCreateOptionID("vis.ratio", "The width/heigh ratio of the output.");
+ public static final OptionID RATIO_ID = new OptionID("vis.ratio", "The width/heigh ratio of the output.");
/**
* Parameter to specify the output folder
@@ -80,7 +81,7 @@ public class ExportVisualizations implements ResultHandler {
* Key: {@code -vis.output}
* </p>
*/
- public static final OptionID FOLDER_ID = OptionID.getOrCreateOptionID("vis.output", "The output folder.");
+ public static final OptionID FOLDER_ID = new OptionID("vis.output", "The output folder.");
/**
* Output folder
@@ -128,44 +129,44 @@ public class ExportVisualizations implements ResultHandler {
@Override
public void processNewResult(HierarchicalResult baseResult, Result newResult) {
- if(output.isFile()) {
+ if (output.isFile()) {
throw new AbortException("Output folder cannot be an existing file.");
}
- if(!output.exists()) {
- if(!output.mkdirs()) {
+ if (!output.exists()) {
+ if (!output.mkdirs()) {
throw new AbortException("Could not create output directory.");
}
}
- if(this.baseResult != baseResult) {
+ if (this.baseResult != baseResult) {
this.baseResult = baseResult;
context = null;
counter = 0;
- logger.verbose("Note: Reusing visualization exporter for more than one result is untested.");
+ LOG.verbose("Note: Reusing visualization exporter for more than one result is untested.");
}
- if(context == null) {
+ if (context == null) {
context = manager.newContext(baseResult);
}
// Projected visualizations
ArrayList<Projector> projectors = ResultUtil.filterResults(baseResult, Projector.class);
- for(Projector proj : projectors) {
+ for (Projector proj : projectors) {
// TODO: allow selecting individual projections only.
Collection<PlotItem> items = proj.arrange();
- for(PlotItem item : items) {
+ for (PlotItem item : items) {
processItem(item);
}
}
ResultHierarchy hier = baseResult.getHierarchy();
ArrayList<VisualizationTask> tasks = ResultUtil.filterResults(baseResult, VisualizationTask.class);
- for(VisualizationTask task : tasks) {
+ for (VisualizationTask task : tasks) {
boolean isprojected = false;
- for(Result parent : hier.getParents(task)) {
- if(parent instanceof Projector) {
+ for (Result parent : hier.getParents(task)) {
+ if (parent instanceof Projector) {
isprojected = true;
break;
}
}
- if(isprojected) {
+ if (isprojected) {
continue;
}
PlotItem pi = new PlotItem(ratio, 1.0, null);
@@ -178,11 +179,11 @@ public class ExportVisualizations implements ResultHandler {
final double height = 1;
final double width = ratio * height;
// Descend into subitems
- for(Iterator<PlotItem> iter = item.subitems.iterator(); iter.hasNext();) {
+ for (Iterator<PlotItem> iter = item.subitems.iterator(); iter.hasNext();) {
PlotItem subitem = iter.next();
processItem(subitem);
}
- if(item.taskSize() <= 0) {
+ if (item.taskSize() <= 0) {
return;
}
item.sort();
@@ -193,44 +194,29 @@ public class ExportVisualizations implements ResultHandler {
svgp.getRoot().setAttribute(SVGConstants.SVG_VIEW_BOX_ATTRIBUTE, "0 0 " + width + " " + height);
ArrayList<Visualization> layers = new ArrayList<Visualization>();
- for(Iterator<VisualizationTask> iter = item.tasks.iterator(); iter.hasNext();) {
+ for (Iterator<VisualizationTask> iter = item.tasks.iterator(); iter.hasNext();) {
VisualizationTask task = iter.next();
- {
- Boolean dis = task.getGenerics(VisualizationTask.META_NODETAIL, Boolean.class);
- if(dis != null && dis == true) {
- continue;
- }
- }
- {
- Boolean dis = task.getGenerics(VisualizationTask.META_NOEXPORT, Boolean.class);
- if(dis != null && dis == true) {
- continue;
- }
- }
- if(!VisualizerUtil.isVisible(task)) {
+ if (task.nodetail || task.noexport || !task.visible) {
continue;
}
try {
Visualization v = task.getFactory().makeVisualization(task.clone(svgp, context, item.proj, width, height));
layers.add(v);
- }
- catch(Exception e) {
- if(Logging.getLogger(task.getFactory().getClass()).isDebugging()) {
+ } catch (Exception e) {
+ if (Logging.getLogger(task.getFactory().getClass()).isDebugging()) {
LoggingUtil.exception("Visualization failed.", e);
- }
- else {
+ } else {
LoggingUtil.warning("Visualizer " + task.getFactory().getClass().getName() + " failed - enable debugging to see details.");
}
}
}
- if(layers.size() <= 0) {
+ if (layers.size() <= 0) {
return;
}
- for(Visualization layer : layers) {
- if(layer.getLayer() != null) {
+ for (Visualization layer : layers) {
+ if (layer.getLayer() != null) {
svgp.getRoot().appendChild(layer.getLayer());
- }
- else {
+ } else {
LoggingUtil.warning("NULL layer seen.");
}
}
@@ -240,11 +226,10 @@ public class ExportVisualizations implements ResultHandler {
File outname = new File(output, "plot-" + counter + ".svg");
try {
svgp.saveAsSVG(outname);
+ } catch (Exception e) {
+ LOG.warning("Export of visualization failed.", e);
}
- catch(Exception e) {
- logger.warning("Export of visualization failed.", e);
- }
- for(Visualization layer : layers) {
+ for (Visualization layer : layers) {
layer.destroy();
}
counter++;
@@ -277,13 +262,14 @@ public class ExportVisualizations implements ResultHandler {
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
FileParameter outputP = new FileParameter(FOLDER_ID, FileType.OUTPUT_FILE);
- if(config.grab(outputP)) {
+ if (config.grab(outputP)) {
output = outputP.getValue();
}
- DoubleParameter ratioP = new DoubleParameter(RATIO_ID, new GreaterConstraint(0.0), 1.33);
- if(config.grab(ratioP)) {
- ratio = ratioP.getValue();
+ DoubleParameter ratioP = new DoubleParameter(RATIO_ID, 1.33);
+ ratioP.addConstraint(new GreaterConstraint(0.0));
+ if (config.grab(ratioP)) {
+ ratio = ratioP.doubleValue();
}
manager = config.tryInstantiate(VisualizerParameterizer.class);
@@ -294,4 +280,4 @@ public class ExportVisualizations implements ResultHandler {
return new ExportVisualizations(output, manager, ratio);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/VisualizationTask.java b/src/de/lmu/ifi/dbs/elki/visualization/VisualizationTask.java
index 39d6e359..050c7b63 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/VisualizationTask.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/VisualizationTask.java
@@ -25,7 +25,7 @@ package de.lmu.ifi.dbs.elki.visualization;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.result.Result;
-import de.lmu.ifi.dbs.elki.utilities.datastructures.AnyMap;
+import de.lmu.ifi.dbs.elki.utilities.exceptions.AbortException;
import de.lmu.ifi.dbs.elki.visualization.projections.Projection;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
import de.lmu.ifi.dbs.elki.visualization.visualizers.VisFactory;
@@ -41,12 +41,7 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.VisFactory;
* @apiviz.has VisFactory
* @apiviz.has Projection oneway - 0:1
*/
-public class VisualizationTask extends AnyMap<String> implements Cloneable, Result, Comparable<VisualizationTask> {
- /**
- * Serial number
- */
- private static final long serialVersionUID = 1L;
-
+public class VisualizationTask implements Cloneable, Result, Comparable<VisualizationTask> {
/**
* Constant for using thumbnail
*/
@@ -60,83 +55,81 @@ public class VisualizationTask extends AnyMap<String> implements Cloneable, Resu
/**
* Meta data key: Level for visualizer ordering
*
- * Returns an integer indicating the "temporal position" of this Visualizer.
- * It is intended to impose an ordering on the execution of Visualizers as a
+ * Returns an integer indicating the "height" of this Visualizer. It is
+ * intended to impose an ordering on the execution of Visualizers as a
* Visualizer may depend on another Visualizer running earlier. <br>
* Lower numbers should result in a earlier use of this Visualizer, while
* higher numbers should result in a later use. If more Visualizers have the
* same level, no ordering is guaranteed. <br>
* Note that this value is only a recommendation, as it is totally up to the
* framework to ignore it.
- *
- * Type: Integer
*/
- public final static String META_LEVEL = "level";
+ public int level = 0;
/**
- * Flag to control visibility. Type: Boolean
+ * Flag to control visibility.
*/
- public final static String META_VISIBLE = "visible";
+ public boolean visible = true;
/**
- * Flag to signal there is no thumbnail needed. Type: Boolean
+ * Flag to signal there is no thumbnail needed.
*/
- public final static String META_NOTHUMB = "no-thumbnail";
+ public boolean thumbnail = true;
/**
* Mark as not having a (sensible) detail view.
*/
- public static final String META_NODETAIL = "no-detail";
+ public boolean nodetail = false;
/**
- * Flag to signal the visualizer should not be exported. Type: Boolean
+ * Flag to signal the visualizer should not be exported.
*/
- public final static String META_NOEXPORT = "no-export";
+ public boolean noexport = false;
/**
- * Flag to signal the visualizer should not be embed. Type: Boolean
+ * Flag to signal the visualizer should not be embedded.
*/
- public final static String META_NOEMBED = "no-embed";
+ public boolean noembed = false;
/**
- * Flag to signal default visibility of a visualizer. Type: Boolean
+ * Flag to signal default visibility of a visualizer.
*/
- public static final String META_VISIBLE_DEFAULT = "visible-default";
+ public boolean default_visibility = true;
/**
- * Flag to mark the visualizer as tool. Type: Boolean
+ * Flag to mark the visualizer as tool.
*/
- public static final String META_TOOL = "tool";
+ public boolean tool = false;
/**
* Indicate whether this task has options.
*/
- public static final String META_HAS_OPTIONS = "has-options";
+ public boolean hasoptions = false;
/**
* Background layer
*/
- public final static int LEVEL_BACKGROUND = 0;
+ public static final int LEVEL_BACKGROUND = 0;
/**
* Data layer
*/
- public final static int LEVEL_DATA = 100;
+ public static final int LEVEL_DATA = 100;
/**
* Static plot layer
*/
- public final static int LEVEL_STATIC = 200;
+ public static final int LEVEL_STATIC = 200;
/**
* Passive foreground layer
*/
- public final static int LEVEL_FOREGROUND = 300;
+ public static final int LEVEL_FOREGROUND = 300;
/**
* Active foreground layer (interactive elements)
*/
- public final static int LEVEL_INTERACTIVE = 1000;
+ public static final int LEVEL_INTERACTIVE = 1000;
/**
* Name
@@ -184,6 +177,11 @@ public class VisualizationTask extends AnyMap<String> implements Cloneable, Resu
public double height;
/**
+ * Thumbnail size
+ */
+ public int thumbsize;
+
+ /**
* Visualization task.
*
* @param name Name
@@ -270,18 +268,23 @@ public class VisualizationTask extends AnyMap<String> implements Cloneable, Resu
return height;
}
+ /**
+ * Init the default visibility of a task.
+ *
+ * @param vis Visibility.
+ */
+ public void initDefaultVisibility(boolean vis) {
+ visible = vis;
+ default_visibility = vis;
+ }
+
@Override
public VisualizationTask clone() {
- VisualizationTask obj = (VisualizationTask) super.clone();
- obj.name = name;
- obj.context = context;
- obj.result = result;
- obj.proj = proj;
- obj.factory = factory;
- obj.svgp = svgp;
- obj.width = width;
- obj.height = height;
- return obj;
+ try {
+ return (VisualizationTask) super.clone();
+ } catch (CloneNotSupportedException e) {
+ throw new AbortException("Cloneable interface was removed.", e);
+ }
}
/**
@@ -330,15 +333,13 @@ public class VisualizationTask extends AnyMap<String> implements Cloneable, Resu
@Override
public int compareTo(VisualizationTask other) {
// sort by levels first
- Integer level1 = this.get(VisualizationTask.META_LEVEL, Integer.class);
- Integer level2 = other.get(VisualizationTask.META_LEVEL, Integer.class);
- if(level1 != null && level2 != null && level1 != level2) {
- return level1 - level2;
+ if (this.level != other.level) {
+ return this.level - other.level;
}
// sort by name otherwise.
String name1 = this.getShortName();
String name2 = other.getShortName();
- if(name1 != null && name2 != null && name1 != name2) {
+ if (name1 != null && name2 != null && name1 != name2) {
return name1.compareTo(name2);
}
return 0;
@@ -346,13 +347,13 @@ public class VisualizationTask extends AnyMap<String> implements Cloneable, Resu
@Override
public String toString() {
- StringBuffer buf = new StringBuffer();
- buf.append("VisTask: ").append(factory.getClass().getName()).append(" ");
- if(result != null) {
- buf.append("Result: ").append(result.getLongName()).append(" ");
+ StringBuilder buf = new StringBuilder();
+ buf.append("VisTask: ").append(factory.getClass().getName()).append(' ');
+ if (result != null) {
+ buf.append("Result: ").append(result.getLongName()).append(' ');
}
- if(proj != null) {
- buf.append("Proj: ").append(proj.toString()).append(" ");
+ if (proj != null) {
+ buf.append("Proj: ").append(proj.toString()).append(' ');
}
buf.append(super.toString());
return buf.toString();
@@ -369,4 +370,4 @@ public class VisualizationTask extends AnyMap<String> implements Cloneable, Resu
// Also don't inherit equals based on list contents!
return (this == o);
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/VisualizerContext.java b/src/de/lmu/ifi/dbs/elki/visualization/VisualizerContext.java
index c4e6c839..417d6d07 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/VisualizerContext.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/VisualizerContext.java
@@ -49,11 +49,9 @@ import de.lmu.ifi.dbs.elki.result.ResultUtil;
import de.lmu.ifi.dbs.elki.result.SelectionResult;
import de.lmu.ifi.dbs.elki.visualization.projector.ProjectorFactory;
import de.lmu.ifi.dbs.elki.visualization.style.ClusterStylingPolicy;
-import de.lmu.ifi.dbs.elki.visualization.style.PropertiesBasedStyleLibrary;
import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary;
import de.lmu.ifi.dbs.elki.visualization.style.StyleResult;
import de.lmu.ifi.dbs.elki.visualization.visualizers.VisFactory;
-import de.lmu.ifi.dbs.elki.visualization.visualizers.VisualizerUtil;
/**
* Map to store context information for the visualizer. This can be any data
@@ -76,7 +74,7 @@ public class VisualizerContext implements DataStoreListener, Result {
/**
* Logger.
*/
- private static final Logging logger = Logging.getLogger(VisualizerContext.class);
+ private static final Logging LOG = Logging.getLogger(VisualizerContext.class);
/**
* The full result object
@@ -89,11 +87,6 @@ public class VisualizerContext implements DataStoreListener, Result {
private EventListenerList listenerList = new EventListenerList();
/**
- * The style library of this context
- */
- private StyleLibrary stylelib;
-
- /**
* Projectors to use
*/
private Collection<ProjectorFactory> projectors;
@@ -117,14 +110,12 @@ public class VisualizerContext implements DataStoreListener, Result {
* Constructor. We currently require a Database and a Result.
*
* @param result Result
- * @param stylelib Style library
* @param projectors Projectors to use
* @param factories Visualizer Factories to use
*/
public VisualizerContext(HierarchicalResult result, StyleLibrary stylelib, Collection<ProjectorFactory> projectors, Collection<VisFactory> factories) {
super();
this.result = result;
- this.stylelib = stylelib;
this.projectors = projectors;
this.factories = factories;
@@ -133,16 +124,16 @@ public class VisualizerContext implements DataStoreListener, Result {
final Database db = ResultUtil.findDatabase(result);
ResultUtil.ensureClusteringResult(db, result);
this.selection = ResultUtil.ensureSelectionResult(db);
- for(Relation<?> rel : ResultUtil.getRelations(result)) {
+ for (Relation<?> rel : ResultUtil.getRelations(result)) {
ResultUtil.getSamplingResult(rel);
// FIXME: this is a really ugly workaround. :-(
- if(TypeUtil.NUMBER_VECTOR_FIELD.isAssignableFromType(rel.getDataTypeInformation())) {
+ if (TypeUtil.NUMBER_VECTOR_FIELD.isAssignableFromType(rel.getDataTypeInformation())) {
@SuppressWarnings("unchecked")
- Relation<? extends NumberVector<?, ?>> vrel = (Relation<? extends NumberVector<?, ?>>) rel;
+ Relation<? extends NumberVector<?>> vrel = (Relation<? extends NumberVector<?>>) rel;
ResultUtil.getScalesResult(vrel);
}
}
- getStyleResult();
+ makeStyleResult(stylelib);
// result.getHierarchy().add(result, this);
@@ -151,7 +142,8 @@ public class VisualizerContext implements DataStoreListener, Result {
// For proxying events.
ResultUtil.findDatabase(result).addDataStoreListener(this);
- // Add a result listener. Don't expose these methods to avoid inappropriate use.
+ // Add a result listener. Don't expose these methods to avoid inappropriate
+ // use.
addResultListener(new ResultListener() {
@Override
public void resultAdded(Result child, Result parent) {
@@ -171,6 +163,24 @@ public class VisualizerContext implements DataStoreListener, Result {
}
/**
+ * Generate a new style result for the given style library.
+ *
+ * @param stylelib Style library
+ */
+ protected void makeStyleResult(StyleLibrary stylelib) {
+ styleresult = new StyleResult();
+ styleresult.setStyleLibrary(stylelib);
+ List<Clustering<? extends Model>> clusterings = ResultUtil.getClusteringResults(result);
+ if (clusterings.size() > 0) {
+ styleresult.setStylingPolicy(new ClusterStylingPolicy(clusterings.get(0), stylelib));
+ } else {
+ Clustering<Model> c = generateDefaultClustering();
+ styleresult.setStylingPolicy(new ClusterStylingPolicy(c, stylelib));
+ }
+ result.getHierarchy().add(result, styleresult);
+ }
+
+ /**
* Get the full result object
*
* @return result object
@@ -189,36 +199,11 @@ public class VisualizerContext implements DataStoreListener, Result {
}
/**
- * Get the style library
- *
- * @return style library
- */
- public StyleLibrary getStyleLibrary() {
- if(stylelib == null) {
- stylelib = new PropertiesBasedStyleLibrary();
- }
- return stylelib;
- }
-
- /**
* Get the style result.
*
* @return Style result
*/
public StyleResult getStyleResult() {
- if(styleresult == null) {
- styleresult = new StyleResult();
- List<Clustering<? extends Model>> clusterings = ResultUtil.getClusteringResults(result);
- if(clusterings.size() > 0) {
- styleresult.setStylingPolicy(new ClusterStylingPolicy(clusterings.get(0), stylelib));
- result.getHierarchy().add(result, styleresult);
- return styleresult;
- }
- Clustering<Model> c = generateDefaultClustering();
- styleresult.setStylingPolicy(new ClusterStylingPolicy(c, stylelib));
- result.getHierarchy().add(result, styleresult);
- return styleresult;
- }
return styleresult;
}
@@ -234,8 +219,7 @@ public class VisualizerContext implements DataStoreListener, Result {
// Try to cluster by labels
ByLabelHierarchicalClustering split = new ByLabelHierarchicalClustering();
c = split.run(db);
- }
- catch(NoSupportedDataTypeException e) {
+ } catch (NoSupportedDataTypeException e) {
// Put everything into one
c = new TrivialAllInOne().run(db);
}
@@ -264,21 +248,6 @@ public class VisualizerContext implements DataStoreListener, Result {
}
/**
- * Change a visualizers visibility.
- *
- * When a Tool visualizer is made visible, other tools are hidden.
- *
- * @param task Visualization task
- * @param visibility new visibility
- *
- * @deprecated Use {@link VisualizerUtil#setVisible}
- */
- @Deprecated
- public void setVisualizationVisibility(VisualizationTask task, boolean visibility) {
- VisualizerUtil.setVisible(this, task, visibility);
- }
-
- /**
* Adds a listener for the <code>DataStoreEvent</code> posted after the
* content changes.
*
@@ -304,7 +273,7 @@ public class VisualizerContext implements DataStoreListener, Result {
*/
@Override
public void contentChanged(DataStoreEvent e) {
- for(DataStoreListener listener : listenerList.getListeners(DataStoreListener.class)) {
+ for (DataStoreListener listener : listenerList.getListeners(DataStoreListener.class)) {
listener.contentChanged(e);
}
}
@@ -316,21 +285,19 @@ public class VisualizerContext implements DataStoreListener, Result {
* @param newResult Newly added Result
*/
private void processNewResult(HierarchicalResult baseResult, Result newResult) {
- for(ProjectorFactory p : projectors) {
+ for (ProjectorFactory p : projectors) {
try {
p.processNewResult(baseResult, newResult);
- }
- catch(Throwable e) {
- logger.warning("ProjectorFactory " + p.getClass().getCanonicalName() + " failed:", e);
+ } catch (Throwable e) {
+ LOG.warning("ProjectorFactory " + p.getClass().getCanonicalName() + " failed:", e);
}
}
// Collect all visualizers.
- for(VisFactory f : factories) {
+ for (VisFactory f : factories) {
try {
f.processNewResult(baseResult, newResult);
- }
- catch(Throwable e) {
- logger.warning("VisFactory " + f.getClass().getCanonicalName() + " failed:", e);
+ } catch (Throwable e) {
+ LOG.warning("VisFactory " + f.getClass().getCanonicalName() + " failed:", e);
}
}
}
@@ -362,4 +329,4 @@ public class VisualizerContext implements DataStoreListener, Result {
public String getShortName() {
return "vis-context";
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/VisualizerParameterizer.java b/src/de/lmu/ifi/dbs/elki/visualization/VisualizerParameterizer.java
index 36bbb823..1d1a7569 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/VisualizerParameterizer.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/VisualizerParameterizer.java
@@ -27,7 +27,6 @@ import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
-import java.util.Random;
import java.util.regex.Pattern;
import de.lmu.ifi.dbs.elki.algorithm.AbstractDistanceBasedAlgorithm;
@@ -43,6 +42,7 @@ import de.lmu.ifi.dbs.elki.result.SamplingResult;
import de.lmu.ifi.dbs.elki.result.SettingsResult;
import de.lmu.ifi.dbs.elki.utilities.ClassGenericsUtil;
import de.lmu.ifi.dbs.elki.utilities.InspectionUtil;
+import de.lmu.ifi.dbs.elki.utilities.RandomFactory;
import de.lmu.ifi.dbs.elki.utilities.exceptions.AbortException;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
@@ -75,7 +75,7 @@ public class VisualizerParameterizer implements Parameterizable {
/**
* Get a logger for this class.
*/
- protected final static Logging logger = Logging.getLogger(VisualizerParameterizer.class);
+ private static final Logging LOG = Logging.getLogger(VisualizerParameterizer.class);
/**
* Parameter to get the style properties file.
@@ -86,12 +86,12 @@ public class VisualizerParameterizer implements Parameterizable {
* Default: default properties file
* </p>
*/
- public final static OptionID STYLELIB_ID = OptionID.getOrCreateOptionID("visualizer.stylesheet", "Style properties file to use");
+ public static final OptionID STYLELIB_ID = new OptionID("visualizer.stylesheet", "Style properties file to use");
/**
* Default pattern for visualizer enabling.
*/
- public final static String DEFAULT_ENABLEVIS = "^" + Pattern.quote(VisualizerParameterizer.class.getPackage().getName()) + "\\..*";
+ public static final String DEFAULT_ENABLEVIS = "^" + Pattern.quote(VisualizerParameterizer.class.getPackage().getName()) + "\\..*";
/**
* Parameter to enable visualizers
@@ -102,7 +102,7 @@ public class VisualizerParameterizer implements Parameterizable {
* Default: ELKI core
* </p>
*/
- public final static OptionID ENABLEVIS_ID = OptionID.getOrCreateOptionID("vis.enable", "Visualizers to enable by default.");
+ public static final OptionID ENABLEVIS_ID = new OptionID("vis.enable", "Visualizers to enable by default.");
/**
* Parameter to set the sampling level
@@ -111,7 +111,7 @@ public class VisualizerParameterizer implements Parameterizable {
* Key: -vis.sampling
* </p>
*/
- public final static OptionID SAMPLING_ID = OptionID.getOrCreateOptionID("vis.sampling", "Maximum number of objects to visualize by default (for performance reasons).");
+ public static final OptionID SAMPLING_ID = new OptionID("vis.sampling", "Maximum number of objects to visualize by default (for performance reasons).");
/**
* Style library to use.
@@ -138,7 +138,7 @@ public class VisualizerParameterizer implements Parameterizable {
*
* FIXME: make parameterizable.
*/
- private long seed = new Random().nextLong();
+ private RandomFactory rnd = RandomFactory.DEFAULT;
/**
* Constructor.
@@ -164,16 +164,16 @@ public class VisualizerParameterizer implements Parameterizable {
* @return New context
*/
public VisualizerContext newContext(HierarchicalResult result) {
- if(samplesize > 0) {
+ if (samplesize > 0) {
Collection<Relation<?>> rels = ResultUtil.filterResults(result, Relation.class);
- for(Relation<?> rel : rels) {
- if(!ResultUtil.filterResults(rel, SamplingResult.class).isEmpty()) {
+ for (Relation<?> rel : rels) {
+ if (!ResultUtil.filterResults(rel, SamplingResult.class).isEmpty()) {
continue;
}
int size = rel.size();
- if(size > samplesize) {
+ if (size > samplesize) {
SamplingResult sample = new SamplingResult(rel);
- sample.setSample(DBIDUtil.randomSample(sample.getSample(), samplesize, seed));
+ sample.setSample(DBIDUtil.randomSample(sample.getSample(), samplesize, rnd));
ResultUtil.addChildResult(rel, sample);
}
}
@@ -190,54 +190,54 @@ public class VisualizerParameterizer implements Parameterizable {
* @return generated title
*/
public static String getTitle(Database db, Result result) {
- List<Pair<Object, Parameter<?, ?>>> settings = new ArrayList<Pair<Object, Parameter<?, ?>>>();
- for(SettingsResult sr : ResultUtil.getSettingsResults(result)) {
+ List<Pair<Object, Parameter<?>>> settings = new ArrayList<Pair<Object, Parameter<?>>>();
+ for (SettingsResult sr : ResultUtil.getSettingsResults(result)) {
settings.addAll(sr.getSettings());
}
String algorithm = null;
String distance = null;
String dataset = null;
- for(Pair<Object, Parameter<?, ?>> setting : settings) {
- if(setting.second.equals(OptionID.ALGORITHM)) {
+ for (Pair<Object, Parameter<?>> setting : settings) {
+ if (setting.second.equals(OptionID.ALGORITHM)) {
algorithm = setting.second.getValue().toString();
}
- if(setting.second.equals(AbstractDistanceBasedAlgorithm.DISTANCE_FUNCTION_ID)) {
+ if (setting.second.equals(AbstractDistanceBasedAlgorithm.DISTANCE_FUNCTION_ID)) {
distance = setting.second.getValue().toString();
}
- if(setting.second.equals(FileBasedDatabaseConnection.INPUT_ID)) {
+ if (setting.second.equals(FileBasedDatabaseConnection.INPUT_ID)) {
dataset = setting.second.getValue().toString();
}
}
StringBuilder buf = new StringBuilder();
- if(algorithm != null) {
+ if (algorithm != null) {
// shorten the algorithm
- if(algorithm.contains(".")) {
- algorithm = algorithm.substring(algorithm.lastIndexOf(".") + 1);
+ if (algorithm.contains(".")) {
+ algorithm = algorithm.substring(algorithm.lastIndexOf('.') + 1);
}
buf.append(algorithm);
}
- if(distance != null) {
+ if (distance != null) {
// shorten the distance
- if(distance.contains(".")) {
- distance = distance.substring(distance.lastIndexOf(".") + 1);
+ if (distance.contains(".")) {
+ distance = distance.substring(distance.lastIndexOf('.') + 1);
}
- if(buf.length() > 0) {
+ if (buf.length() > 0) {
buf.append(" using ");
}
buf.append(distance);
}
- if(dataset != null) {
+ if (dataset != null) {
// shorten the data set filename
- if(dataset.contains(File.separator)) {
+ if (dataset.contains(File.separator)) {
dataset = dataset.substring(dataset.lastIndexOf(File.separator) + 1);
}
- if(buf.length() > 0) {
+ if (buf.length() > 0) {
buf.append(" on ");
}
buf.append(dataset);
}
- if(buf.length() > 0) {
+ if (buf.length() > 0) {
return buf.toString();
}
return null;
@@ -260,27 +260,27 @@ public class VisualizerParameterizer implements Parameterizable {
protected Collection<ProjectorFactory> projectors = null;
protected int samplesize = -1;
-
+
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- IntParameter samplingP = new IntParameter(SAMPLING_ID, new GreaterEqualConstraint(-1), 10000);
- if(config.grab(samplingP)) {
- samplesize = samplingP.getValue();
+ IntParameter samplingP = new IntParameter(SAMPLING_ID, 10000);
+ samplingP.addConstraint(new GreaterEqualConstraint(-1));
+ if (config.grab(samplingP)) {
+ samplesize = samplingP.intValue();
}
StringParameter stylelibP = new StringParameter(STYLELIB_ID, PropertiesBasedStyleLibrary.DEFAULT_SCHEME_FILENAME);
- if(config.grab(stylelibP)) {
+ if (config.grab(stylelibP)) {
String filename = stylelibP.getValue();
try {
stylelib = new PropertiesBasedStyleLibrary(filename, "Command line style");
- }
- catch(AbortException e) {
+ } catch (AbortException e) {
config.reportError(new WrongParameterValueException(stylelibP, filename, e));
}
}
PatternParameter enablevisP = new PatternParameter(ENABLEVIS_ID, DEFAULT_ENABLEVIS);
- if(config.grab(enablevisP)) {
- if(!"all".equals(enablevisP.getValueAsString())) {
+ if (config.grab(enablevisP)) {
+ if (!"all".equals(enablevisP.getValueAsString())) {
enableVisualizers = enablevisP.getValue();
}
}
@@ -298,21 +298,19 @@ public class VisualizerParameterizer implements Parameterizable {
*/
private static <O> Collection<ProjectorFactory> collectProjectorFactorys(MergedParameterization config, Pattern filter) {
ArrayList<ProjectorFactory> factories = new ArrayList<ProjectorFactory>();
- for(Class<?> c : InspectionUtil.cachedFindAllImplementations(ProjectorFactory.class)) {
- if(filter != null && !filter.matcher(c.getCanonicalName()).find()) {
+ for (Class<?> c : InspectionUtil.cachedFindAllImplementations(ProjectorFactory.class)) {
+ if (filter != null && !filter.matcher(c.getCanonicalName()).find()) {
continue;
}
try {
config.rewind();
ProjectorFactory a = ClassGenericsUtil.tryInstantiate(ProjectorFactory.class, c, config);
factories.add(a);
- }
- catch(Throwable e) {
- if(logger.isDebugging()) {
- logger.exception("Error instantiating visualization factory " + c.getName(), e.getCause());
- }
- else {
- logger.warning("Error instantiating visualization factory " + c.getName() + ": " + e.getMessage());
+ } catch (Throwable e) {
+ if (LOG.isDebugging()) {
+ LOG.exception("Error instantiating visualization factory " + c.getName(), e.getCause());
+ } else {
+ LOG.warning("Error instantiating visualization factory " + c.getName() + ": " + e.getMessage());
}
}
}
@@ -328,21 +326,19 @@ public class VisualizerParameterizer implements Parameterizable {
*/
private static <O> Collection<VisFactory> collectVisFactorys(MergedParameterization config, Pattern filter) {
ArrayList<VisFactory> factories = new ArrayList<VisFactory>();
- for(Class<?> c : InspectionUtil.cachedFindAllImplementations(VisFactory.class)) {
- if(filter != null && !filter.matcher(c.getCanonicalName()).find()) {
+ for (Class<?> c : InspectionUtil.cachedFindAllImplementations(VisFactory.class)) {
+ if (filter != null && !filter.matcher(c.getCanonicalName()).find()) {
continue;
}
try {
config.rewind();
VisFactory a = ClassGenericsUtil.tryInstantiate(VisFactory.class, c, config);
factories.add(a);
- }
- catch(Throwable e) {
- if(logger.isDebugging()) {
- logger.exception("Error instantiating visualization factory " + c.getName(), e.getCause());
- }
- else {
- logger.warning("Error instantiating visualization factory " + c.getName() + ": " + e.getMessage());
+ } catch (Throwable e) {
+ if (LOG.isDebugging()) {
+ LOG.exception("Error instantiating visualization factory " + c.getName(), e.getCause());
+ } else {
+ LOG.warning("Error instantiating visualization factory " + c.getName() + ": " + e.getMessage());
}
}
}
@@ -354,4 +350,4 @@ public class VisualizerParameterizer implements Parameterizable {
return new VisualizerParameterizer(samplesize, stylelib, projectors, factories, enableVisualizers);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/batikutil/CSSHoverClass.java b/src/de/lmu/ifi/dbs/elki/visualization/batikutil/CSSHoverClass.java
index 3e9f355a..54d8e7e3 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/batikutil/CSSHoverClass.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/batikutil/CSSHoverClass.java
@@ -30,12 +30,11 @@ import org.w3c.dom.events.EventListener;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
-
/**
* Do a hover effect using a CSS class.
*
* @author Erich Schubert
- *
+ *
*/
public class CSSHoverClass implements EventListener {
/**
@@ -47,7 +46,7 @@ public class CSSHoverClass implements EventListener {
* Class to set when out
*/
private String outclass;
-
+
/**
* Consider a click as 'out'?
*/
@@ -55,6 +54,7 @@ public class CSSHoverClass implements EventListener {
/**
* Constructor
+ *
* @param overclass class to set when over
* @param outclass class to set when out
* @param clickisout consider a click to be an 'out' event
@@ -82,7 +82,7 @@ public class CSSHoverClass implements EventListener {
@Override
public void handleEvent(Event evt) {
Element e = (Element) evt.getTarget();
- if (evt.getType() == SVGConstants.SVG_EVENT_MOUSEOVER) {
+ if (SVGConstants.SVG_EVENT_MOUSEOVER.equals(evt.getType())) {
if (overclass != null) {
SVGUtil.addCSSClass(e, overclass);
}
@@ -90,7 +90,7 @@ public class CSSHoverClass implements EventListener {
SVGUtil.removeCSSClass(e, outclass);
}
}
- if (evt.getType() == SVGConstants.SVG_EVENT_MOUSEOUT) {
+ if (SVGConstants.SVG_EVENT_MOUSEOUT.equals(evt.getType())) {
if (overclass != null) {
SVGUtil.removeCSSClass(e, overclass);
}
@@ -98,7 +98,7 @@ public class CSSHoverClass implements EventListener {
SVGUtil.addCSSClass(e, outclass);
}
}
- if (clickisout && evt.getType() == SVGConstants.SVG_EVENT_CLICK) {
+ if (clickisout && SVGConstants.SVG_EVENT_CLICK.equals(evt.getType())) {
if (overclass != null) {
SVGUtil.removeCSSClass(e, overclass);
}
@@ -107,4 +107,4 @@ public class CSSHoverClass implements EventListener {
}
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/batikutil/CloneInlineImages.java b/src/de/lmu/ifi/dbs/elki/visualization/batikutil/CloneInlineImages.java
index 30456ba9..9c53d72b 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/batikutil/CloneInlineImages.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/batikutil/CloneInlineImages.java
@@ -46,6 +46,8 @@ import de.lmu.ifi.dbs.elki.visualization.svg.SVGCloneVisible;
* Clone an SVG document, inlining temporary and in-memory linked images.
*
* @author Erich Schubert
+ *
+ * @apiviz.has ThumbnailRegistryEntry
*/
public class CloneInlineImages extends SVGCloneVisible {
@Override
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/batikutil/JSVGUpdateSynchronizer.java b/src/de/lmu/ifi/dbs/elki/visualization/batikutil/JSVGUpdateSynchronizer.java
index 953c39da..110d38ec 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/batikutil/JSVGUpdateSynchronizer.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/batikutil/JSVGUpdateSynchronizer.java
@@ -24,6 +24,7 @@ package de.lmu.ifi.dbs.elki.visualization.batikutil;
*/
import java.lang.ref.WeakReference;
+import java.util.ArrayList;
import java.util.List;
import org.apache.batik.bridge.UpdateManager;
@@ -50,7 +51,7 @@ class JSVGUpdateSynchronizer implements UpdateSynchronizer {
/**
* The UpdateRunner we are put into
*/
- private List<WeakReference<UpdateRunner>> updaterunner = new java.util.Vector<WeakReference<UpdateRunner>>();
+ private List<WeakReference<UpdateRunner>> updaterunner = new ArrayList<WeakReference<UpdateRunner>>();
/**
* Adapter to track component changes
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/batikutil/ThumbnailRegistryEntry.java b/src/de/lmu/ifi/dbs/elki/visualization/batikutil/ThumbnailRegistryEntry.java
index dd844165..83c101e3 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/batikutil/ThumbnailRegistryEntry.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/batikutil/ThumbnailRegistryEntry.java
@@ -75,7 +75,7 @@ public class ThumbnailRegistryEntry extends AbstractRegistryEntry implements URL
/**
* The logger class.
*/
- private static final Logging logger = Logging.getLogger(ThumbnailRegistryEntry.class);
+ private static final Logging LOG = Logging.getLogger(ThumbnailRegistryEntry.class);
/**
* The image cache.
@@ -95,8 +95,8 @@ public class ThumbnailRegistryEntry extends AbstractRegistryEntry implements URL
*/
public ThumbnailRegistryEntry() {
super("Internal", PRIORITY, new String[0], new String[] { INTERNAL_MIME_TYPE });
- if(logger.isDebuggingFiner()) {
- logger.debugFiner("Registry initialized.");
+ if(LOG.isDebuggingFiner()) {
+ LOG.debugFiner("Registry initialized.");
}
}
@@ -122,8 +122,8 @@ public class ThumbnailRegistryEntry extends AbstractRegistryEntry implements URL
}
}
}
- if(logger.isDebuggingFiner()) {
- logger.debugFiner("Registered image: " + key);
+ if(LOG.isDebuggingFiner()) {
+ LOG.debugFiner("Registered image: " + key);
}
return key;
}
@@ -162,8 +162,8 @@ public class ThumbnailRegistryEntry extends AbstractRegistryEntry implements URL
* @return Image, or null
*/
public static Filter handleURL(ParsedURL url) {
- if(logger.isDebuggingFiner()) {
- logger.debugFiner("handleURL " + url.toString());
+ if(LOG.isDebuggingFiner()) {
+ LOG.debugFiner("handleURL " + url.toString());
}
if(!isCompatibleURLStatic(url)) {
return null;
@@ -179,7 +179,7 @@ public class ThumbnailRegistryEntry extends AbstractRegistryEntry implements URL
if(ref != null) {
RenderedImage ri = ref.get();
if(ri == null) {
- logger.warning("Referenced image has expired from the cache!");
+ LOG.warning("Referenced image has expired from the cache!");
}
else {
return new RedRable(GraphicsUtil.wrap(ri));
@@ -193,6 +193,8 @@ public class ThumbnailRegistryEntry extends AbstractRegistryEntry implements URL
* URL representation for internal URLs.
*
* @author Erich Schubert
+ *
+ * @apiviz.exclude
*/
class InternalParsedURLData extends ParsedURLData {
/**
@@ -225,8 +227,8 @@ public class ThumbnailRegistryEntry extends AbstractRegistryEntry implements URL
@Override
public ParsedURLData parseURL(String urlStr) {
- if(logger.isDebuggingFinest()) {
- logger.debugFinest("parseURL: " + urlStr);
+ if(LOG.isDebuggingFinest()) {
+ LOG.debugFinest("parseURL: " + urlStr);
}
if(urlStr.startsWith(INTERNAL_PREFIX)) {
InternalParsedURLData ret = new InternalParsedURLData(urlStr.substring(INTERNAL_PREFIX.length()));
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/colors/ColorLibrary.java b/src/de/lmu/ifi/dbs/elki/visualization/colors/ColorLibrary.java
index 2693bc6b..2d7512c8 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/colors/ColorLibrary.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/colors/ColorLibrary.java
@@ -23,7 +23,6 @@ package de.lmu.ifi.dbs.elki.visualization.colors;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-
/**
* Color scheme interface
*
@@ -34,49 +33,61 @@ public interface ColorLibrary {
* List of line colors
*/
final static String COLOR_LINE_COLORS = "line.colors";
+
/**
* Named color for the page background
*/
final static String COLOR_PAGE_BACKGROUND = "page.background";
+
/**
* Named color for a typical axis
*/
final static String COLOR_AXIS_LINE = "axis.line";
+
/**
* Named color for a typical axis tick mark
*/
final static String COLOR_AXIS_TICK = "axis.tick";
+
/**
* Named color for a typical axis tick mark
*/
final static String COLOR_AXIS_MINOR_TICK = "axis.tick.minor";
+
/**
* Named color for a typical axis label
*/
final static String COLOR_AXIS_LABEL = "axis.label";
+
/**
* Named color for the background of the key box
*/
final static String COLOR_KEY_BACKGROUND = "key.background";
+
/**
* Named color for a label in the key part
*/
final static String COLOR_KEY_LABEL = "key.label";
+
/**
* Background color for plot area
*/
final static String COLOR_PLOT_BACKGROUND = "plot.background";
+
/**
- * Return the number of native colors available. These are guaranteed to be unique.
+ * Return the number of native colors available. These are guaranteed to be
+ * unique.
*
* @return number of native colors
*/
public int getNumberOfNativeColors();
+
/**
* Return the i'th color.
*
* @param index color index
- * @return color in hexadecimal notation (#aabbcc) or color name ("red") as valid in CSS and SVG.
+ * @return color in hexadecimal notation (#aabbcc) or color name ("red") as
+ * valid in CSS and SVG.
*/
public String getColor(int index);
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/css/CSSClass.java b/src/de/lmu/ifi/dbs/elki/visualization/css/CSSClass.java
index 5c89f638..bb62b7ef 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/css/CSSClass.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/css/CSSClass.java
@@ -265,13 +265,13 @@ public class CSSClass {
*
* @param buf String buffer to append to.
*/
- public void appendCSSDefinition(StringBuffer buf) {
+ public void appendCSSDefinition(StringBuilder buf) {
buf.append("\n.");
buf.append(name);
- buf.append("{");
+ buf.append('{');
for (Pair<String, String> pair : statements) {
buf.append(pair.getFirst());
- buf.append(":");
+ buf.append(':');
buf.append(pair.getSecond());
buf.append(";\n");
}
@@ -305,12 +305,12 @@ public class CSSClass {
* @return string rendition of CSS for inline use
*/
public String inlineCSS() {
- StringBuffer buf = new StringBuffer();
+ StringBuilder buf = new StringBuilder();
for (Pair<String, String> pair : statements) {
buf.append(pair.getFirst());
- buf.append(":");
+ buf.append(':');
buf.append(pair.getSecond());
- buf.append(";");
+ buf.append(';');
}
return buf.toString();
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/css/CSSClassManager.java b/src/de/lmu/ifi/dbs/elki/visualization/css/CSSClassManager.java
index 22e53c55..34debe56 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/css/CSSClassManager.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/css/CSSClassManager.java
@@ -119,7 +119,7 @@ public class CSSClassManager {
*
* @param buf String buffer
*/
- public void serialize(StringBuffer buf) {
+ public void serialize(StringBuilder buf) {
for (CSSClass clss : store.values()) {
clss.appendCSSDefinition(buf);
}
@@ -193,7 +193,7 @@ public class CSSClassManager {
* @param style Style element
*/
public void updateStyleElement(Document document, Element style) {
- StringBuffer buf = new StringBuffer();
+ StringBuilder buf = new StringBuilder();
serialize(buf);
Text cont = document.createTextNode(buf.toString());
while (style.hasChildNodes()) {
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/gui/ResultVisualizer.java b/src/de/lmu/ifi/dbs/elki/visualization/gui/ResultVisualizer.java
index 2c3db308..635a0466 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/gui/ResultVisualizer.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/gui/ResultVisualizer.java
@@ -25,6 +25,7 @@ package de.lmu.ifi.dbs.elki.visualization.gui;
import javax.swing.JFrame;
+import de.lmu.ifi.dbs.elki.gui.GUIUtil;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
import de.lmu.ifi.dbs.elki.result.Result;
@@ -51,7 +52,7 @@ public class ResultVisualizer implements ResultHandler {
/**
* Get a logger for this class.
*/
- protected final static Logging logger = Logging.getLogger(ResultVisualizer.class);
+ private static final Logging LOG = Logging.getLogger(ResultVisualizer.class);
/**
* Parameter to specify the window title
@@ -62,7 +63,7 @@ public class ResultVisualizer implements ResultHandler {
* Default value: "ELKI Result Visualization"
* </p>
*/
- public static final OptionID WINDOW_TITLE_ID = OptionID.getOrCreateOptionID("vis.window.title", "Title to use for visualization window.");
+ public static final OptionID WINDOW_TITLE_ID = new OptionID("vis.window.title", "Title to use for visualization window.");
/**
* Flag to set single display
@@ -71,7 +72,7 @@ public class ResultVisualizer implements ResultHandler {
* Key: -vis.single
* </p>
*/
- public final static OptionID SINGLE_ID = OptionID.getOrCreateOptionID("vis.window.single", "Embed visualizers in a single window, not using thumbnails and detail views.");
+ public static final OptionID SINGLE_ID = new OptionID("vis.window.single", "Embed visualizers in a single window, not using thumbnails and detail views.");
/**
* Stores the set title.
@@ -81,7 +82,7 @@ public class ResultVisualizer implements ResultHandler {
/**
* Default title
*/
- protected final static String DEFAULT_TITLE = "ELKI Result Visualization";
+ protected static final String DEFAULT_TITLE = "ELKI Result Visualization";
/**
* Visualization manager.
@@ -124,13 +125,14 @@ public class ResultVisualizer implements ResultHandler {
@Override
public void run() {
try {
+ GUIUtil.setLookAndFeel();
ResultWindow window = new ResultWindow(title, top, context, single);
window.setVisible(true);
window.setExtendedState(window.getExtendedState() | JFrame.MAXIMIZED_BOTH);
window.showOverview();
}
catch(Throwable e) {
- logger.exception("Error in starting visualizer window.", e);
+ LOG.exception("Error in starting visualizer window.", e);
}
}
});
@@ -162,13 +164,14 @@ public class ResultVisualizer implements ResultHandler {
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- StringParameter titleP = new StringParameter(WINDOW_TITLE_ID, true);
+ StringParameter titleP = new StringParameter(WINDOW_TITLE_ID);
+ titleP.setOptional(true);
if(config.grab(titleP)) {
title = titleP.getValue();
}
Flag singleF = new Flag(SINGLE_ID);
if (config.grab(singleF)) {
- single = singleF.getValue();
+ single = singleF.isTrue();
}
manager = config.tryInstantiate(VisualizerParameterizer.class);
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/gui/ResultWindow.java b/src/de/lmu/ifi/dbs/elki/visualization/gui/ResultWindow.java
index 38476d7d..609932cf 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/gui/ResultWindow.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/gui/ResultWindow.java
@@ -87,7 +87,7 @@ public class ResultWindow extends JFrame implements ResultListener {
/**
* Get a logger for this class.
*/
- protected final static Logging logger = Logging.getLogger(ResultWindow.class);
+ private static final Logging LOG = Logging.getLogger(ResultWindow.class);
/**
* The "Overview" button, which goes to the overview view.
@@ -348,7 +348,7 @@ public class ResultWindow extends JFrame implements ResultListener {
SVGSaveDialog.showSaveDialog(currentPlot, 512, 512);
}
else {
- logger.warning("saveCurrentPlot() called without a visible plot!");
+ LOG.warning("saveCurrentPlot() called without a visible plot!");
}
}
@@ -431,8 +431,8 @@ public class ResultWindow extends JFrame implements ResultListener {
// Currently enabled?
final String name = v.getLongName();
- boolean enabled = VisualizerUtil.isVisible(v);
- boolean istool = VisualizerUtil.isTool(v);
+ boolean enabled = v.visible;
+ boolean istool = v.tool;
if(!istool) {
final JCheckBoxMenuItem visItem = new JCheckBoxMenuItem(name, enabled);
visItem.addItemListener(new ItemListener() {
@@ -465,8 +465,7 @@ public class ResultWindow extends JFrame implements ResultListener {
});
item = visItem;
}
- boolean hasoptions = VisualizerUtil.hasOptions(v);
- if(hasoptions) {
+ if(v.hasoptions) {
final JMenu menu = new JMenu(name);
menu.add(item);
// TODO: build a menu for the visualizer!
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/gui/SelectionTableWindow.java b/src/de/lmu/ifi/dbs/elki/visualization/gui/SelectionTableWindow.java
index 754779fe..4fea630a 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/gui/SelectionTableWindow.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/gui/SelectionTableWindow.java
@@ -99,7 +99,7 @@ public class SelectionTableWindow extends JFrame implements DataStoreListener, R
/**
* The logger
*/
- static final Logging logger = Logging.getLogger(SelectionTableWindow.class);
+ private static final Logging LOG = Logging.getLogger(SelectionTableWindow.class);
/**
* The DBIDs to display
@@ -229,7 +229,7 @@ public class SelectionTableWindow extends JFrame implements DataStoreListener, R
context.setSelection(new DBIDSelection(remain));
// Now delete them.
for(DBIDIter iter = todel.iter(); iter.valid(); iter.advance()) {
- upd.delete(iter.getDBID());
+ upd.delete(iter);
}
}
@@ -248,7 +248,7 @@ public class SelectionTableWindow extends JFrame implements DataStoreListener, R
@Override
public int getColumnCount() {
- return 3; //DatabaseUtil.dimensionality(database) + 3;
+ return 3; //RelationUtil.dimensionality(database) + 3;
}
@Override
@@ -301,7 +301,7 @@ public class SelectionTableWindow extends JFrame implements DataStoreListener, R
@Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
if(columnIndex == 0) {
- logger.warning("Tried to edit DBID, this is not allowed.");
+ LOG.warning("Tried to edit DBID, this is not allowed.");
return;
}
final DBID id = dbids.get(rowIndex);
@@ -314,7 +314,7 @@ public class SelectionTableWindow extends JFrame implements DataStoreListener, R
crep.set(id, lbl);
}
if(!(aValue instanceof String)) {
- logger.warning("Was expecting a String value from the input element, got: " + aValue.getClass());
+ LOG.warning("Was expecting a String value from the input element, got: " + aValue.getClass());
return;
}
throw new AbortException("FIXME: INCOMPLETE TRANSITION");
@@ -323,7 +323,7 @@ public class SelectionTableWindow extends JFrame implements DataStoreListener, R
logger.warning("Tried to edit removed object?");
return;
}
- final int dimensionality = DatabaseUtil.dimensionality(database);
+ final int dimensionality = RelationUtil.dimensionality(database);
double[] vals = new double[dimensionality];
for(int d = 0; d < dimensionality; d++) {
if(d == columnIndex - 3) {
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/gui/detail/DetailView.java b/src/de/lmu/ifi/dbs/elki/visualization/gui/detail/DetailView.java
index 3e1348ac..091bc8ca 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/gui/detail/DetailView.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/gui/detail/DetailView.java
@@ -46,7 +46,6 @@ import de.lmu.ifi.dbs.elki.visualization.svg.SVGEffects;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
-import de.lmu.ifi.dbs.elki.visualization.visualizers.VisualizerUtil;
/**
* Manages a detail view.
@@ -122,7 +121,7 @@ public class DetailView extends SVGPlot implements ResultListener {
private void addBackground(VisualizerContext context) {
// Make a background
CSSClass cls = new CSSClass(this, "background");
- cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, context.getStyleLibrary().getBackgroundColor(StyleLibrary.PAGE));
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, context.getStyleResult().getStyleLibrary().getBackgroundColor(StyleLibrary.PAGE));
Element bg = this.svgElement(SVGConstants.SVG_RECT_TAG);
SVGUtil.setAtt(bg, SVGConstants.SVG_X_ATTRIBUTE, "0");
SVGUtil.setAtt(bg, SVGConstants.SVG_Y_ATTRIBUTE, "0");
@@ -146,10 +145,10 @@ public class DetailView extends SVGPlot implements ResultListener {
// TODO: center/arrange visualizations?
for(Iterator<VisualizationTask> tit = visi.tasks.iterator(); tit.hasNext();) {
VisualizationTask task = tit.next();
- if(VisualizerUtil.isVisible(task)) {
+ if(task.visible) {
try {
Visualization v = task.getFactory().makeVisualization(task.clone(this, context, visi.proj, width, height));
- if (VisualizerUtil.isNoExport(task)) {
+ if (task.noexport) {
v.getLayer().setAttribute(NO_EXPORT_ATTRIBUTE, NO_EXPORT_ATTRIBUTE);
}
layers.add(v);
@@ -277,8 +276,8 @@ public class DetailView extends SVGPlot implements ResultListener {
Visualization vis = layermap.get(task);
if(vis != null) {
// Ensure visibility is as expected
- boolean isHidden = vis.getLayer().getAttribute(SVGConstants.CSS_VISIBILITY_PROPERTY) == SVGConstants.CSS_HIDDEN_VALUE;
- if(VisualizerUtil.isVisible(task)) {
+ boolean isHidden = SVGConstants.CSS_HIDDEN_VALUE.equals(vis.getLayer().getAttribute(SVGConstants.CSS_VISIBILITY_PROPERTY));
+ if(task.visible) {
if(isHidden) {
this.scheduleUpdate(new AttributeModifier(vis.getLayer(), SVGConstants.CSS_VISIBILITY_PROPERTY, SVGConstants.CSS_VISIBLE_VALUE));
}
@@ -291,10 +290,10 @@ public class DetailView extends SVGPlot implements ResultListener {
}
else {
// Only materialize when becoming visible
- if(VisualizerUtil.isVisible(task)) {
+ if(task.visible) {
// LoggingUtil.warning("Need to recreate a missing layer for " + v);
vis = task.getFactory().makeVisualization(task.clone(this, context, visi.proj, width, height));
- if (VisualizerUtil.isNoExport(task)) {
+ if (task.noexport) {
vis.getLayer().setAttribute(NO_EXPORT_ATTRIBUTE, NO_EXPORT_ATTRIBUTE);
}
layermap.put(task, vis);
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/gui/overview/OverviewPlot.java b/src/de/lmu/ifi/dbs/elki/visualization/gui/overview/OverviewPlot.java
index 92ddb0b8..271a62ff 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/gui/overview/OverviewPlot.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/gui/overview/OverviewPlot.java
@@ -55,7 +55,6 @@ import de.lmu.ifi.dbs.elki.visualization.svg.SVGEffects;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
-import de.lmu.ifi.dbs.elki.visualization.visualizers.VisualizerUtil;
/**
* Generate an overview plot for a set of visualizations.
@@ -76,7 +75,7 @@ public class OverviewPlot extends SVGPlot implements ResultListener {
/**
* Our logging class
*/
- private static final Logging logger = Logging.getLogger(OverviewPlot.class);
+ private static final Logging LOG = Logging.getLogger(OverviewPlot.class);
/**
* Visualizer context
@@ -91,7 +90,7 @@ public class OverviewPlot extends SVGPlot implements ResultListener {
/**
* Action listeners for this plot.
*/
- private java.util.Vector<ActionListener> actionListeners = new java.util.Vector<ActionListener>();
+ private ArrayList<ActionListener> actionListeners = new ArrayList<ActionListener>();
/**
* Single view mode
@@ -163,7 +162,7 @@ public class OverviewPlot extends SVGPlot implements ResultListener {
// Add a background element:
{
CSSClass cls = new CSSClass(this, "background");
- cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, context.getStyleLibrary().getBackgroundColor(StyleLibrary.PAGE));
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, context.getStyleResult().getStyleLibrary().getBackgroundColor(StyleLibrary.PAGE));
addCSSClassOrLogError(cls);
Element background = this.svgElement(SVGConstants.SVG_RECT_TAG);
background.setAttribute(SVGConstants.SVG_X_ATTRIBUTE, "0");
@@ -174,7 +173,7 @@ public class OverviewPlot extends SVGPlot implements ResultListener {
getRoot().appendChild(background);
}
- if(single) {
+ if (single) {
setDisableInteractions(true);
}
SVGEffects.addShadowFilter(this);
@@ -186,19 +185,22 @@ public class OverviewPlot extends SVGPlot implements ResultListener {
/**
* Recompute the layout of visualizations.
+ *
+ * @param width Initial width
+ * @param height Initial height
+ * @return Arrangement
*/
- private void arrangeVisualizations() {
- RectangleArranger<PlotItem> plotmap = new RectangleArranger<PlotItem>(ratio);
+ private RectangleArranger<PlotItem> arrangeVisualizations(double width, double height) {
+ RectangleArranger<PlotItem> plotmap = new RectangleArranger<PlotItem>(width, height);
ArrayList<Projector> projectors = ResultUtil.filterResults(context.getResult(), Projector.class);
// Rectangle layout
- for(Projector p : projectors) {
+ for (Projector p : projectors) {
Collection<PlotItem> projs = p.arrange();
- for(PlotItem it : projs) {
- if(it.w <= 0.0 || it.h <= 0.0) {
- logger.warning("Plot item with improper size information: " + it);
- }
- else {
+ for (PlotItem it : projs) {
+ if (it.w <= 0.0 || it.h <= 0.0) {
+ LOG.warning("Plot item with improper size information: " + it);
+ } else {
plotmap.put(it.w, it.h, it);
}
}
@@ -206,22 +208,21 @@ public class OverviewPlot extends SVGPlot implements ResultListener {
ResultHierarchy hier = context.getHierarchy();
ArrayList<VisualizationTask> tasks = ResultUtil.filterResults(context.getResult(), VisualizationTask.class);
- nextTask: for(VisualizationTask task : tasks) {
- for(Result parent : hier.getParents(task)) {
- if(parent instanceof Projector) {
+ nextTask: for (VisualizationTask task : tasks) {
+ for (Result parent : hier.getParents(task)) {
+ if (parent instanceof Projector) {
continue nextTask;
}
}
- if(task.getWidth() <= 0.0 || task.getHeight() <= 0.0) {
- logger.warning("Task with improper size information: " + task);
- }
- else {
+ if (task.getWidth() <= 0.0 || task.getHeight() <= 0.0) {
+ LOG.warning("Task with improper size information: " + task);
+ } else {
PlotItem it = new PlotItem(task.getWidth(), task.getHeight(), null);
it.tasks.add(task);
plotmap.put(it.w, it.h, it);
}
}
- this.plotmap = plotmap;
+ return plotmap;
}
/**
@@ -229,13 +230,19 @@ public class OverviewPlot extends SVGPlot implements ResultListener {
*/
private void reinitialize() {
setupHoverer();
- arrangeVisualizations();
+ plotmap = arrangeVisualizations(ratio, 1.0);
+ double s = plotmap.relativeFill();
+ if (s < 0.9) {
+ // Retry, sometimes this yields better results
+ plotmap = arrangeVisualizations(plotmap.getWidth() * s, plotmap.getHeight() * s);
+ }
+
recalcViewbox();
final int thumbsize = (int) Math.max(screenwidth / plotmap.getWidth(), screenheight / plotmap.getHeight());
// TODO: cancel pending thumbnail requests!
// Detach existing elements:
- for(Pair<Element, Visualization> pair : vistoelem.values()) {
+ for (Pair<Element, Visualization> pair : vistoelem.values()) {
SVGUtil.removeFromParent(pair.first);
}
// Replace the layer map
@@ -249,10 +256,10 @@ public class OverviewPlot extends SVGPlot implements ResultListener {
hoverlayer = this.svgElement(SVGConstants.SVG_G_TAG);
// Redo the layout
- for(Entry<PlotItem, double[]> e : plotmap.entrySet()) {
+ for (Entry<PlotItem, double[]> e : plotmap.entrySet()) {
final double basex = e.getValue()[0];
final double basey = e.getValue()[1];
- for(Iterator<PlotItem> iter = e.getKey().itemIterator(); iter.hasNext();) {
+ for (Iterator<PlotItem> iter = e.getKey().itemIterator(); iter.hasNext();) {
PlotItem it = iter.next();
boolean hasDetails = false;
@@ -262,27 +269,24 @@ public class OverviewPlot extends SVGPlot implements ResultListener {
plotlayer.appendChild(g);
vistoelem.put(it, null, g, null);
// Add the actual tasks:
- for(VisualizationTask task : it.tasks) {
- if(!visibleInOverview(task)) {
+ for (VisualizationTask task : it.tasks) {
+ if (!visibleInOverview(task)) {
continue;
}
- if(VisualizerUtil.detailsEnabled(task)) {
- hasDetails = true;
- // TODO: not updatable
- }
+ hasDetails |= !task.nodetail;
Pair<Element, Visualization> pair = oldlayers.remove(it, task);
- if(pair == null) {
+ if (pair == null) {
pair = new Pair<Element, Visualization>(null, null);
pair.first = svgElement(SVGConstants.SVG_G_TAG);
}
- if(pair.second == null) {
+ if (pair.second == null) {
pair.second = embedOrThumbnail(thumbsize, it, task, pair.first);
}
g.appendChild(pair.first);
vistoelem.put(it, task, pair);
}
// When needed, add a hover effect
- if(hasDetails && !single) {
+ if (hasDetails && !single) {
Element hover = this.svgRect(basex + it.x, basey + it.y, it.w, it.h);
SVGUtil.addCSSClass(hover, selcss.getName());
// link hoverer.
@@ -296,8 +300,8 @@ public class OverviewPlot extends SVGPlot implements ResultListener {
}
}
}
- for(Pair<Element, Visualization> pair : oldlayers.values()) {
- if(pair.second != null) {
+ for (Pair<Element, Visualization> pair : oldlayers.values()) {
+ if (pair.second != null) {
pair.second.destroy();
}
}
@@ -315,29 +319,26 @@ public class OverviewPlot extends SVGPlot implements ResultListener {
* @param parent Parent element to draw to
*/
private Visualization embedOrThumbnail(final int thumbsize, PlotItem it, VisualizationTask task, Element parent) {
- if(single) {
+ if (single) {
VisualizationTask thumbtask = task.clone(this, context, it.proj, it.w, it.h);
final Visualization vis = thumbtask.getFactory().makeVisualization(thumbtask);
- if(vis.getLayer() == null) {
+ if (vis.getLayer() == null) {
LoggingUtil.warning("Visualization returned empty layer: " + vis);
- }
- else {
- if (VisualizerUtil.isNoExport(task)) {
+ } else {
+ if (task.noexport) {
vis.getLayer().setAttribute(NO_EXPORT_ATTRIBUTE, NO_EXPORT_ATTRIBUTE);
}
parent.appendChild(vis.getLayer());
}
return vis;
- }
- else {
+ } else {
VisualizationTask thumbtask = task.clone(this, context, it.proj, it.w, it.h);
- thumbtask.put(VisualizationTask.THUMBNAIL, true);
- thumbtask.put(VisualizationTask.THUMBNAIL_RESOLUTION, thumbsize);
+ thumbtask.thumbnail = true;
+ thumbtask.thumbsize = thumbsize;
final Visualization vis = thumbtask.getFactory().makeVisualizationOrThumbnail(thumbtask);
- if(vis.getLayer() == null) {
+ if (vis.getLayer() == null) {
LoggingUtil.warning("Visualization returned empty layer: " + vis);
- }
- else {
+ } else {
parent.appendChild(vis.getLayer());
}
return vis;
@@ -349,25 +350,24 @@ public class OverviewPlot extends SVGPlot implements ResultListener {
*/
synchronized void refresh() {
pendingRefresh = null;
- if(reinitOnRefresh) {
- logger.debug("Reinitialize");
+ if (reinitOnRefresh) {
+ LOG.debug("Reinitialize");
reinitialize();
reinitOnRefresh = false;
- }
- else {
- logger.debug("Incremental refresh");
+ } else {
+ LOG.debug("Incremental refresh");
boolean refreshcss = false;
final int thumbsize = (int) Math.max(screenwidth / plotmap.getWidth(), screenheight / plotmap.getHeight());
- for(PlotItem pi : plotmap.keySet()) {
- for(Iterator<PlotItem> iter = pi.itemIterator(); iter.hasNext();) {
+ for (PlotItem pi : plotmap.keySet()) {
+ for (Iterator<PlotItem> iter = pi.itemIterator(); iter.hasNext();) {
PlotItem it = iter.next();
- for(Iterator<VisualizationTask> tit = it.tasks.iterator(); tit.hasNext();) {
+ for (Iterator<VisualizationTask> tit = it.tasks.iterator(); tit.hasNext();) {
VisualizationTask task = tit.next();
Pair<Element, Visualization> pair = vistoelem.get(it, task);
// New task?
- if(pair == null) {
- if(visibleInOverview(task)) {
+ if (pair == null) {
+ if (visibleInOverview(task)) {
pair = new Pair<Element, Visualization>(null, null);
pair.first = svgElement(SVGConstants.SVG_G_TAG);
pair.second = embedOrThumbnail(thumbsize, it, task, pair.first);
@@ -375,24 +375,22 @@ public class OverviewPlot extends SVGPlot implements ResultListener {
vistoelem.put(it, task, pair);
refreshcss = true;
}
- }
- else {
- if(visibleInOverview(task)) {
+ } else {
+ if (visibleInOverview(task)) {
// unhide if hidden.
- if(pair.first.hasAttribute(SVGConstants.CSS_VISIBILITY_PROPERTY)) {
+ if (pair.first.hasAttribute(SVGConstants.CSS_VISIBILITY_PROPERTY)) {
pair.first.removeAttribute(SVGConstants.CSS_VISIBILITY_PROPERTY);
}
// if not yet rendered, add a thumbnail
- if(!pair.first.hasChildNodes()) {
- logger.warning("This codepath should no longer be needed.");
+ if (!pair.first.hasChildNodes()) {
+ LOG.warning("This codepath should no longer be needed.");
Visualization visualization = embedOrThumbnail(thumbsize, it, task, pair.first);
vistoelem.put(it, task, pair.first, visualization);
refreshcss = true;
}
- }
- else {
+ } else {
// hide if there is anything to hide.
- if(pair.first != null && pair.first.hasChildNodes()) {
+ if (pair.first != null && pair.first.hasChildNodes()) {
pair.first.setAttribute(SVGConstants.CSS_VISIBILITY_PROPERTY, SVGConstants.CSS_HIDDEN_VALUE);
}
}
@@ -401,7 +399,7 @@ public class OverviewPlot extends SVGPlot implements ResultListener {
}
}
}
- if(refreshcss) {
+ if (refreshcss) {
updateStyleElement();
}
}
@@ -414,15 +412,10 @@ public class OverviewPlot extends SVGPlot implements ResultListener {
* @return visibility
*/
protected boolean visibleInOverview(VisualizationTask task) {
- if(!VisualizerUtil.isVisible(task)) {
- return false;
- }
- if(single) {
- Boolean nothumb = task.getGenerics(VisualizationTask.META_NOEMBED, Boolean.class);
- return (nothumb == null) || !nothumb;
- }
- else {
- return VisualizerUtil.thumbnailEnabled(task);
+ if (single) {
+ return task.visible && !task.noembed;
+ } else {
+ return task.visible && task.thumbnail;
}
}
@@ -483,7 +476,7 @@ public class OverviewPlot extends SVGPlot implements ResultListener {
*/
protected void triggerSubplotSelectEvent(PlotItem it) {
// forward event to all listeners.
- for(ActionListener actionListener : actionListeners) {
+ for (ActionListener actionListener : actionListeners) {
actionListener.actionPerformed(new DetailViewSelectedEvent(this, ActionEvent.ACTION_PERFORMED, null, 0, it));
}
}
@@ -544,11 +537,11 @@ public class OverviewPlot extends SVGPlot implements ResultListener {
* Trigger a redraw, but avoid excessive redraws.
*/
public final void lazyRefresh() {
- logger.debug("Scheduling refresh.");
+ LOG.debug("Scheduling refresh.");
Runnable pr = new Runnable() {
@Override
public void run() {
- if(OverviewPlot.this.pendingRefresh == this) {
+ if (OverviewPlot.this.pendingRefresh == this) {
OverviewPlot.this.refresh();
}
}
@@ -559,8 +552,8 @@ public class OverviewPlot extends SVGPlot implements ResultListener {
@Override
public void resultAdded(Result child, Result parent) {
- logger.debug("result added: " + child);
- if(child instanceof VisualizationTask) {
+ LOG.debug("result added: " + child);
+ if (child instanceof VisualizationTask) {
reinitOnRefresh = true;
}
lazyRefresh();
@@ -568,13 +561,13 @@ public class OverviewPlot extends SVGPlot implements ResultListener {
@Override
public void resultChanged(Result current) {
- logger.debug("result changed: " + current);
+ LOG.debug("result changed: " + current);
lazyRefresh();
}
@Override
public void resultRemoved(Result child, Result parent) {
- logger.debug("result removed: " + child);
+ LOG.debug("result removed: " + child);
lazyRefresh();
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/gui/overview/RectangleArranger.java b/src/de/lmu/ifi/dbs/elki/visualization/gui/overview/RectangleArranger.java
index a70393a5..b5e6031e 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/gui/overview/RectangleArranger.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/gui/overview/RectangleArranger.java
@@ -23,6 +23,8 @@ package de.lmu.ifi.dbs.elki.visualization.gui.overview;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+import gnu.trove.list.array.TDoubleArrayList;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -47,7 +49,7 @@ public class RectangleArranger<T> {
/**
* Logging class
*/
- private static final Logging logger = Logging.getLogger(RectangleArranger.class);
+ private static final Logging LOG = Logging.getLogger(RectangleArranger.class);
/**
* Target height/width ratio
@@ -67,15 +69,15 @@ public class RectangleArranger<T> {
/**
* Column widths
*/
- private ArrayList<Double> widths = new ArrayList<Double>();
+ private TDoubleArrayList widths = new TDoubleArrayList();
/**
* Column heights
*/
- private ArrayList<Double> heights = new ArrayList<Double>();
+ private TDoubleArrayList heights = new TDoubleArrayList();
/**
- * Bit sets to store usage. ArrayList = y, BitSet = x
+ * Map indicating which cells are used.
*/
private ArrayList<ArrayList<Object>> usage = new ArrayList<ArrayList<Object>>();
@@ -120,7 +122,9 @@ public class RectangleArranger<T> {
* @param data Data object to add (key)
*/
public void put(double w, double h, T data) {
- logger.finest("Add: " + w + "x" + h);
+ if(LOG.isDebuggingFinest()) {
+ LOG.finest("Add: " + w + "x" + h);
+ }
final int cols = widths.size();
final int rows = heights.size();
@@ -233,7 +237,9 @@ public class RectangleArranger<T> {
double hinc = Math.max(0.0, h - avh);
double inc = computeIncreaseArea(winc, hinc);
- logger.debugFinest("Candidate: " + sx + "," + sy + " - " + ex + "," + ey + ": " + avw + "x" + avh + " " + inc);
+ if(LOG.isDebuggingFinest()) {
+ LOG.debugFinest("Candidate: " + sx + "," + sy + " - " + ex + "," + ey + ": " + avw + "x" + avh + " " + inc);
+ }
if(inc < bestinc) {
bestinc = inc;
bestsx = sx;
@@ -251,7 +257,9 @@ public class RectangleArranger<T> {
}
assert assertConsistent();
}
- logger.debugFinest("Best: " + bestsx + "," + bestsy + " - " + bestex + "," + bestey + " inc: " + bestwi + "x" + besthi + " " + bestinc);
+ if(LOG.isDebuggingFinest()) {
+ LOG.debugFinest("Best: " + bestsx + "," + bestsy + " - " + bestex + "," + bestey + " inc: " + bestwi + "x" + besthi + " " + bestinc);
+ }
// Need to increase the total area
if(bestinc > 0) {
assert (bestex == cols - 1 || bestey == rows - 1);
@@ -290,7 +298,7 @@ public class RectangleArranger<T> {
}
}
map.put(data, new double[] { xpos, ypos, w, h });
- if(logger.isDebuggingFinest()) {
+ if(LOG.isDebuggingFinest()) {
logSizes();
}
}
@@ -302,25 +310,29 @@ public class RectangleArranger<T> {
}
protected void splitRow(int bestey, double besthi) {
- assert(bestey < heights.size());
- if (heights.get(bestey) - besthi <= Double.MIN_NORMAL) {
+ assert (bestey < heights.size());
+ if(heights.get(bestey) - besthi <= Double.MIN_NORMAL) {
return;
}
- logger.debugFine("Split row " + bestey);
- heights.add(bestey + 1, besthi);
+ if(LOG.isDebuggingFine()) {
+ LOG.debugFine("Split row " + bestey);
+ }
+ heights.insert(bestey + 1, besthi);
heights.set(bestey, heights.get(bestey) - besthi);
// Update used map
usage.add(bestey + 1, new ArrayList<Object>(usage.get(bestey)));
}
protected void splitCol(int bestex, double bestwi) {
- assert(bestex < widths.size());
- if (widths.get(bestex) - bestwi <= Double.MIN_NORMAL) {
+ assert (bestex < widths.size());
+ if(widths.get(bestex) - bestwi <= Double.MIN_NORMAL) {
return;
}
final int rows = heights.size();
- logger.debugFine("Split column " + bestex);
- widths.add(bestex + 1, bestwi);
+ if(LOG.isDebuggingFine()) {
+ LOG.debugFine("Split column " + bestex);
+ }
+ widths.insert(bestex + 1, bestwi);
widths.set(bestex, widths.get(bestex) - bestwi);
// Update used map
for(int y = 0; y < rows; y++) {
@@ -332,9 +344,11 @@ public class RectangleArranger<T> {
private void resize(double inc) {
final int cols = widths.size();
final int rows = heights.size();
- logger.debugFine("Resize by " + inc + "x" + (inc / ratio));
- if(logger.isDebuggingFinest()) {
- logSizes();
+ if(LOG.isDebuggingFine()) {
+ LOG.debugFine("Resize by " + inc + "x" + (inc / ratio));
+ if(LOG.isDebuggingFinest()) {
+ logSizes();
+ }
}
// TODO: if the last row or column is empty, we can do this simpler
widths.add(inc);
@@ -354,7 +368,7 @@ public class RectangleArranger<T> {
usage.add(row);
}
assert assertConsistent();
- if(logger.isDebuggingFinest()) {
+ if(LOG.isDebuggingFinest()) {
logSizes();
}
}
@@ -379,7 +393,7 @@ public class RectangleArranger<T> {
{
double wsum = 0.0;
for(int x = 0; x < cols; x++) {
- assert (widths.get(x) > 0) : "Non-positive width: "+widths.get(x);
+ assert (widths.get(x) > 0) : "Non-positive width: " + widths.get(x);
wsum += widths.get(x);
}
assert (Math.abs(wsum - twidth) < 1E-10);
@@ -387,7 +401,7 @@ public class RectangleArranger<T> {
{
double hsum = 0.0;
for(int y = 0; y < rows; y++) {
- assert (heights.get(y) > 0) : "Non-positive height: "+heights.get(y);
+ assert (heights.get(y) > 0) : "Non-positive height: " + heights.get(y);
hsum += heights.get(y);
}
assert (Math.abs(hsum - theight) < 1E-10);
@@ -401,8 +415,11 @@ public class RectangleArranger<T> {
return true;
}
- public void logSizes() {
- StringBuffer buf = new StringBuffer();
+ /**
+ * Debug logging
+ */
+ protected void logSizes() {
+ StringBuilder buf = new StringBuilder();
final int cols = widths.size();
final int rows = heights.size();
{
@@ -413,7 +430,7 @@ public class RectangleArranger<T> {
}
buf.append(widths.get(x));
}
- buf.append("\n");
+ buf.append('\n');
}
{
buf.append("Heights: ");
@@ -423,7 +440,7 @@ public class RectangleArranger<T> {
}
buf.append(heights.get(y));
}
- buf.append("\n");
+ buf.append('\n');
}
{
for(int y = 0; y < rows; y++) {
@@ -433,11 +450,33 @@ public class RectangleArranger<T> {
buf.append("|\n");
}
for(int x = 0; x < cols; x++) {
- buf.append("-");
+ buf.append('-');
}
buf.append("+\n");
}
- logger.debug(buf);
+ LOG.debug(buf);
+ }
+
+ /**
+ * Compute the relative fill. Useful for triggering a relayout if the relative
+ * fill is not satisfactory.
+ *
+ * @return relative fill
+ */
+ public double relativeFill() {
+ double acc = 0.0;
+ final int cols = widths.size();
+ final int rows = heights.size();
+ {
+ for(int y = 0; y < rows; y++) {
+ for(int x = 0; x < cols; x++) {
+ if(usage.get(y).get(x) != null) {
+ acc += widths.get(x) * heights.get(y);
+ }
+ }
+ }
+ }
+ return acc / (twidth * theight);
}
/**
@@ -482,7 +521,7 @@ public class RectangleArranger<T> {
* @param args
*/
public static void main(String[] args) {
- logger.getWrappedLogger().setLevel(Level.FINEST);
+ LOG.getWrappedLogger().setLevel(Level.FINEST);
RectangleArranger<String> r = new RectangleArranger<String>(1.3);
r.put(4., 1., "Histogram");
r.put(4., 4., "3D view");
@@ -505,4 +544,4 @@ public class RectangleArranger<T> {
r.put(4., 1., "C");
r.put(1., .1, "D");
}
-}
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/gui/overview/package-info.java b/src/de/lmu/ifi/dbs/elki/visualization/gui/overview/package-info.java
index f4690d0a..013f86ca 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/gui/overview/package-info.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/gui/overview/package-info.java
@@ -1,6 +1,7 @@
/**
* <p>Classes for managing the overview plot.</p>
*
+ * @apiviz.exclude java.awt.event.*
*/
/*
This file is part of ELKI:
@@ -24,4 +25,4 @@ GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package de.lmu.ifi.dbs.elki.visualization.gui.overview; \ No newline at end of file
+package de.lmu.ifi.dbs.elki.visualization.gui.overview;
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/opticsplot/OPTICSPlot.java b/src/de/lmu/ifi/dbs/elki/visualization/opticsplot/OPTICSPlot.java
index e0aac5ca..b6b6fb31 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/opticsplot/OPTICSPlot.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/opticsplot/OPTICSPlot.java
@@ -56,7 +56,7 @@ public class OPTICSPlot<D extends Distance<D>> implements Result {
/**
* Logger
*/
- protected static final Logging logger = Logging.getLogger(OPTICSPlot.class);
+ private static final Logging LOG = Logging.getLogger(OPTICSPlot.class);
/**
* Scale to use
@@ -200,7 +200,7 @@ public class OPTICSPlot<D extends Distance<D>> implements Result {
}
}
catch(ArrayIndexOutOfBoundsException e) {
- logger.error("Plotting out of range: " + x + "," + y + " >= " + width + "x" + height);
+ LOG.error("Plotting out of range: " + x + "," + y + " >= " + width + "x" + height);
}
x++;
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/package-info.java b/src/de/lmu/ifi/dbs/elki/visualization/package-info.java
index b331cc0d..f4e854e0 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/package-info.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/package-info.java
@@ -1,6 +1,8 @@
/**
* <p>Visualization package of ELKI.</p>
*
+ * @apiviz.exclude elki.utilities
+ * @apiviz.exclude java.lang.*
*/
/*
This file is part of ELKI:
@@ -24,4 +26,4 @@ GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package de.lmu.ifi.dbs.elki.visualization; \ No newline at end of file
+package de.lmu.ifi.dbs.elki.visualization;
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/projections/AbstractFullProjection.java b/src/de/lmu/ifi/dbs/elki/visualization/projections/AbstractFullProjection.java
index 6b08f033..e8f7a91a 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/projections/AbstractFullProjection.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/projections/AbstractFullProjection.java
@@ -52,12 +52,12 @@ public abstract class AbstractFullProjection extends AbstractProjection implemen
* @return vector in scaled space
*/
@Override
- public Vector projectDataToScaledSpace(NumberVector<?, ?> data) {
+ public Vector projectDataToScaledSpace(NumberVector<?> data) {
final int dim = data.getDimensionality();
Vector vec = new Vector(dim);
double[] ds = vec.getArrayRef();
for(int d = 0; d < dim; d++) {
- ds[d] = scales[d].getScaled(data.doubleValue(d + 1));
+ ds[d] = scales[d].getScaled(data.doubleValue(d));
}
return vec;
}
@@ -86,12 +86,12 @@ public abstract class AbstractFullProjection extends AbstractProjection implemen
* @return relative vector in scaled space
*/
@Override
- public Vector projectRelativeDataToScaledSpace(NumberVector<?, ?> data) {
+ public Vector projectRelativeDataToScaledSpace(NumberVector<?> data) {
final int dim = data.getDimensionality();
Vector vec = new Vector(dim);
double[] ds = vec.getArrayRef();
for(int d = 0; d < dim; d++) {
- ds[d] = scales[d].getRelativeScaled(data.doubleValue(d + 1));
+ ds[d] = scales[d].getRelativeScaled(data.doubleValue(d));
}
return vec;
}
@@ -120,7 +120,7 @@ public abstract class AbstractFullProjection extends AbstractProjection implemen
* @return vector in rendering space
*/
@Override
- public Vector projectDataToRenderSpace(NumberVector<?, ?> data) {
+ public Vector projectDataToRenderSpace(NumberVector<?> data) {
return projectScaledToRender(projectDataToScaledSpace(data));
}
@@ -142,7 +142,7 @@ public abstract class AbstractFullProjection extends AbstractProjection implemen
* @return relative vector in rendering space
*/
@Override
- public Vector projectRelativeDataToRenderSpace(NumberVector<?, ?> data) {
+ public Vector projectRelativeDataToRenderSpace(NumberVector<?> data) {
return projectRelativeScaledToRender(projectRelativeDataToScaledSpace(data));
}
@@ -166,7 +166,7 @@ public abstract class AbstractFullProjection extends AbstractProjection implemen
* @return vector in data space
*/
@Override
- public <NV extends NumberVector<NV, ?>> NV projectScaledToDataSpace(Vector v, NV factory) {
+ public <NV extends NumberVector<?>> NV projectScaledToDataSpace(Vector v, NumberVector.Factory<NV, ?> factory) {
final int dim = v.getDimensionality();
Vector vec = v.copy();
double[] ds = vec.getArrayRef();
@@ -185,7 +185,7 @@ public abstract class AbstractFullProjection extends AbstractProjection implemen
* @return vector in data space
*/
@Override
- public <NV extends NumberVector<NV, ?>> NV projectRenderToDataSpace(Vector v, NV prototype) {
+ public <NV extends NumberVector<?>> NV projectRenderToDataSpace(Vector v, NumberVector.Factory<NV, ?> prototype) {
final int dim = v.getDimensionality();
Vector vec = projectRenderToScaled(v);
double[] ds = vec.getArrayRef();
@@ -206,7 +206,7 @@ public abstract class AbstractFullProjection extends AbstractProjection implemen
* @return relative vector in data space
*/
@Override
- public <NV extends NumberVector<NV, ?>> NV projectRelativeScaledToDataSpace(Vector v, NV prototype) {
+ public <NV extends NumberVector<?>> NV projectRelativeScaledToDataSpace(Vector v, NumberVector.Factory<NV, ?> prototype) {
final int dim = v.getDimensionality();
Vector vec = v.copy();
double[] ds = vec.getArrayRef();
@@ -225,7 +225,7 @@ public abstract class AbstractFullProjection extends AbstractProjection implemen
* @return relative vector in data space
*/
@Override
- public <NV extends NumberVector<NV, ?>> NV projectRelativeRenderToDataSpace(Vector v, NV prototype) {
+ public <NV extends NumberVector<?>> NV projectRelativeRenderToDataSpace(Vector v, NumberVector.Factory<NV, ?> prototype) {
final int dim = v.getDimensionality();
Vector vec = projectRelativeRenderToScaled(v);
double[] ds = vec.getArrayRef();
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/projections/AffineProjection.java b/src/de/lmu/ifi/dbs/elki/visualization/projections/AffineProjection.java
index 10533ee0..0e184e32 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/projections/AffineProjection.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/projections/AffineProjection.java
@@ -109,7 +109,7 @@ public class AffineProjection extends AbstractFullProjection implements Projecti
@Override
public CanvasSize estimateViewport() {
- if(viewport == null) {
+ if (viewport == null) {
final int dim = proj.getDimensionality();
DoubleMinMax minmaxx = new DoubleMinMax();
DoubleMinMax minmaxy = new DoubleMinMax();
@@ -121,14 +121,14 @@ public class AffineProjection extends AbstractFullProjection implements Projecti
minmaxy.put(orig.get(1));
// Diagonal point
Vector diag = new Vector(dim);
- for(int d2 = 0; d2 < dim; d2++) {
+ for (int d2 = 0; d2 < dim; d2++) {
diag.set(d2, 1);
}
diag = projectScaledToRender(diag);
minmaxx.put(diag.get(0));
minmaxy.put(diag.get(1));
// Axis end points
- for(int d = 0; d < dim; d++) {
+ for (int d = 0; d < dim; d++) {
Vector v = new Vector(dim);
v.set(d, 1);
Vector ax = projectScaledToRender(v);
@@ -154,7 +154,7 @@ public class AffineProjection extends AbstractFullProjection implements Projecti
AffineTransformation proj = AffineTransformation.reorderAxesTransformation(dim, new int[] { ax1, ax2 });
// Assuming that the data was normalized on [0:1], center it:
double[] trans = new double[dim];
- for(int i = 0; i < dim; i++) {
+ for (int i = 0; i < dim; i++) {
trans[i] = -.5;
}
proj.addTranslation(new Vector(trans));
@@ -173,7 +173,7 @@ public class AffineProjection extends AbstractFullProjection implements Projecti
}
@Override
- public double[] fastProjectDataToRenderSpace(NumberVector<?, ?> data) {
+ public double[] fastProjectDataToRenderSpace(NumberVector<?> data) {
return fastProjectScaledToRenderSpace(fastProjectDataToScaledSpace(data));
}
@@ -184,7 +184,7 @@ public class AffineProjection extends AbstractFullProjection implements Projecti
}
@Override
- public double[] fastProjectDataToScaledSpace(NumberVector<?, ?> data) {
+ public double[] fastProjectDataToScaledSpace(NumberVector<?> data) {
// FIXME: implement with less objects?
return projectDataToScaledSpace(data).getArrayRef();
}
@@ -201,7 +201,7 @@ public class AffineProjection extends AbstractFullProjection implements Projecti
final double[] cols = matrix[vr.length];
assert (colx.length == coly.length && colx.length == cols.length && cols.length == vr.length + 1);
- for(int k = 0; k < vr.length; k++) {
+ for (int k = 0; k < vr.length; k++) {
x += colx[k] * vr[k];
y += coly[k] * vr[k];
s += cols[k] * vr[k];
@@ -210,7 +210,7 @@ public class AffineProjection extends AbstractFullProjection implements Projecti
x += colx[vr.length];
y += coly[vr.length];
s += cols[vr.length];
- assert (s != 0.0);
+ assert (s > 0.0 || s < 0.0);
return new double[] { x / s, y / s };
}
@@ -221,7 +221,7 @@ public class AffineProjection extends AbstractFullProjection implements Projecti
}
@Override
- public double[] fastProjectRelativeDataToRenderSpace(NumberVector<?, ?> data) {
+ public double[] fastProjectRelativeDataToRenderSpace(NumberVector<?> data) {
// FIXME: implement with less objects?
return fastProjectRelativeScaledToRenderSpace(projectRelativeDataToScaledSpace(data).getArrayRef());
}
@@ -236,7 +236,7 @@ public class AffineProjection extends AbstractFullProjection implements Projecti
final double[] coly = matrix[1];
assert (colx.length == coly.length);
- for(int k = 0; k < vr.length; k++) {
+ for (int k = 0; k < vr.length; k++) {
x += colx[k] * vr[k];
y += coly[k] * vr[k];
}
@@ -246,7 +246,7 @@ public class AffineProjection extends AbstractFullProjection implements Projecti
@Override
public double[] fastProjectRenderToDataSpace(double[] data) {
double[] ret = fastProjectRenderToScaledSpace(data);
- for(int d = 0; d < scales.length; d++) {
+ for (int d = 0; d < scales.length; d++) {
ret[d] = scales[d].getUnscaled(ret[d]);
}
return ret;
@@ -254,11 +254,11 @@ public class AffineProjection extends AbstractFullProjection implements Projecti
@Override
public double[] fastProjectRenderToScaledSpace(double[] v) {
- if(v.length == scales.length) {
+ if (v.length == scales.length) {
return projectRenderToScaled(new Vector(v)).getArrayRef();
}
double[] c = Arrays.copyOf(v, scales.length);
- for(int d = v.length; d < scales.length; d++) {
+ for (int d = v.length; d < scales.length; d++) {
c[d] = 0.5;
}
return projectRenderToScaled(new Vector(c)).getArrayRef();
@@ -269,16 +269,16 @@ public class AffineProjection extends AbstractFullProjection implements Projecti
final int dim = proj.getDimensionality();
BitSet actDim = new BitSet(dim);
Vector vScale = new Vector(dim);
- for(int d = 0; d < dim; d++) {
+ for (int d = 0; d < dim; d++) {
vScale.setZero();
vScale.set(d, 1);
double[] vRender = fastProjectScaledToRenderSpace(vScale.getArrayRef());
// TODO: Can't we do this by inspecting the projection matrix directly?
- if(vRender[0] != 0.0 || vRender[1] != 0) {
+ if (vRender[0] > 0.0 || vRender[0] < 0.0 || vRender[1] != 0) {
actDim.set(d);
}
}
return actDim;
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/projections/FullProjection.java b/src/de/lmu/ifi/dbs/elki/visualization/projections/FullProjection.java
index cc1433aa..02705355 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/projections/FullProjection.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/projections/FullProjection.java
@@ -24,7 +24,6 @@ package de.lmu.ifi.dbs.elki.visualization.projections;
*/
import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector;
-import de.lmu.ifi.dbs.elki.visualization.projections.Projection;
/**
* Full vector space projections.
@@ -44,7 +43,7 @@ public interface FullProjection extends Projection {
* @param v vector in scaled space
* @return vector in rendering space
*/
- public Vector projectScaledToRender(Vector v);
+ Vector projectScaledToRender(Vector v);
/**
* Project a vector from rendering space to scaled space.
@@ -52,7 +51,7 @@ public interface FullProjection extends Projection {
* @param v vector in rendering space
* @return vector in scaled space
*/
- public Vector projectRenderToScaled(Vector v);
+ Vector projectRenderToScaled(Vector v);
/**
* Project a relative vector from scaled space to rendering space.
@@ -60,7 +59,7 @@ public interface FullProjection extends Projection {
* @param v relative vector in scaled space
* @return relative vector in rendering space
*/
- public Vector projectRelativeScaledToRender(Vector v);
+ Vector projectRelativeScaledToRender(Vector v);
/**
* Project a relative vector from rendering space to scaled space.
@@ -68,7 +67,7 @@ public interface FullProjection extends Projection {
* @param v relative vector in rendering space
* @return relative vector in scaled space
*/
- public Vector projectRelativeRenderToScaled(Vector v);
+ Vector projectRelativeRenderToScaled(Vector v);
/**
* Project a data vector from data space to scaled space.
@@ -76,7 +75,7 @@ public interface FullProjection extends Projection {
* @param data vector in data space
* @return vector in scaled space
*/
- public Vector projectDataToScaledSpace(NumberVector<?, ?> data);
+ Vector projectDataToScaledSpace(NumberVector<?> data);
/**
* Project a data vector from data space to scaled space.
@@ -84,7 +83,7 @@ public interface FullProjection extends Projection {
* @param data vector in data space
* @return vector in scaled space
*/
- public Vector projectDataToScaledSpace(Vector data);
+ Vector projectDataToScaledSpace(Vector data);
/**
* Project a relative data vector from data space to scaled space.
@@ -92,7 +91,7 @@ public interface FullProjection extends Projection {
* @param data relative vector in data space
* @return relative vector in scaled space
*/
- public Vector projectRelativeDataToScaledSpace(NumberVector<?, ?> data);
+ Vector projectRelativeDataToScaledSpace(NumberVector<?> data);
/**
* Project a relative data vector from data space to scaled space.
@@ -100,7 +99,7 @@ public interface FullProjection extends Projection {
* @param data relative vector in data space
* @return relative vector in scaled space
*/
- public Vector projectRelativeDataToScaledSpace(Vector data);
+ Vector projectRelativeDataToScaledSpace(Vector data);
/**
* Project a data vector from data space to rendering space.
@@ -108,7 +107,7 @@ public interface FullProjection extends Projection {
* @param data vector in data space
* @return vector in rendering space
*/
- public Vector projectDataToRenderSpace(NumberVector<?, ?> data);
+ Vector projectDataToRenderSpace(NumberVector<?> data);
/**
* Project a data vector from data space to rendering space.
@@ -116,7 +115,7 @@ public interface FullProjection extends Projection {
* @param data vector in data space
* @return vector in rendering space
*/
- public Vector projectDataToRenderSpace(Vector data);
+ Vector projectDataToRenderSpace(Vector data);
/**
* Project a vector from scaled space to data space.
@@ -126,7 +125,7 @@ public interface FullProjection extends Projection {
* @param factory Object factory
* @return vector in data space
*/
- public <NV extends NumberVector<NV, ?>> NV projectScaledToDataSpace(Vector v, NV factory);
+ <NV extends NumberVector<?>> NV projectScaledToDataSpace(Vector v, NumberVector.Factory<NV, ?> factory);
/**
* Project a vector from rendering space to data space.
@@ -136,7 +135,7 @@ public interface FullProjection extends Projection {
* @param prototype Object factory
* @return vector in data space
*/
- public <NV extends NumberVector<NV, ?>> NV projectRenderToDataSpace(Vector v, NV prototype);
+ <NV extends NumberVector<?>> NV projectRenderToDataSpace(Vector v, NumberVector.Factory<NV, ?> prototype);
/**
* Project a relative data vector from data space to rendering space.
@@ -144,7 +143,7 @@ public interface FullProjection extends Projection {
* @param data relative vector in data space
* @return relative vector in rendering space
*/
- public Vector projectRelativeDataToRenderSpace(NumberVector<?, ?> data);
+ Vector projectRelativeDataToRenderSpace(NumberVector<?> data);
/**
* Project a relative data vector from data space to rendering space.
@@ -152,7 +151,7 @@ public interface FullProjection extends Projection {
* @param data relative vector in data space
* @return relative vector in rendering space
*/
- public Vector projectRelativeDataToRenderSpace(Vector data);
+ Vector projectRelativeDataToRenderSpace(Vector data);
/**
* Project a relative vector from scaled space to data space.
@@ -162,7 +161,7 @@ public interface FullProjection extends Projection {
* @param prototype Object factory
* @return relative vector in data space
*/
- public <NV extends NumberVector<NV, ?>> NV projectRelativeScaledToDataSpace(Vector v, NV prototype);
+ <NV extends NumberVector<?>> NV projectRelativeScaledToDataSpace(Vector v, NumberVector.Factory<NV, ?> prototype);
/**
* Project a relative vector from rendering space to data space.
@@ -172,5 +171,5 @@ public interface FullProjection extends Projection {
* @param prototype Object factory
* @return relative vector in data space
*/
- public <NV extends NumberVector<NV, ?>> NV projectRelativeRenderToDataSpace(Vector v, NV prototype);
+ <NV extends NumberVector<?>> NV projectRelativeRenderToDataSpace(Vector v, NumberVector.Factory<NV, ?> prototype);
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/projections/OPTICSProjection.java b/src/de/lmu/ifi/dbs/elki/visualization/projections/OPTICSProjection.java
new file mode 100644
index 00000000..9a6dd9b8
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/visualization/projections/OPTICSProjection.java
@@ -0,0 +1,94 @@
+package de.lmu.ifi.dbs.elki.visualization.projections;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
+import de.lmu.ifi.dbs.elki.math.scales.LinearScale;
+import de.lmu.ifi.dbs.elki.result.AbstractHierarchicalResult;
+import de.lmu.ifi.dbs.elki.result.optics.ClusterOrderResult;
+import de.lmu.ifi.dbs.elki.visualization.VisualizerContext;
+import de.lmu.ifi.dbs.elki.visualization.opticsplot.OPTICSPlot;
+import de.lmu.ifi.dbs.elki.visualization.projector.OPTICSProjector;
+
+/**
+ * OPTICS projection. This is not really needed, but a quick hack to have more
+ * consistency in the visualizer API.
+ *
+ * @author Erich Schubert
+ */
+public class OPTICSProjection<D extends Distance<D>> extends AbstractHierarchicalResult implements Projection {
+ /**
+ * The projector we were generated from.
+ */
+ OPTICSProjector<D> projector;
+
+ /**
+ * Constructor.
+ *
+ * @param opticsProjector OPTICS projector
+ */
+ public OPTICSProjection(OPTICSProjector<D> opticsProjector) {
+ super();
+ this.projector = opticsProjector;
+ }
+
+ @Override
+ public String getLongName() {
+ return "OPTICS projection";
+ }
+
+ @Override
+ public String getShortName() {
+ return "OPTICSproj";
+ }
+
+ @Override
+ public int getInputDimensionality() {
+ return -1;
+ }
+
+ @Override
+ public LinearScale getScale(int d) {
+ return null;
+ }
+
+ /**
+ * Get or produce the actual OPTICS plot.
+ *
+ * @param context Context to use
+ * @return Plot
+ */
+ public OPTICSPlot<D> getOPTICSPlot(VisualizerContext context) {
+ return projector.getOPTICSPlot(context);
+ }
+
+ /**
+ * Get the OPTICS cluster order.
+ *
+ * @return Cluster oder result.
+ */
+ public ClusterOrderResult<D> getResult() {
+ return projector.getResult();
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/projections/Projection1D.java b/src/de/lmu/ifi/dbs/elki/visualization/projections/Projection1D.java
index 2232e0b9..82c9483d 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/projections/Projection1D.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/projections/Projection1D.java
@@ -48,7 +48,7 @@ public interface Projection1D extends Projection {
* @param data vector in data space
* @return vector in rendering space
*/
- public double fastProjectDataToRenderSpace(NumberVector<?, ?> data);
+ public double fastProjectDataToRenderSpace(NumberVector<?> data);
/**
* Project a vector from scaled space to rendering space.
@@ -72,7 +72,7 @@ public interface Projection1D extends Projection {
* @param data vector in data space
* @return vector in rendering space
*/
- public double fastProjectRelativeDataToRenderSpace(NumberVector<?, ?> data);
+ public double fastProjectRelativeDataToRenderSpace(NumberVector<?> data);
/**
* Project a vector from scaled space to rendering space.
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/projections/Projection2D.java b/src/de/lmu/ifi/dbs/elki/visualization/projections/Projection2D.java
index 66518c8f..acd4a829 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/projections/Projection2D.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/projections/Projection2D.java
@@ -34,6 +34,8 @@ import de.lmu.ifi.dbs.elki.data.NumberVector;
* @author Erich Schubert
*
* @apiviz.landmark
+ *
+ * @apiviz.has CanvasSize
*/
public interface Projection2D extends Projection {
/**
@@ -50,7 +52,7 @@ public interface Projection2D extends Projection {
* @param data vector in data space
* @return vector in rendering space
*/
- public double[] fastProjectDataToRenderSpace(NumberVector<?, ?> data);
+ public double[] fastProjectDataToRenderSpace(NumberVector<?> data);
/**
* Project a data vector from data space to scaled space.
@@ -66,7 +68,7 @@ public interface Projection2D extends Projection {
* @param data vector in data space
* @return vector in scaled space
*/
- public double[] fastProjectDataToScaledSpace(NumberVector<?, ?> data);
+ public double[] fastProjectDataToScaledSpace(NumberVector<?> data);
/**
* Project a vector from scaled space to rendering space.
@@ -91,7 +93,7 @@ public interface Projection2D extends Projection {
* @param prototype Prototype to create vector from
* @return vector in data space
*/
- // public <V extends NumberVector<V, ?>> V fastProjectRenderToDataSpace(double[] data, V prototype);
+ // public <V extends NumberVector<?>> V fastProjectRenderToDataSpace(double[] data, V prototype);
/**
* Project a vector from rendering space to scaled space.
@@ -115,7 +117,7 @@ public interface Projection2D extends Projection {
* @param data vector in data space
* @return vector in rendering space
*/
- public double[] fastProjectRelativeDataToRenderSpace(NumberVector<?, ?> data);
+ public double[] fastProjectRelativeDataToRenderSpace(NumberVector<?> data);
/**
* Project a vector from scaled space to rendering space.
@@ -140,4 +142,4 @@ public interface Projection2D extends Projection {
* @return Bit set, first dimension is bit 0.
*/
public BitSet getVisibleDimensions2D();
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/projections/ProjectionParallel.java b/src/de/lmu/ifi/dbs/elki/visualization/projections/ProjectionParallel.java
index 03e8a245..98c2ac33 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/projections/ProjectionParallel.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/projections/ProjectionParallel.java
@@ -166,7 +166,7 @@ public interface ProjectionParallel extends Projection {
* @param v Input vector
* @return Vector with reordering, inversions and scales applied.
*/
- public double[] fastProjectDataToRenderSpace(NumberVector<?, ?> v);
+ public double[] fastProjectDataToRenderSpace(NumberVector<?> v);
/**
* Project the value of a single axis to its display value
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/projections/Simple1D.java b/src/de/lmu/ifi/dbs/elki/visualization/projections/Simple1D.java
index b701c934..ef97fe15 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/projections/Simple1D.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/projections/Simple1D.java
@@ -42,11 +42,11 @@ public class Simple1D extends AbstractSimpleProjection implements Projection1D {
* Simple 1D projection using scaling only.
*
* @param scales Scales to use
- * @param dnum Dimension (starting at 1)
+ * @param dnum Dimension (starting at 0)
*/
public Simple1D(LinearScale[] scales, int dnum) {
super(scales);
- this.dnum = dnum - 1;
+ this.dnum = dnum;
}
@Override
@@ -55,8 +55,8 @@ public class Simple1D extends AbstractSimpleProjection implements Projection1D {
}
@Override
- public double fastProjectDataToRenderSpace(NumberVector<?, ?> data) {
- return (scales[dnum].getScaled(data.doubleValue(dnum + 1)) - 0.5) * SCALE;
+ public double fastProjectDataToRenderSpace(NumberVector<?> data) {
+ return (scales[dnum].getScaled(data.doubleValue(dnum)) - 0.5) * SCALE;
}
@Override
@@ -70,7 +70,7 @@ public class Simple1D extends AbstractSimpleProjection implements Projection1D {
}
@Override
- public double fastProjectRelativeDataToRenderSpace(NumberVector<?, ?> data) {
+ public double fastProjectRelativeDataToRenderSpace(NumberVector<?> data) {
return (data.doubleValue(dnum) - 0.5) * SCALE;
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/projections/Simple2D.java b/src/de/lmu/ifi/dbs/elki/visualization/projections/Simple2D.java
index 1964f12b..81821a7c 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/projections/Simple2D.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/projections/Simple2D.java
@@ -54,8 +54,8 @@ public class Simple2D extends AbstractSimpleProjection implements Projection2D {
*/
public Simple2D(LinearScale[] scales, int ax1, int ax2) {
super(scales);
- this.dim1 = ax1 - 1;
- this.dim2 = ax2 - 1;
+ this.dim1 = ax1;
+ this.dim2 = ax2;
}
@Override
@@ -66,9 +66,9 @@ public class Simple2D extends AbstractSimpleProjection implements Projection2D {
}
@Override
- public double[] fastProjectDataToRenderSpace(NumberVector<?, ?> data) {
- double x = (scales[dim1].getScaled(data.doubleValue(dim1 + 1)) - 0.5) * SCALE;
- double y = (scales[dim2].getScaled(data.doubleValue(dim2 + 1)) - 0.5) * -SCALE;
+ public double[] fastProjectDataToRenderSpace(NumberVector<?> data) {
+ double x = (scales[dim1].getScaled(data.doubleValue(dim1)) - 0.5) * SCALE;
+ double y = (scales[dim2].getScaled(data.doubleValue(dim2)) - 0.5) * -SCALE;
return new double[] { x, y };
}
@@ -83,11 +83,11 @@ public class Simple2D extends AbstractSimpleProjection implements Projection2D {
}
@Override
- public double[] fastProjectDataToScaledSpace(NumberVector<?, ?> data) {
+ public double[] fastProjectDataToScaledSpace(NumberVector<?> data) {
final int dim = data.getDimensionality();
double[] ds = new double[dim];
for(int d = 0; d < dim; d++) {
- ds[d] = scales[d].getScaled(data.doubleValue(d + 1));
+ ds[d] = scales[d].getScaled(data.doubleValue(d));
}
return ds;
}
@@ -141,9 +141,9 @@ public class Simple2D extends AbstractSimpleProjection implements Projection2D {
}
@Override
- public double[] fastProjectRelativeDataToRenderSpace(NumberVector<?, ?> data) {
- double x = scales[dim1].getRelativeScaled(data.doubleValue(dim1 + 1)) * SCALE;
- double y = scales[dim2].getRelativeScaled(data.doubleValue(dim2 + 1)) * -SCALE;
+ public double[] fastProjectRelativeDataToRenderSpace(NumberVector<?> data) {
+ double x = scales[dim1].getRelativeScaled(data.doubleValue(dim1)) * SCALE;
+ double y = scales[dim2].getRelativeScaled(data.doubleValue(dim2)) * -SCALE;
return new double[] { x, y };
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/projections/SimpleParallel.java b/src/de/lmu/ifi/dbs/elki/visualization/projections/SimpleParallel.java
index c843d61d..6b07895d 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/projections/SimpleParallel.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/projections/SimpleParallel.java
@@ -182,12 +182,13 @@ public class SimpleParallel extends BasicResult implements ProjectionParallel {
@Override
public int getDimForVisibleAxis(int pos) {
for(int i = 0; i < scales.length; i++) {
- if (isAxisVisible(i)) {
- if (pos == 0) {
- return dimOrder[i];
- }
- pos--;
+ if (isDimHidden(dimOrder[i])) {
+ continue;
+ }
+ if (pos == 0) {
+ return dimOrder[i];
}
+ pos--;
}
return -1;
}
@@ -214,14 +215,14 @@ public class SimpleParallel extends BasicResult implements ProjectionParallel {
}
@Override
- public double[] fastProjectDataToRenderSpace(NumberVector<?, ?> data) {
+ public double[] fastProjectDataToRenderSpace(NumberVector<?> data) {
double[] v = new double[visDims];
for(int j = 0, o = 0; j < scales.length; j++) {
if(isDimHidden(j)) {
continue;
}
int i = dimOrder[j];
- v[o] = scales[i].getScaled(data.doubleValue(i + 1));
+ v[o] = scales[i].getScaled(data.doubleValue(i));
if(!isDimInverted(i)) {
v[o] = 1 - v[o];
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/projector/HistogramFactory.java b/src/de/lmu/ifi/dbs/elki/visualization/projector/HistogramFactory.java
index 3f487e0e..24defc7b 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/projector/HistogramFactory.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/projector/HistogramFactory.java
@@ -28,10 +28,10 @@ import java.util.ArrayList;
import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterEqualConstraint;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
@@ -46,7 +46,7 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
*/
public class HistogramFactory implements ProjectorFactory {
/**
- * Maximum dimensionality
+ * Maximum dimensionality.
*/
private int maxdim = ScatterPlotFactory.MAX_DIMENSIONS_DEFAULT;
@@ -66,9 +66,9 @@ public class HistogramFactory implements ProjectorFactory {
for(Relation<?> rel : rels) {
if(TypeUtil.NUMBER_VECTOR_FIELD.isAssignableFromType(rel.getDataTypeInformation())) {
@SuppressWarnings("unchecked")
- Relation<NumberVector<?, ?>> vrel = (Relation<NumberVector<?, ?>>) rel;
- final int dim = DatabaseUtil.dimensionality(vrel);
- HistogramProjector<NumberVector<?, ?>> proj = new HistogramProjector<NumberVector<?, ?>>(vrel, Math.min(dim, maxdim));
+ Relation<NumberVector<?>> vrel = (Relation<NumberVector<?>>) rel;
+ final int dim = RelationUtil.dimensionality(vrel);
+ HistogramProjector<NumberVector<?>> proj = new HistogramProjector<NumberVector<?>>(vrel, Math.min(dim, maxdim));
baseResult.getHierarchy().add(vrel, proj);
}
}
@@ -90,9 +90,10 @@ public class HistogramFactory implements ProjectorFactory {
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- IntParameter maxdimP = new IntParameter(ScatterPlotFactory.Parameterizer.MAXDIM_ID, new GreaterEqualConstraint(1), ScatterPlotFactory.MAX_DIMENSIONS_DEFAULT);
+ IntParameter maxdimP = new IntParameter(ScatterPlotFactory.Parameterizer.MAXDIM_ID, ScatterPlotFactory.MAX_DIMENSIONS_DEFAULT);
+ maxdimP.addConstraint(new GreaterEqualConstraint(1));
if(config.grab(maxdimP)) {
- maxdim = maxdimP.getValue();
+ maxdim = maxdimP.intValue();
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/projector/HistogramProjector.java b/src/de/lmu/ifi/dbs/elki/visualization/projector/HistogramProjector.java
index 030ea954..9153476b 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/projector/HistogramProjector.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/projector/HistogramProjector.java
@@ -29,15 +29,15 @@ import java.util.List;
import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.result.AbstractHierarchicalResult;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
import de.lmu.ifi.dbs.elki.result.ScalesResult;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
import de.lmu.ifi.dbs.elki.visualization.gui.overview.PlotItem;
import de.lmu.ifi.dbs.elki.visualization.projections.Projection1D;
import de.lmu.ifi.dbs.elki.visualization.projections.Simple1D;
-import de.lmu.ifi.dbs.elki.visualization.visualizers.visunproj.LabelVisFactory;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.visunproj.LabelVisualization;
/**
* ScatterPlotProjector is responsible for producing a set of scatterplot
@@ -50,14 +50,14 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.visunproj.LabelVisFactory;
*
* @param <V> Vector type
*/
-public class HistogramProjector<V extends NumberVector<?, ?>> extends AbstractHierarchicalResult implements Projector {
+public class HistogramProjector<V extends NumberVector<?>> extends AbstractHierarchicalResult implements Projector {
/**
- * Relation we project
+ * Relation we project.
*/
Relation<V> rel;
/**
- * Database dimensionality
+ * Database dimensionality.
*/
int dmax;
@@ -71,7 +71,7 @@ public class HistogramProjector<V extends NumberVector<?, ?>> extends AbstractHi
super();
this.rel = rel;
this.dmax = maxdim;
- assert (maxdim <= DatabaseUtil.dimensionality(rel)) : "Requested dimensionality larger than data dimensionality?!?";
+ assert (maxdim <= RelationUtil.dimensionality(rel)) : "Requested dimensionality larger than data dimensionality?!?";
}
@Override
@@ -85,7 +85,7 @@ public class HistogramProjector<V extends NumberVector<?, ?>> extends AbstractHi
PlotItem master = new PlotItem(dmax + xoff, hheight + lheight, null);
ScalesResult scales = ResultUtil.getScalesResult(rel);
for(int d1 = 0; d1 < dmax; d1++) {
- Projection1D proj = new Simple1D(scales.getScales(), d1 + 1);
+ Projection1D proj = new Simple1D(scales.getScales(), d1);
final PlotItem it = new PlotItem(d1 + xoff, lheight, 1., hheight, proj);
it.tasks = tasks;
master.subitems.add(it);
@@ -94,11 +94,11 @@ public class HistogramProjector<V extends NumberVector<?, ?>> extends AbstractHi
// Add labels
for(int d1 = 0; d1 < dmax; d1++) {
PlotItem it = new PlotItem(d1 + xoff, 0, 1., lheight, null);
- LabelVisFactory lbl = new LabelVisFactory(DatabaseUtil.getColumnLabel(rel, d1 + 1));
+ LabelVisualization lbl = new LabelVisualization(RelationUtil.getColumnLabel(rel, d1));
final VisualizationTask task = new VisualizationTask("", null, null, lbl);
task.height = lheight;
task.width = 1;
- task.put(VisualizationTask.META_NODETAIL, true);
+ task.nodetail = true;
it.tasks.add(task);
master.subitems.add(it);
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/projector/OPTICSProjector.java b/src/de/lmu/ifi/dbs/elki/visualization/projector/OPTICSProjector.java
index 297b7b12..5aa43cd9 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/projector/OPTICSProjector.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/projector/OPTICSProjector.java
@@ -35,6 +35,7 @@ import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
import de.lmu.ifi.dbs.elki.visualization.VisualizerContext;
import de.lmu.ifi.dbs.elki.visualization.gui.overview.PlotItem;
import de.lmu.ifi.dbs.elki.visualization.opticsplot.OPTICSPlot;
+import de.lmu.ifi.dbs.elki.visualization.projections.OPTICSProjection;
/**
* Projection for OPTICS plots.
@@ -77,7 +78,7 @@ public class OPTICSProjector<D extends Distance<D>> extends AbstractHierarchical
List<PlotItem> col = new ArrayList<PlotItem>(1);
List<VisualizationTask> tasks = ResultUtil.filterResults(this, VisualizationTask.class);
if (tasks.size() > 0) {
- final PlotItem it = new PlotItem(4., 1., null);
+ final PlotItem it = new PlotItem(4., 1., new OPTICSProjection<D>(this));
it.tasks = tasks;
col.add(it);
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/projector/ParallelPlotFactory.java b/src/de/lmu/ifi/dbs/elki/visualization/projector/ParallelPlotFactory.java
index 8b110967..23ba6d3f 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/projector/ParallelPlotFactory.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/projector/ParallelPlotFactory.java
@@ -54,8 +54,8 @@ public class ParallelPlotFactory implements ProjectorFactory {
// TODO: multi-relational parallel plots
if(TypeUtil.NUMBER_VECTOR_FIELD.isAssignableFromType(rel.getDataTypeInformation())) {
@SuppressWarnings("unchecked")
- Relation<NumberVector<?, ?>> vrel = (Relation<NumberVector<?, ?>>) rel;
- ParallelPlotProjector<NumberVector<?, ?>> proj = new ParallelPlotProjector<NumberVector<?, ?>>(vrel);
+ Relation<NumberVector<?>> vrel = (Relation<NumberVector<?>>) rel;
+ ParallelPlotProjector<NumberVector<?>> proj = new ParallelPlotProjector<NumberVector<?>>(vrel);
baseResult.getHierarchy().add(vrel, proj);
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/projector/ParallelPlotProjector.java b/src/de/lmu/ifi/dbs/elki/visualization/projector/ParallelPlotProjector.java
index 2076655b..3e172ff9 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/projector/ParallelPlotProjector.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/projector/ParallelPlotProjector.java
@@ -47,9 +47,9 @@ import de.lmu.ifi.dbs.elki.visualization.projections.SimpleParallel;
* @param <V> Vector type
*/
// TODO: support categorical features, and multiple relations too
-public class ParallelPlotProjector<V extends NumberVector<?, ?>> extends AbstractHierarchicalResult implements Projector {
+public class ParallelPlotProjector<V extends NumberVector<?>> extends AbstractHierarchicalResult implements Projector {
/**
- * Relation we project
+ * Relation we project.
*/
Relation<V> rel;
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/projector/ScatterPlotFactory.java b/src/de/lmu/ifi/dbs/elki/visualization/projector/ScatterPlotFactory.java
index 6e1a899b..0367d54b 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/projector/ScatterPlotFactory.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/projector/ScatterPlotFactory.java
@@ -28,10 +28,10 @@ import java.util.ArrayList;
import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterEqualConstraint;
@@ -74,9 +74,9 @@ public class ScatterPlotFactory implements ProjectorFactory {
for(Relation<?> rel : rels) {
if(TypeUtil.NUMBER_VECTOR_FIELD.isAssignableFromType(rel.getDataTypeInformation())) {
@SuppressWarnings("unchecked")
- Relation<NumberVector<?, ?>> vrel = (Relation<NumberVector<?, ?>>) rel;
- final int dim = DatabaseUtil.dimensionality(vrel);
- ScatterPlotProjector<NumberVector<?, ?>> proj = new ScatterPlotProjector<NumberVector<?, ?>>(vrel, Math.min(maxdim, dim));
+ Relation<NumberVector<?>> vrel = (Relation<NumberVector<?>>) rel;
+ final int dim = RelationUtil.dimensionality(vrel);
+ ScatterPlotProjector<NumberVector<?>> proj = new ScatterPlotProjector<NumberVector<?>>(vrel, Math.min(maxdim, dim));
baseResult.getHierarchy().add(vrel, proj);
}
}
@@ -91,13 +91,13 @@ public class ScatterPlotFactory implements ProjectorFactory {
*/
public static class Parameterizer extends AbstractParameterizer {
/**
- * Parameter for the maximum number of dimensions,
+ * Parameter for the maximum number of dimensions.
*
* <p>
* Code: -vis.maxdim
* </p>
*/
- public static final OptionID MAXDIM_ID = OptionID.getOrCreateOptionID("vis.maxdim", "Maximum number of dimensions to display.");
+ public static final OptionID MAXDIM_ID = new OptionID("vis.maxdim", "Maximum number of dimensions to display.");
/**
* Stores the maximum number of dimensions to show.
@@ -107,9 +107,10 @@ public class ScatterPlotFactory implements ProjectorFactory {
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- IntParameter maxdimP = new IntParameter(MAXDIM_ID, new GreaterEqualConstraint(1), MAX_DIMENSIONS_DEFAULT);
+ IntParameter maxdimP = new IntParameter(MAXDIM_ID, MAX_DIMENSIONS_DEFAULT);
+ maxdimP.addConstraint(new GreaterEqualConstraint(1));
if(config.grab(maxdimP)) {
- maxdim = maxdimP.getValue();
+ maxdim = maxdimP.intValue();
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/projector/ScatterPlotProjector.java b/src/de/lmu/ifi/dbs/elki/visualization/projector/ScatterPlotProjector.java
index f251733e..e97ad653 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/projector/ScatterPlotProjector.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/projector/ScatterPlotProjector.java
@@ -29,17 +29,17 @@ import java.util.List;
import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.math.linearalgebra.AffineTransformation;
import de.lmu.ifi.dbs.elki.result.AbstractHierarchicalResult;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
import de.lmu.ifi.dbs.elki.result.ScalesResult;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
import de.lmu.ifi.dbs.elki.visualization.gui.overview.PlotItem;
import de.lmu.ifi.dbs.elki.visualization.projections.AffineProjection;
import de.lmu.ifi.dbs.elki.visualization.projections.Projection2D;
import de.lmu.ifi.dbs.elki.visualization.projections.Simple2D;
-import de.lmu.ifi.dbs.elki.visualization.visualizers.visunproj.LabelVisFactory;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.visunproj.LabelVisualization;
/**
* ScatterPlotProjector is responsible for producing a set of scatterplot
@@ -52,14 +52,14 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.visunproj.LabelVisFactory;
*
* @param <V> Vector type
*/
-public class ScatterPlotProjector<V extends NumberVector<?, ?>> extends AbstractHierarchicalResult implements Projector {
+public class ScatterPlotProjector<V extends NumberVector<?>> extends AbstractHierarchicalResult implements Projector {
/**
- * Relation we project
+ * Relation we project.
*/
Relation<V> rel;
/**
- * Database dimensionality
+ * Database dimensionality.
*/
int dmax;
@@ -73,7 +73,7 @@ public class ScatterPlotProjector<V extends NumberVector<?, ?>> extends Abstract
super();
this.rel = rel;
this.dmax = maxdim;
- assert (maxdim <= DatabaseUtil.dimensionality(rel)) : "Requested dimensionality larger than data dimensionality?!?";
+ assert (maxdim <= RelationUtil.dimensionality(rel)) : "Requested dimensionality larger than data dimensionality?!?";
}
@Override
@@ -87,7 +87,7 @@ public class ScatterPlotProjector<V extends NumberVector<?, ?>> extends Abstract
// In 2d, make the plot twice as big.
master = new PlotItem(2 + .1, 2 + .1, null);
{
- Projection2D proj = new Simple2D(scales.getScales(), 1, 2);
+ Projection2D proj = new Simple2D(scales.getScales(), 0, 1);
PlotItem it = new PlotItem(.1, 0, 2., 2., proj);
it.tasks = tasks;
master.subitems.add(it);
@@ -95,38 +95,38 @@ public class ScatterPlotProjector<V extends NumberVector<?, ?>> extends Abstract
// Label at bottom
{
PlotItem it = new PlotItem(.1, 2., 2., .1, null);
- final VisualizationTask task = new VisualizationTask("", null, null, new LabelVisFactory(DatabaseUtil.getColumnLabel(rel, 1)));
+ final VisualizationTask task = new VisualizationTask("", null, null, new LabelVisualization(RelationUtil.getColumnLabel(rel, 0)));
task.height = .1;
task.width = 2.;
- task.put(VisualizationTask.META_NODETAIL, true);
+ task.nodetail = true;
it.tasks.add(task);
master.subitems.add(it);
}
// Label on left
{
PlotItem it = new PlotItem(0, 0, .1, 2, null);
- final VisualizationTask task = new VisualizationTask("", null, null, new LabelVisFactory(DatabaseUtil.getColumnLabel(rel, 2), true));
+ final VisualizationTask task = new VisualizationTask("", null, null, new LabelVisualization(RelationUtil.getColumnLabel(rel, 1), true));
task.height = 2.;
task.width = .1;
- task.put(VisualizationTask.META_NODETAIL, true);
+ task.nodetail = true;
it.tasks.add(task);
master.subitems.add(it);
}
}
else {
final double sizeh = Math.ceil((dmax - 1) / 2.0);
- master = new PlotItem(sizeh * 2 + .1, dmax - 1 + .1, null);
+ master = new PlotItem(sizeh * 2. + .1, dmax - 1 + .1, null);
- for(int d1 = 1; d1 < dmax; d1++) {
- for(int d2 = d1 + 1; d2 <= dmax; d2++) {
+ for(int d1 = 0; d1 < dmax - 1; d1++) {
+ for(int d2 = d1 + 1; d2 < dmax; d2++) {
Projection2D proj = new Simple2D(scales.getScales(), d1, d2);
- PlotItem it = new PlotItem(d1 - 1 + .1, d2 - 2, 1., 1., proj);
+ PlotItem it = new PlotItem(d1 + .1, d2 - 1, 1., 1., proj);
it.tasks = tasks;
master.subitems.add(it);
}
}
if(dmax >= 3) {
- AffineTransformation p = AffineProjection.axisProjection(DatabaseUtil.dimensionality(rel), 1, 2);
+ AffineTransformation p = AffineProjection.axisProjection(RelationUtil.dimensionality(rel), 1, 2);
p.addRotation(0, 2, Math.PI / 180 * -10.);
p.addRotation(1, 2, Math.PI / 180 * 15.);
// Wanna try 4d? go ahead:
@@ -138,22 +138,22 @@ public class ScatterPlotProjector<V extends NumberVector<?, ?>> extends Abstract
master.subitems.add(it);
}
// Labels at bottom
- for(int d1 = 1; d1 < dmax; d1++) {
- PlotItem it = new PlotItem(d1 - 1 + .1, dmax - 1, 1., .1, null);
- final VisualizationTask task = new VisualizationTask("", null, null, new LabelVisFactory(DatabaseUtil.getColumnLabel(rel, d1)));
+ for(int d1 = 0; d1 < dmax - 1; d1++) {
+ PlotItem it = new PlotItem(d1 + .1, dmax - 1, 1., .1, null);
+ final VisualizationTask task = new VisualizationTask("", null, null, new LabelVisualization(RelationUtil.getColumnLabel(rel, d1)));
task.height = .1;
task.width = 1;
- task.put(VisualizationTask.META_NODETAIL, true);
+ task.nodetail = true;
it.tasks.add(task);
master.subitems.add(it);
}
// Labels on left
- for(int d2 = 2; d2 <= dmax; d2++) {
- PlotItem it = new PlotItem(0, d2 - 2, .1, 1, null);
- final VisualizationTask task = new VisualizationTask("", null, null, new LabelVisFactory(DatabaseUtil.getColumnLabel(rel, d2), true));
+ for(int d2 = 1; d2 < dmax; d2++) {
+ PlotItem it = new PlotItem(0, d2 - 1, .1, 1, null);
+ final VisualizationTask task = new VisualizationTask("", null, null, new LabelVisualization(RelationUtil.getColumnLabel(rel, d2), true));
task.height = 1;
task.width = .1;
- task.put(VisualizationTask.META_NODETAIL, true);
+ task.nodetail = true;
it.tasks.add(task);
master.subitems.add(it);
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/savedialog/SVGSaveDialog.java b/src/de/lmu/ifi/dbs/elki/visualization/savedialog/SVGSaveDialog.java
index f895c46e..8f56c562 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/savedialog/SVGSaveDialog.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/savedialog/SVGSaveDialog.java
@@ -57,10 +57,10 @@ import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
*/
public class SVGSaveDialog {
/** The default title. "Save as ...". */
- public final static String DEFAULT_TITLE = "Save as ...";
+ public static final String DEFAULT_TITLE = "Save as ...";
/** Static logger reference */
- private final static Logging logger = Logging.getLogger(SVGSaveDialog.class);
+ private static final Logging LOG = Logging.getLogger(SVGSaveDialog.class);
/** Automagic file format */
final static String automagic_format = "automatic";
@@ -73,11 +73,10 @@ public class SVGSaveDialog {
static {
// FOP installed?
- if(SVGPlot.hasFOPInstalled()) {
+ if (SVGPlot.hasFOPInstalled()) {
formats = new String[] { "svg", "png", "jpeg", "jpg", "pdf", "ps", "eps" };
visibleformats = new String[] { automagic_format, "svg", "png", "jpeg", "pdf", "ps", "eps" };
- }
- else {
+ } else {
formats = new String[] { "svg", "png", "jpeg", "jpg" };
visibleformats = new String[] { automagic_format, "svg", "png", "jpeg" };
}
@@ -95,7 +94,7 @@ public class SVGSaveDialog {
double quality = 1.0;
int ret = -1;
- JFileChooser fc = new JFileChooser();
+ JFileChooser fc = new JFileChooser(new File("."));
fc.setDialogTitle(DEFAULT_TITLE);
// fc.setFileFilter(new ImageFilter());
SaveOptionsPanel optionsPanel = new SaveOptionsPanel(fc, width, height);
@@ -103,70 +102,54 @@ public class SVGSaveDialog {
ret = fc.showSaveDialog(null);
fc.setDialogTitle("Saving... Please wait.");
- if(ret == JFileChooser.APPROVE_OPTION) {
+ if (ret == JFileChooser.APPROVE_OPTION) {
File file = fc.getSelectedFile();
String format = optionsPanel.getSelectedFormat();
- if(format == null || format == automagic_format) {
+ if (format == null || automagic_format.equals(format)) {
format = guessFormat(file.getName());
}
try {
- if(format == null) {
+ if (format == null) {
showError(fc, "Error saving image.", "File format not recognized.");
- }
- else if(format.equals("jpeg") || format.equals("jpg")) {
+ } else if ("jpeg".equals(format) || "jpg".equals(format)) {
quality = optionsPanel.getJPEGQuality();
plot.saveAsJPEG(file, width, height, quality);
- }
- else if(format.equals("png")) {
+ } else if ("png".equals(format)) {
plot.saveAsPNG(file, width, height);
- }
- else if(format.equals("ps")) {
+ } else if ("ps".equals(format)) {
plot.saveAsPS(file);
- }
- else if(format.equals("eps")) {
+ } else if ("eps".equals(format)) {
plot.saveAsEPS(file);
- }
- else if(format.equals("pdf")) {
+ } else if ("pdf".equals(format)) {
plot.saveAsPDF(file);
- }
- else if(format.equals("svg")) {
+ } else if ("svg".equals(format)) {
plot.saveAsSVG(file);
- }
- else {
+ } else {
showError(fc, "Error saving image.", "Unsupported format: " + format);
}
- }
- catch(java.lang.IncompatibleClassChangeError e) {
+ } catch (java.lang.IncompatibleClassChangeError e) {
showError(fc, "Error saving image.", "It seems that your Java version is incompatible with this version of Batik and Jpeg writing. Sorry.");
- }
- catch(ClassNotFoundException e) {
+ } catch (ClassNotFoundException e) {
showError(fc, "Error saving image.", "A class was not found when saving this image. Maybe installing Apache FOP will help (for PDF, PS and EPS output).\n" + e.toString());
- }
- catch(IOException e) {
- logger.exception(e);
+ } catch (IOException e) {
+ LOG.exception(e);
showError(fc, "Error saving image.", e.toString());
- }
- catch(TranscoderException e) {
- logger.exception(e);
+ } catch (TranscoderException e) {
+ LOG.exception(e);
showError(fc, "Error saving image.", e.toString());
- }
- catch(TransformerFactoryConfigurationError e) {
- logger.exception(e);
+ } catch (TransformerFactoryConfigurationError e) {
+ LOG.exception(e);
showError(fc, "Error saving image.", e.toString());
- }
- catch(TransformerException e) {
- logger.exception(e);
+ } catch (TransformerException e) {
+ LOG.exception(e);
showError(fc, "Error saving image.", e.toString());
- }
- catch(Exception e) {
- logger.exception(e);
+ } catch (Exception e) {
+ LOG.exception(e);
showError(fc, "Error saving image.", e.toString());
}
- }
- else if(ret == JFileChooser.ERROR_OPTION) {
+ } else if (ret == JFileChooser.ERROR_OPTION) {
showError(fc, "Error in file dialog.", "Unknown Error.");
- }
- else if(ret == JFileChooser.CANCEL_OPTION) {
+ } else if (ret == JFileChooser.CANCEL_OPTION) {
// do nothing - except return result
}
return ret;
@@ -180,8 +163,8 @@ public class SVGSaveDialog {
*/
public static String guessFormat(String name) {
String ext = FileUtil.getFilenameExtension(name);
- for(String format : formats) {
- if(format.equals(ext)) {
+ for (String format : formats) {
+ if (format.equals(ext)) {
return ext;
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/savedialog/SaveOptionsPanel.java b/src/de/lmu/ifi/dbs/elki/visualization/savedialog/SaveOptionsPanel.java
index 6193310b..7e77c79f 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/savedialog/SaveOptionsPanel.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/savedialog/SaveOptionsPanel.java
@@ -206,8 +206,8 @@ public class SaveOptionsPanel extends JPanel {
resetSizeButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
- modelWidth.setValue(width);
- modelHeight.setValue(height);
+ modelWidth.setValue(Integer.valueOf(width));
+ modelHeight.setValue(Integer.valueOf(height));
aspectRatioLock.setSelected(true);
}
});
@@ -312,8 +312,7 @@ public class SaveOptionsPanel extends JPanel {
* @return Quality value for JPEG.
*/
public double getJPEGQuality() {
- Double qual = 0.7;
- qual = modelQuality.getNumber().doubleValue();
+ double qual =modelQuality.getNumber().doubleValue();
if(qual > 1.0) {
qual = 1.0;
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/style/ClusterStylingPolicy.java b/src/de/lmu/ifi/dbs/elki/visualization/style/ClusterStylingPolicy.java
index 1d329736..7adecc68 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/style/ClusterStylingPolicy.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/style/ClusterStylingPolicy.java
@@ -77,14 +77,15 @@ public class ClusterStylingPolicy implements ClassStylingPolicy {
colors = new TIntArrayList(clusters.size());
Iterator<? extends Cluster<?>> ci = clusters.iterator();
- for(int i = 0;; i++) {
+ for(int i = 0; ci.hasNext(); i++) {
Cluster<?> c = ci.next();
ids.add(DBIDUtil.ensureSet(c.getIDs()));
Color col = SVGUtil.stringToColor(colorset.getColor(i));
- if (col != null) {
+ if(col != null) {
colors.add(col.getRGB());
- } else {
- LoggingUtil.warning("Unrecognized color name: "+colorset.getColor(i));
+ }
+ else {
+ LoggingUtil.warning("Unrecognized color name: " + colorset.getColor(i));
}
if(!ci.hasNext()) {
break;
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/style/PropertiesBasedStyleLibrary.java b/src/de/lmu/ifi/dbs/elki/visualization/style/PropertiesBasedStyleLibrary.java
index 2bdae5e6..6b0869fb 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/style/PropertiesBasedStyleLibrary.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/style/PropertiesBasedStyleLibrary.java
@@ -49,7 +49,7 @@ public class PropertiesBasedStyleLibrary implements StyleLibrary {
/**
* Logger
*/
- protected static final Logging logger = Logging.getLogger(PropertiesBasedStyleLibrary.class);
+ private static final Logging LOG = Logging.getLogger(PropertiesBasedStyleLibrary.class);
/**
* Name of the default color scheme.
@@ -130,24 +130,20 @@ public class PropertiesBasedStyleLibrary implements StyleLibrary {
InputStream stream = null;
try {
stream = FileUtil.openSystemFile(filename);
- }
- catch(FileNotFoundException e) {
+ } catch (FileNotFoundException e) {
try {
stream = FileUtil.openSystemFile(filename + DEFAULT_PROPERTIES_EXTENSION);
- }
- catch(FileNotFoundException e2) {
+ } catch (FileNotFoundException e2) {
try {
stream = FileUtil.openSystemFile(DEFAULT_PROPERTIES_PATH + filename + DEFAULT_PROPERTIES_EXTENSION);
- }
- catch(FileNotFoundException e3) {
+ } catch (FileNotFoundException e3) {
throw new AbortException("Could not find style scheme file '" + filename + "' for scheme '" + name + "'!");
}
}
}
try {
properties.load(stream);
- }
- catch(Exception e) {
+ } catch (Exception e) {
throw new AbortException("Error loading properties file " + filename + ".\n", e);
}
}
@@ -171,7 +167,7 @@ public class PropertiesBasedStyleLibrary implements StyleLibrary {
* @return Resulting value
*/
private <T> T getCached(String prefix, String postfix, Class<T> cls) {
- return cache.get(prefix + "." + postfix, cls);
+ return cache.get(prefix + '.' + postfix, cls);
}
/**
@@ -183,7 +179,7 @@ public class PropertiesBasedStyleLibrary implements StyleLibrary {
* @param data Data
*/
private <T> void setCached(String prefix, String postfix, T data) {
- cache.put(prefix + "." + postfix, data);
+ cache.put(prefix + '.' + postfix, data);
}
/**
@@ -195,26 +191,26 @@ public class PropertiesBasedStyleLibrary implements StyleLibrary {
*/
protected String getPropertyValue(String prefix, String postfix) {
String ret = properties.getProperty(prefix + "." + postfix);
- if(ret != null) {
+ if (ret != null) {
// logger.debugFine("Found property: "+prefix + "." +
// postfix+" for "+prefix);
return ret;
}
int pos = prefix.length();
- while(pos > 0) {
- pos = prefix.lastIndexOf(".", pos - 1);
- if(pos <= 0) {
+ while (pos > 0) {
+ pos = prefix.lastIndexOf('.', pos - 1);
+ if (pos <= 0) {
break;
}
- ret = properties.getProperty(prefix.substring(0, pos) + "." + postfix);
- if(ret != null) {
+ ret = properties.getProperty(prefix.substring(0, pos) + '.' + postfix);
+ if (ret != null) {
// logger.debugFine("Found property: "+prefix.substring(0, pos) + "." +
// postfix+" for "+prefix);
return ret;
}
}
ret = properties.getProperty(postfix);
- if(ret != null) {
+ if (ret != null) {
// logger.debugFine("Found property: "+postfix+" for "+prefix);
return ret;
}
@@ -239,7 +235,7 @@ public class PropertiesBasedStyleLibrary implements StyleLibrary {
@Override
public ColorLibrary getColorSet(String key) {
ColorLibrary cl = getCached(key, COLORSET, ColorLibrary.class);
- if(cl == null) {
+ if (cl == null) {
String[] colors = getPropertyValue(key, COLORSET).split(LIST_SEPARATOR);
cl = new ListBasedColorLibrary(colors, "Default");
setCached(key, COLORSET, cl);
@@ -250,29 +246,27 @@ public class PropertiesBasedStyleLibrary implements StyleLibrary {
@Override
public double getLineWidth(String key) {
Double lw = getCached(key, LINE_WIDTH, Double.class);
- if(lw == null) {
+ if (lw == null) {
try {
- lw = Double.parseDouble(getPropertyValue(key, LINE_WIDTH)) * SCALE;
- }
- catch(NullPointerException e) {
- throw new AbortException("Missing/invalid value in style library: " + key + "." + LINE_WIDTH);
+ lw = Double.valueOf(Double.parseDouble(getPropertyValue(key, LINE_WIDTH)) * SCALE);
+ } catch (NullPointerException e) {
+ throw new AbortException("Missing/invalid value in style library: " + key + '.' + LINE_WIDTH);
}
}
- return lw;
+ return lw.doubleValue();
}
@Override
public double getTextSize(String key) {
Double lw = getCached(key, TEXT_SIZE, Double.class);
- if(lw == null) {
+ if (lw == null) {
try {
- lw = Double.parseDouble(getPropertyValue(key, TEXT_SIZE)) * SCALE;
- }
- catch(NullPointerException e) {
- throw new AbortException("Missing/invalid value in style library: " + key + "." + TEXT_SIZE);
+ lw = Double.valueOf(Double.parseDouble(getPropertyValue(key, TEXT_SIZE)) * SCALE);
+ } catch (NullPointerException e) {
+ throw new AbortException("Missing/invalid value in style library: " + key + '.' + TEXT_SIZE);
}
}
- return lw;
+ return lw.doubleValue();
}
@Override
@@ -283,47 +277,43 @@ public class PropertiesBasedStyleLibrary implements StyleLibrary {
@Override
public double getSize(String key) {
Double lw = getCached(key, GENERIC_SIZE, Double.class);
- if(lw == null) {
+ if (lw == null) {
try {
- lw = Double.parseDouble(getPropertyValue(key, GENERIC_SIZE)) * SCALE;
- }
- catch(NullPointerException e) {
- throw new AbortException("Missing/invalid value in style library: " + key + "." + GENERIC_SIZE);
+ lw = Double.valueOf(Double.parseDouble(getPropertyValue(key, GENERIC_SIZE)) * SCALE);
+ } catch (NullPointerException e) {
+ throw new AbortException("Missing/invalid value in style library: " + key + '.' + GENERIC_SIZE);
}
}
- return lw;
+ return lw.doubleValue();
}
@Override
public double getOpacity(String key) {
Double lw = getCached(key, OPACITY, Double.class);
- if(lw == null) {
+ if (lw == null) {
try {
- lw = Double.parseDouble(getPropertyValue(key, OPACITY));
- }
- catch(NullPointerException e) {
- throw new AbortException("Missing/invalid value in style library: " + key + "." + OPACITY);
+ lw = Double.valueOf(Double.parseDouble(getPropertyValue(key, OPACITY)));
+ } catch (NullPointerException e) {
+ throw new AbortException("Missing/invalid value in style library: " + key + '.' + OPACITY);
}
}
- return lw;
+ return lw.doubleValue();
}
@Override
public LineStyleLibrary lines() {
- if(linelib == null) {
+ if (linelib == null) {
String libname = properties.getProperty(PROP_LINES_LIBRARY, SolidLineStyleLibrary.class.getName());
try {
Class<?> cls;
try {
cls = Class.forName(libname);
- }
- catch(ClassNotFoundException e) {
- cls = Class.forName(LineStyleLibrary.class.getPackage().getName() + "." + libname);
+ } catch (ClassNotFoundException e) {
+ cls = Class.forName(LineStyleLibrary.class.getPackage().getName() + '.' + libname);
}
linelib = (LineStyleLibrary) cls.getConstructor(StyleLibrary.class).newInstance(this);
- }
- catch(Exception e) {
- logger.exception(e);
+ } catch (Exception e) {
+ LOG.exception(e);
linelib = new SolidLineStyleLibrary(this);
}
}
@@ -332,23 +322,21 @@ public class PropertiesBasedStyleLibrary implements StyleLibrary {
@Override
public MarkerLibrary markers() {
- if(markerlib == null) {
+ if (markerlib == null) {
String libname = properties.getProperty(PROP_MARKER_LIBRARY, PrettyMarkers.class.getName());
try {
Class<?> cls;
try {
cls = Class.forName(libname);
- }
- catch(ClassNotFoundException e) {
- cls = Class.forName(MarkerLibrary.class.getPackage().getName() + "." + libname);
+ } catch (ClassNotFoundException e) {
+ cls = Class.forName(MarkerLibrary.class.getPackage().getName() + '.' + libname);
}
markerlib = (MarkerLibrary) cls.getConstructor(StyleLibrary.class).newInstance(this);
- }
- catch(Exception e) {
- logger.exception(e);
+ } catch (Exception e) {
+ LOG.exception(e);
markerlib = new PrettyMarkers(this);
}
}
return markerlib;
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/style/StyleResult.java b/src/de/lmu/ifi/dbs/elki/visualization/style/StyleResult.java
index fe8a73ce..e8f0d3fb 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/style/StyleResult.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/style/StyleResult.java
@@ -32,7 +32,6 @@ import de.lmu.ifi.dbs.elki.result.Result;
* @apiviz.landmark
* @apiviz.composedOf StylingPolicy
*/
-// TODO: pull style library etc. from VisualizerContext here?
public class StyleResult implements Result {
/**
* Styling policy
@@ -40,6 +39,11 @@ public class StyleResult implements Result {
StylingPolicy policy;
/**
+ * Style library
+ */
+ StyleLibrary library;
+
+ /**
* Get the active styling policy
*
* @return Styling policy
@@ -57,6 +61,24 @@ public class StyleResult implements Result {
this.policy = policy;
}
+ /**
+ * Get the style library
+ *
+ * @return Style library
+ */
+ public StyleLibrary getStyleLibrary() {
+ return library;
+ }
+
+ /**
+ * Get the style library
+ *
+ * @param library Style library
+ */
+ public void setStyleLibrary(StyleLibrary library) {
+ this.library = library;
+ }
+
@Override
public String getLongName() {
return "Style policy";
@@ -66,4 +88,4 @@ public class StyleResult implements Result {
public String getShortName() {
return "style-policy";
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/style/lines/DashedLineStyleLibrary.java b/src/de/lmu/ifi/dbs/elki/visualization/style/lines/DashedLineStyleLibrary.java
index 5dbfe67a..e3abd7c1 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/style/lines/DashedLineStyleLibrary.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/style/lines/DashedLineStyleLibrary.java
@@ -107,14 +107,14 @@ public class DashedLineStyleLibrary implements LineStyleLibrary {
boolean interpolated = false;
// process flavoring flags
for(Object flag : flags) {
- if(flag == LineStyleLibrary.FLAG_STRONG) {
+ if(LineStyleLibrary.FLAG_STRONG.equals(flag)) {
width = width * 1.5;
}
- else if(flag == LineStyleLibrary.FLAG_WEAK) {
+ else if(LineStyleLibrary.FLAG_WEAK.equals(flag)) {
cls.setStatement(CSSConstants.CSS_STROKE_OPACITY_PROPERTY, ".50");
width = width * 0.75;
}
- else if(flag == LineStyleLibrary.FLAG_INTERPOLATED) {
+ else if(LineStyleLibrary.FLAG_INTERPOLATED.equals(flag)) {
interpolated = true;
}
}
@@ -125,10 +125,10 @@ public class DashedLineStyleLibrary implements LineStyleLibrary {
double[] pat = dashpatterns[styleflav];
assert (pat.length % 2 == 0);
if(pat.length > 0) {
- StringBuffer pattern = new StringBuffer();
+ StringBuilder pattern = new StringBuilder();
for(int i = 0; i < pat.length; i++) {
if(i > 0) {
- pattern.append(",");
+ pattern.append(',');
}
pattern.append(SVGUtil.fmt(pat[i] * width * 30));
// pattern.append("%");
@@ -144,10 +144,10 @@ public class DashedLineStyleLibrary implements LineStyleLibrary {
assert (pat.length % 2 == 0);
// TODO: add dotting.
if(pat.length > 0) {
- StringBuffer pattern = new StringBuffer();
+ StringBuilder pattern = new StringBuilder();
for(int i = 0; i < pat.length; i++) {
if(i > 0) {
- pattern.append(",");
+ pattern.append(',');
}
pattern.append(SVGUtil.fmt(pat[i] * width));
// pattern.append("%");
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/style/lines/LineStyleLibrary.java b/src/de/lmu/ifi/dbs/elki/visualization/style/lines/LineStyleLibrary.java
index aff1e0fc..79f3eda0 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/style/lines/LineStyleLibrary.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/style/lines/LineStyleLibrary.java
@@ -50,17 +50,17 @@ public interface LineStyleLibrary {
/**
* Meta flag to request a 'stronger' version of the style
*/
- public final static String FLAG_STRONG = "strong";
+ public static final String FLAG_STRONG = "strong";
/**
* Meta flag to request a 'weaker' version of the style
*/
- public final static String FLAG_WEAK = "weak";
+ public static final String FLAG_WEAK = "weak";
/**
* Meta flag to request an 'interpolated' version of the style
*/
- public final static String FLAG_INTERPOLATED = "interpolated";
+ public static final String FLAG_INTERPOLATED = "interpolated";
/**
* Add the formatting statements to the given CSS class.
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/style/lines/SolidLineStyleLibrary.java b/src/de/lmu/ifi/dbs/elki/visualization/style/lines/SolidLineStyleLibrary.java
index 051bff0a..51491db2 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/style/lines/SolidLineStyleLibrary.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/style/lines/SolidLineStyleLibrary.java
@@ -87,20 +87,20 @@ public class SolidLineStyleLibrary implements LineStyleLibrary {
boolean interpolated = false;
// process flavoring flags
for(Object flag : flags) {
- if(flag == LineStyleLibrary.FLAG_STRONG) {
+ if(LineStyleLibrary.FLAG_STRONG.equals(flag)) {
width = width * 1.5;
}
- else if(flag == LineStyleLibrary.FLAG_WEAK) {
+ else if(LineStyleLibrary.FLAG_WEAK.equals(flag)) {
cls.setStatement(CSSConstants.CSS_STROKE_OPACITY_PROPERTY, ".50");
width = width * 0.75;
}
- else if(flag == LineStyleLibrary.FLAG_INTERPOLATED) {
+ else if(LineStyleLibrary.FLAG_INTERPOLATED.equals(flag)) {
interpolated = true;
}
}
cls.setStatement(CSSConstants.CSS_STROKE_WIDTH_PROPERTY, SVGUtil.fmt(width));
if(interpolated) {
- cls.setStatement(CSSConstants.CSS_STROKE_DASHARRAY_PROPERTY, "" + SVGUtil.fmt(width / StyleLibrary.SCALE * 2) + "," + SVGUtil.fmt(width / StyleLibrary.SCALE * 2));
+ cls.setStatement(CSSConstants.CSS_STROKE_DASHARRAY_PROPERTY, "" + SVGUtil.fmt(width / StyleLibrary.SCALE * 2.) + "," + SVGUtil.fmt(width / StyleLibrary.SCALE * 2.));
}
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/style/marker/CircleMarkers.java b/src/de/lmu/ifi/dbs/elki/visualization/style/marker/CircleMarkers.java
index b847d571..ae066c9e 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/style/marker/CircleMarkers.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/style/marker/CircleMarkers.java
@@ -72,7 +72,7 @@ public class CircleMarkers implements MarkerLibrary {
*/
@Override
public Element useMarker(SVGPlot plot, Element parent, double x, double y, int stylenr, double size) {
- Element marker = plot.svgCircle(x, y, size / 2);
+ Element marker = plot.svgCircle(x, y, size * .5);
final String col;
if(stylenr == -1) {
col = dotcolor;
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/style/marker/MinimalMarkers.java b/src/de/lmu/ifi/dbs/elki/visualization/style/marker/MinimalMarkers.java
index 2f5a8c2a..b7ad6274 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/style/marker/MinimalMarkers.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/style/marker/MinimalMarkers.java
@@ -72,7 +72,7 @@ public class MinimalMarkers implements MarkerLibrary {
*/
@Override
public Element useMarker(SVGPlot plot, Element parent, double x, double y, int stylenr, double size) {
- Element marker = plot.svgRect(x - size / 2, y - size / 2, size, size);
+ Element marker = plot.svgRect(x - size * .5, y - size * .5, size, size);
final String col;
if(stylenr == -1) {
col = dotcolor;
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/style/marker/PrettyMarkers.java b/src/de/lmu/ifi/dbs/elki/visualization/style/marker/PrettyMarkers.java
index d868bb06..7352fea3 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/style/marker/PrettyMarkers.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/style/marker/PrettyMarkers.java
@@ -48,7 +48,7 @@ public class PrettyMarkers implements MarkerLibrary {
/**
* Default prefix to use.
*/
- private final static String DEFAULT_PREFIX = "s";
+ private static final String DEFAULT_PREFIX = "s";
/**
* Prefix for the IDs generated.
@@ -159,7 +159,7 @@ public class PrettyMarkers implements MarkerLibrary {
}
case 5: {
// O filled circle
- Element circ = plot.svgCircle(x, y, size / 2);
+ Element circ = plot.svgCircle(x, y, size * .5);
SVGUtil.setStyle(circ, SVGConstants.CSS_FILL_PROPERTY + ":" + colorstr);
parent.appendChild(circ);
break;
@@ -192,7 +192,7 @@ public class PrettyMarkers implements MarkerLibrary {
* @param size Size
*/
protected void plotGray(SVGPlot plot, Element parent, double x, double y, double size) {
- Element marker = plot.svgCircle(x, y, size / 2);
+ Element marker = plot.svgCircle(x, y, size * .5);
SVGUtil.setStyle(marker, SVGConstants.CSS_FILL_PROPERTY + ":" + greycolor);
parent.appendChild(marker);
}
@@ -207,7 +207,7 @@ public class PrettyMarkers implements MarkerLibrary {
* @param size Size
*/
protected void plotUncolored(SVGPlot plot, Element parent, double x, double y, double size) {
- Element marker = plot.svgCircle(x, y, size / 2);
+ Element marker = plot.svgCircle(x, y, size * .5);
SVGUtil.setStyle(marker, SVGConstants.CSS_FILL_PROPERTY + ":" + dotcolor);
parent.appendChild(marker);
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/style/presentation.properties b/src/de/lmu/ifi/dbs/elki/visualization/style/presentation.properties
index 8c597066..01264673 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/style/presentation.properties
+++ b/src/de/lmu/ifi/dbs/elki/visualization/style/presentation.properties
@@ -74,9 +74,9 @@ xycurve.text-size=0.04
overview.labels.text-size=0.08
## Selection colors
-plot.selection.color=darkblue
+plot.selection.color=red
plot.selection.opacity=0.4
-plot.selection.size=0.015
+plot.selection.size=0.03
## Circle segment colors. Will be interpolated to produce extra classes.
segments.border.color=#FF0073
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGArrow.java b/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGArrow.java
new file mode 100644
index 00000000..3859deb5
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGArrow.java
@@ -0,0 +1,114 @@
+package de.lmu.ifi.dbs.elki.visualization.svg;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import org.w3c.dom.Element;
+
+/**
+ * Static class for drawing simple arrows
+ *
+ * @author Erich Schubert
+ * @author Robert Rödler
+ *
+ * @apiviz.uses SVGPath
+ */
+public final class SVGArrow {
+ /**
+ * Direction constants
+ *
+ * @author Erich Schubert
+ * @author Robert Rödler
+ *
+ * @apiviz.exclude
+ */
+ public static enum Direction {
+ LEFT, DOWN, RIGHT, UP, // SWAPWITH, INSERT
+ }
+
+ /**
+ * Constant for "up"
+ */
+ public static final Direction UP = Direction.UP;
+
+ /**
+ * Constant for "down"
+ */
+ public static final Direction DOWN = Direction.DOWN;
+
+ /**
+ * Constant for "right"
+ */
+ public static final Direction RIGHT = Direction.RIGHT;
+
+ /**
+ * Constant for "left"
+ */
+ public static final Direction LEFT = Direction.LEFT;
+
+ /**
+ * Draw an arrow at the given position.
+ *
+ * Note: the arrow is an unstyled svg path. You need to apply style afterwards.
+ *
+ * @param svgp Plot to draw to
+ * @param dir Direction to draw
+ * @param x Center x coordinate
+ * @param y Center y coordinate
+ * @param size Arrow size
+ * @return SVG Element
+ */
+ public static Element makeArrow(SVGPlot svgp, Direction dir, double x, double y, double size) {
+ final SVGPath path = new SVGPath();
+ final double hs = size / 2.;
+
+ switch(dir){
+ case LEFT:
+ path.drawTo(x + hs, y + hs);
+ path.drawTo(x - hs, y);
+ path.drawTo(x + hs, y - hs);
+ path.drawTo(x + hs, y + hs);
+ break;
+ case DOWN:
+ path.drawTo(x - hs, y - hs);
+ path.drawTo(x + hs, y - hs);
+ path.drawTo(x, y + hs);
+ path.drawTo(x - hs, y - hs);
+ break;
+ case RIGHT:
+ path.drawTo(x - hs, y - hs);
+ path.drawTo(x + hs, y);
+ path.drawTo(x - hs, y + hs);
+ path.drawTo(x - hs, y - hs);
+ break;
+ case UP:
+ path.drawTo(x - hs, y + hs);
+ path.drawTo(x, y - hs);
+ path.drawTo(x + hs, y + hs);
+ path.drawTo(x - hs, y + hs);
+ break;
+ }
+ path.close();
+ return path.makeElement(svgp);
+ }
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGHyperCube.java b/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGHyperCube.java
index 723f13df..656e42da 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGHyperCube.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGHyperCube.java
@@ -70,7 +70,7 @@ public class SVGHyperCube {
* @param max Opposite corner
* @return path element
*/
- public static <V extends NumberVector<V, ?>> Element drawFrame(SVGPlot svgp, Projection2D proj, V min, V max) {
+ public static <V extends NumberVector<?>> Element drawFrame(SVGPlot svgp, Projection2D proj, V min, V max) {
SVGPath path = new SVGPath();
ArrayList<double[]> edges = getVisibleEdges(proj, min.getColumnVector().getArrayRef(), max.getColumnVector().getArrayRef());
double[] rv_min = proj.fastProjectDataToRenderSpace(min);
@@ -107,7 +107,7 @@ public class SVGHyperCube {
* @param max Opposite corner
* @return group element
*/
- public static <V extends NumberVector<V, ?>> Element drawFilled(SVGPlot svgp, String cls, Projection2D proj, V min, V max) {
+ public static <V extends NumberVector<?>> Element drawFilled(SVGPlot svgp, String cls, Projection2D proj, V min, V max) {
Element group = svgp.svgElement(SVGConstants.SVG_G_TAG);
ArrayList<double[]> edges = getVisibleEdges(proj, min.getColumnVector().getArrayRef(), max.getColumnVector().getArrayRef());
double[] rv_min = proj.fastProjectDataToRenderSpace(min);
@@ -184,14 +184,14 @@ public class SVGHyperCube {
for(int j = i + 1; j < r_edges.size(); j++) {
if(!b.get(i) && !b.get(j)) {
double[] deltaj = r_edges.get(j);
- StringBuffer pbuf = new StringBuffer();
- pbuf.append(SVGUtil.fmt(r_min[0])).append(",");
- pbuf.append(SVGUtil.fmt(r_min[1])).append(" ");
- pbuf.append(SVGUtil.fmt(r_min[0] + deltai[0])).append(",");
- pbuf.append(SVGUtil.fmt(r_min[1] + deltai[1])).append(" ");
- pbuf.append(SVGUtil.fmt(r_min[0] + deltai[0] + deltaj[0])).append(",");
- pbuf.append(SVGUtil.fmt(r_min[1] + deltai[1] + deltaj[1])).append(" ");
- pbuf.append(SVGUtil.fmt(r_min[0] + deltaj[0])).append(",");
+ StringBuilder pbuf = new StringBuilder();
+ pbuf.append(SVGUtil.fmt(r_min[0])).append(',');
+ pbuf.append(SVGUtil.fmt(r_min[1])).append(' ');
+ pbuf.append(SVGUtil.fmt(r_min[0] + deltai[0])).append(',');
+ pbuf.append(SVGUtil.fmt(r_min[1] + deltai[1])).append(' ');
+ pbuf.append(SVGUtil.fmt(r_min[0] + deltai[0] + deltaj[0])).append(',');
+ pbuf.append(SVGUtil.fmt(r_min[1] + deltai[1] + deltaj[1])).append(' ');
+ pbuf.append(SVGUtil.fmt(r_min[0] + deltaj[0])).append(',');
pbuf.append(SVGUtil.fmt(r_min[1] + deltaj[1]));
Element poly = plot.svgElement(SVGConstants.SVG_POLYGON_TAG);
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGHyperSphere.java b/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGHyperSphere.java
index 29b437f9..33daa56b 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGHyperSphere.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGHyperSphere.java
@@ -45,7 +45,7 @@ public class SVGHyperSphere {
*
* kappa = 4 * (Math.sqrt(2)-1)/3
*/
- public final static double EUCLIDEAN_KAPPA = 0.5522847498;
+ public static final double EUCLIDEAN_KAPPA = 0.5522847498;
/**
* Wireframe "manhattan" hypersphere
@@ -57,7 +57,7 @@ public class SVGHyperSphere {
* @param rad radius
* @return path element
*/
- public static <D extends NumberDistance<?, ?>> Element drawManhattan(SVGPlot svgp, Projection2D proj, NumberVector<?, ?> mid, D rad) {
+ public static <D extends NumberDistance<?, ?>> Element drawManhattan(SVGPlot svgp, Projection2D proj, NumberVector<?> mid, D rad) {
final double radius = rad.doubleValue();
final double[] v_mid = mid.getColumnVector().getArrayRef(); // a copy
final BitSet dims = proj.getVisibleDimensions2D();
@@ -104,7 +104,7 @@ public class SVGHyperSphere {
* @param rad radius
* @return path element
*/
- public static <D extends NumberDistance<?, ?>> Element drawEuclidean(SVGPlot svgp, Projection2D proj, NumberVector<?, ?> mid, D rad) {
+ public static <D extends NumberDistance<?, ?>> Element drawEuclidean(SVGPlot svgp, Projection2D proj, NumberVector<?> mid, D rad) {
final double radius = rad.doubleValue();
double[] v_mid = mid.getColumnVector().getArrayRef(); // a copy
BitSet dims = proj.getVisibleDimensions2D();
@@ -157,7 +157,7 @@ public class SVGHyperSphere {
* @param p L_p value
* @return path element
*/
- public static <D extends NumberDistance<?, ?>> Element drawLp(SVGPlot svgp, Projection2D proj, NumberVector<?, ?> mid, D rad, double p) {
+ public static <D extends NumberDistance<?, ?>> Element drawLp(SVGPlot svgp, Projection2D proj, NumberVector<?> mid, D rad, double p) {
final double radius = rad.doubleValue();
final double[] v_mid = mid.getColumnVector().getArrayRef();
final BitSet dims = proj.getVisibleDimensions2D();
@@ -277,7 +277,7 @@ public class SVGHyperSphere {
* @param rad radius
* @return path element
*/
- public static <D extends NumberDistance<?, ?>> Element drawCross(SVGPlot svgp, Projection2D proj, NumberVector<?, ?> mid, D rad) {
+ public static <D extends NumberDistance<?, ?>> Element drawCross(SVGPlot svgp, Projection2D proj, NumberVector<?> mid, D rad) {
final double radius = rad.doubleValue();
final double[] v_mid = mid.getColumnVector().getArrayRef();
final BitSet dims = proj.getVisibleDimensions2D();
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGPath.java b/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGPath.java
index 2b86870b..7c3e97e9 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGPath.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGPath.java
@@ -40,7 +40,7 @@ public class SVGPath {
/**
* String buffer for building the path.
*/
- private StringBuffer buf = new StringBuffer();
+ private StringBuilder buf = new StringBuilder();
/**
* The last action we did, to not add unnecessary commands
@@ -56,48 +56,48 @@ public class SVGPath {
/**
* The lower case version (relative) line to command.
*/
- public final static String PATH_LINE_TO_RELATIVE = SVGConstants.PATH_LINE_TO.toLowerCase();
+ public static final String PATH_LINE_TO_RELATIVE = SVGConstants.PATH_LINE_TO.toLowerCase();
/**
* The lower case version (relative) move command.
*/
- public final static String PATH_MOVE_RELATIVE = SVGConstants.PATH_MOVE.toLowerCase();
+ public static final String PATH_MOVE_RELATIVE = SVGConstants.PATH_MOVE.toLowerCase();
/**
* The lower case version (relative) horizontal line to command.
*/
- public final static String PATH_HORIZONTAL_LINE_TO_RELATIVE = SVGConstants.PATH_HORIZONTAL_LINE_TO.toLowerCase();
+ public static final String PATH_HORIZONTAL_LINE_TO_RELATIVE = SVGConstants.PATH_HORIZONTAL_LINE_TO.toLowerCase();
/**
* The lower case version (relative) vertical line to command.
*/
- public final static String PATH_VERTICAL_LINE_TO_RELATIVE = SVGConstants.PATH_VERTICAL_LINE_TO.toLowerCase();
+ public static final String PATH_VERTICAL_LINE_TO_RELATIVE = SVGConstants.PATH_VERTICAL_LINE_TO.toLowerCase();
/**
* The lower case version (relative) cubic line to command.
*/
- public final static String PATH_CUBIC_TO_RELATIVE = SVGConstants.PATH_CUBIC_TO.toLowerCase();
+ public static final String PATH_CUBIC_TO_RELATIVE = SVGConstants.PATH_CUBIC_TO.toLowerCase();
/**
* The lower case version (relative) smooth cubic to command.
*/
- public final static String PATH_SMOOTH_CUBIC_TO_RELATIVE = PATH_SMOOTH_CUBIC_TO.toLowerCase();
+ public static final String PATH_SMOOTH_CUBIC_TO_RELATIVE = PATH_SMOOTH_CUBIC_TO.toLowerCase();
/**
* The lower case version (relative) quadratic interpolation to command.
*/
- public final static String PATH_QUAD_TO_RELATIVE = SVGConstants.PATH_QUAD_TO.toLowerCase();
+ public static final String PATH_QUAD_TO_RELATIVE = SVGConstants.PATH_QUAD_TO.toLowerCase();
/**
* The lower case version (relative) smooth quadratic interpolation to
* command.
*/
- public final static String PATH_SMOOTH_QUAD_TO_RELATIVE = SVGConstants.PATH_SMOOTH_QUAD_TO.toLowerCase();
+ public static final String PATH_SMOOTH_QUAD_TO_RELATIVE = SVGConstants.PATH_SMOOTH_QUAD_TO.toLowerCase();
/**
* The lower case version (relative) path arc command.
*/
- public final static String PATH_ARC_RELATIVE = SVGConstants.PATH_ARC.toLowerCase();
+ public static final String PATH_ARC_RELATIVE = SVGConstants.PATH_ARC.toLowerCase();
/**
* Empty path constructor.
@@ -794,7 +794,7 @@ public class SVGPath {
}
for(double d : ds) {
buf.append(SVGUtil.FMT.format(d));
- buf.append(" ");
+ buf.append(' ');
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGPlot.java b/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGPlot.java
index e45c7b5a..43244047 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGPlot.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGPlot.java
@@ -24,6 +24,7 @@ package de.lmu.ifi.dbs.elki.visualization.svg;
*/
import java.awt.image.BufferedImage;
+import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -331,7 +332,7 @@ public class SVGPlot {
* @throws TransformerException Transformation error
*/
public void saveAsSVG(File file) throws IOException, TransformerFactoryConfigurationError, TransformerException {
- OutputStream out = new FileOutputStream(file);
+ OutputStream out = new BufferedOutputStream(new FileOutputStream(file));
// TODO embed linked images.
javax.xml.transform.Result result = new StreamResult(out);
SVGDocument doc = cloneDocument();
@@ -356,7 +357,7 @@ public class SVGPlot {
transcoder.addTranscodingHint(XMLAbstractTranscoder.KEY_XML_PARSER_VALIDATING, Boolean.FALSE);
SVGDocument doc = cloneDocument();
TranscoderInput input = new TranscoderInput(doc);
- OutputStream out = new FileOutputStream(file);
+ OutputStream out = new BufferedOutputStream(new FileOutputStream(file));
TranscoderOutput output = new TranscoderOutput(out);
transcoder.transcode(input, output);
out.flush();
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGScoreBar.java b/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGScoreBar.java
index ac352169..3af2ea41 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGScoreBar.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGScoreBar.java
@@ -111,7 +111,7 @@ public class SVGScoreBar {
Element bar = svgp.svgRect(x, y, width, height);
bar.setAttribute(SVGConstants.SVG_FILL_ATTRIBUTE, "#a0a0a0");
bar.setAttribute(SVGConstants.SVG_STROKE_ATTRIBUTE, "#a0a0a0");
- bar.setAttribute(SVGConstants.SVG_STROKE_WIDTH_ATTRIBUTE, "" + height * 0.01);
+ bar.setAttribute(SVGConstants.SVG_STROKE_WIDTH_ATTRIBUTE, String.valueOf(height * 0.01));
barchart.appendChild(bar);
if(fill >= 0 && fill <= size + 1) {
@@ -119,7 +119,7 @@ public class SVGScoreBar {
Element chart = svgp.svgRect(x + 0.02 * height, y + 0.02 * height, fpos, height - 0.04 * height);
chart.setAttribute(SVGConstants.SVG_FILL_ATTRIBUTE, "#d4e4f1");
chart.setAttribute(SVGConstants.SVG_STROKE_ATTRIBUTE, "#a0a0a0");
- chart.setAttribute(SVGConstants.SVG_STROKE_WIDTH_ATTRIBUTE, "" + height * 0.01);
+ chart.setAttribute(SVGConstants.SVG_STROKE_WIDTH_ATTRIBUTE, String.valueOf(height * 0.01));
barchart.appendChild(chart);
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGSimpleLinearAxis.java b/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGSimpleLinearAxis.java
index 987915b7..930e9e8e 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGSimpleLinearAxis.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGSimpleLinearAxis.java
@@ -66,17 +66,17 @@ public class SVGSimpleLinearAxis {
/**
* CSS class name for the axes
*/
- private final static String CSS_AXIS = "axis";
+ private static final String CSS_AXIS = "axis";
/**
* CSS class name for the axes
*/
- private final static String CSS_AXIS_TICK = "axis-tick";
+ private static final String CSS_AXIS_TICK = "axis-tick";
/**
* CSS class name for the axes
*/
- private final static String CSS_AXIS_LABEL = "axis-label";
+ private static final String CSS_AXIS_LABEL = "axis-label";
/**
* Register CSS classes with a {@link CSSClassManager}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGUtil.java b/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGUtil.java
index 4e40d1b0..633cb3e5 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGUtil.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGUtil.java
@@ -79,7 +79,7 @@ public final class SVGUtil {
* SVG color names conversion.
*/
final private static TObjectIntHashMap<String> SVG_COLOR_NAMES;
-
+
/**
* Key not found value. Not a reasonable color, fully transparent!
*/
@@ -244,7 +244,7 @@ public final class SVGUtil {
/**
* CSS Stylesheet from Javax, to parse color values.
*/
- private final static StyleSheet colorLookupStylesheet = new StyleSheet();
+ private static final StyleSheet colorLookupStylesheet = new StyleSheet();
/**
* Format a double according to the SVG specs.
@@ -329,13 +329,13 @@ public final class SVGUtil {
*/
public static void addCSSClass(Element e, String cssclass) {
String oldval = e.getAttribute(SVGConstants.SVG_CLASS_ATTRIBUTE);
- if(oldval == null || oldval.length() == 0) {
+ if (oldval == null || oldval.length() == 0) {
setAtt(e, SVGConstants.SVG_CLASS_ATTRIBUTE, cssclass);
return;
}
String[] classes = oldval.split(" ");
- for(String c : classes) {
- if(c.equals(cssclass)) {
+ for (String c : classes) {
+ if (c.equals(cssclass)) {
return;
}
}
@@ -350,22 +350,36 @@ public final class SVGUtil {
*/
public static void removeCSSClass(Element e, String cssclass) {
String oldval = e.getAttribute(SVGConstants.SVG_CLASS_ATTRIBUTE);
- if(oldval == null) {
+ if (oldval == null) {
return;
}
String[] classes = oldval.split(" ");
- String joined = "";
- for(String c : classes) {
- if(!c.equals(cssclass)) {
- if(joined.length() > 0) {
- joined = joined + " " + c;
+ if (classes.length == 1) {
+ if (cssclass.equals(classes[0])) {
+ e.removeAttribute(SVGConstants.SVG_CLASS_ATTRIBUTE);
+ }
+ } else if (classes.length == 2) {
+ if (cssclass.equals(classes[0])) {
+ if (cssclass.equals(classes[1])) {
+ e.removeAttribute(SVGConstants.SVG_CLASS_ATTRIBUTE);
+ } else {
+ e.setAttribute(SVGConstants.SVG_CLASS_ATTRIBUTE, classes[1]);
}
- else {
- joined = c;
+ } else if (cssclass.equals(classes[1])) {
+ e.setAttribute(SVGConstants.SVG_CLASS_ATTRIBUTE, classes[0]);
+ }
+ } else {
+ StringBuilder joined = new StringBuilder();
+ for (String c : classes) {
+ if (!c.equals(cssclass)) {
+ if (joined.length() > 0) {
+ joined.append(' ');
+ }
+ joined.append(c);
}
}
+ e.setAttribute(SVGConstants.SVG_CLASS_ATTRIBUTE, joined.toString());
}
- SVGUtil.setAtt(e, SVGConstants.SVG_CLASS_ATTRIBUTE, joined);
}
/**
@@ -376,7 +390,7 @@ public final class SVGUtil {
*/
public static Element makeStyleElement(Document document) {
Element style = SVGUtil.svgElement(document, SVGConstants.SVG_STYLE_TAG);
- SVGUtil.setAtt(style, SVGConstants.SVG_TYPE_ATTRIBUTE, SVGConstants.CSS_MIME_TYPE);
+ style.setAttribute(SVGConstants.SVG_TYPE_ATTRIBUTE, SVGConstants.CSS_MIME_TYPE);
return style;
}
@@ -479,7 +493,7 @@ public final class SVGUtil {
*/
public static Color stringToColor(String str) {
int icol = SVG_COLOR_NAMES.get(str.toLowerCase());
- if(icol != NO_VALUE) {
+ if (icol != NO_VALUE) {
return new Color(icol, false);
}
return colorLookupStylesheet.stringToColor(str);
@@ -494,7 +508,7 @@ public final class SVGUtil {
* @return Color string
*/
public static String colorToString(Color col) {
- return String.format("#%02x%02x%02x", col.getRed(), col.getGreen(), col.getBlue());
+ return colorToString(col.getRGB());
}
/**
@@ -506,7 +520,12 @@ public final class SVGUtil {
* @return Color string
*/
public static String colorToString(int col) {
- return String.format("#%02x%02x%02x", (col >>> 16) & 0xFF, (col >>> 8) & 0xFF, col & 0xFF);
+ char[] buf = new char[] { '#', '0', '0', '0', '0', '0', '0' };
+ for (int i = 7; i > 0; i--) {
+ buf[i] += (col & 0xF);
+ col >>= 4;
+ }
+ return new String(buf);
}
/**
@@ -526,8 +545,8 @@ public final class SVGUtil {
double swidth = iwidth + lmargin + rmargin;
double sheight = iheight + tmargin + bmargin;
double scale = Math.max(swidth / owidth, sheight / oheight);
- double offx = (scale * owidth - swidth) / 2 + lmargin;
- double offy = (scale * oheight - sheight) / 2 + tmargin;
+ double offx = (scale * owidth - swidth) * .5 + lmargin;
+ double offy = (scale * oheight - sheight) * .5 + tmargin;
return "scale(" + fmt(1 / scale) + ") translate(" + fmt(offx) + " " + fmt(offy) + ")";
}
@@ -578,8 +597,7 @@ public final class SVGUtil {
cPt.setX(gnme.getClientX());
cPt.setY(gnme.getClientY());
return cPt.matrixTransform(imat);
- }
- catch(Exception e) {
+ } catch (Exception e) {
LoggingUtil.warning("Error getting coordinates from SVG event.", e);
return null;
}
@@ -592,7 +610,7 @@ public final class SVGUtil {
*/
public static void removeLastChild(Element tag) {
final Node last = tag.getLastChild();
- if(last != null) {
+ if (last != null) {
tag.removeChild(last);
}
}
@@ -603,8 +621,8 @@ public final class SVGUtil {
* @param elem Element to remove
*/
public static void removeFromParent(Element elem) {
- if(elem != null) {
- if(elem.getParentNode() != null) {
+ if (elem != null) {
+ if (elem.getParentNode() != null) {
elem.getParentNode().removeChild(elem);
}
}
@@ -625,25 +643,25 @@ public final class SVGUtil {
public static Element svgCircleSegment(SVGPlot svgp, double centerx, double centery, double angleStart, double angleDelta, double innerRadius, double outerRadius) {
double sin1st = Math.sin(angleStart);
double cos1st = Math.cos(angleStart);
-
+
double sin2nd = Math.sin(angleStart + angleDelta);
double cos2nd = Math.cos(angleStart + angleDelta);
-
+
double inner1stx = centerx + (innerRadius * sin1st);
double inner1sty = centery - (innerRadius * cos1st);
double outer1stx = centerx + (outerRadius * sin1st);
double outer1sty = centery - (outerRadius * cos1st);
-
+
double inner2ndx = centerx + (innerRadius * sin2nd);
double inner2ndy = centery - (innerRadius * cos2nd);
double outer2ndx = centerx + (outerRadius * sin2nd);
double outer2ndy = centery - (outerRadius * cos2nd);
-
+
double largeArc = 0;
- if(angleDelta >= Math.PI) {
+ if (angleDelta >= Math.PI) {
largeArc = 1;
}
-
+
SVGPath path = new SVGPath(inner1stx, inner1sty);
path.lineTo(outer1stx, outer1sty);
path.ellipticalArc(outerRadius, outerRadius, 0, largeArc, 1, outer2ndx, outer2ndy);
@@ -651,7 +669,7 @@ public final class SVGUtil {
if (innerRadius > 0) {
path.ellipticalArc(innerRadius, innerRadius, 0, largeArc, 0, inner1stx, inner1sty);
}
-
+
return path.makeElement(svgp);
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/svg/VoronoiDraw.java b/src/de/lmu/ifi/dbs/elki/visualization/svg/VoronoiDraw.java
index 317a44ae..2997456d 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/svg/VoronoiDraw.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/svg/VoronoiDraw.java
@@ -35,6 +35,9 @@ import de.lmu.ifi.dbs.elki.visualization.projections.Projection2D;
*
* @author Robert Rödler
* @author Erich Schubert
+ *
+ * @apiviz.uses de.lmu.ifi.dbs.elki.math.geometry.SweepHullDelaunay2D.Triangle
+ * @apiviz.uses Projection2D
*/
public class VoronoiDraw {
/**
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/AbstractVisFactory.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/AbstractVisFactory.java
index 40ae5cdf..565cf784 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/AbstractVisFactory.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/AbstractVisFactory.java
@@ -32,6 +32,7 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.thumbs.ThumbnailVisualizati
* @author Remigius Wojdanowski
*
* @apiviz.uses ThumbnailVisualization oneway - - «create»
+ * @apiviz.excludeSubtypes
*/
public abstract class AbstractVisFactory implements VisFactory {
/**
@@ -49,8 +50,7 @@ public abstract class AbstractVisFactory implements VisFactory {
@Override
public Visualization makeVisualizationOrThumbnail(VisualizationTask task) {
// Is this a thumbnail request?
- Boolean isthumb = task.get(VisualizationTask.THUMBNAIL, Boolean.class);
- if (isthumb != null && isthumb.booleanValue() && allowThumbnails(task)) {
+ if (task.thumbnail && allowThumbnails(task)) {
return new ThumbnailVisualization(this, task, thumbmask);
}
return makeVisualization(task);
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/AbstractVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/AbstractVisualization.java
index bbff0117..b023827d 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/AbstractVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/AbstractVisualization.java
@@ -37,6 +37,7 @@ import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
* Abstract base class for visualizations.
*
* @author Erich Schubert
+ * @apiviz.excludeSubtypes
*/
public abstract class AbstractVisualization implements Visualization, ResultListener {
/**
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/StaticVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/StaticVisualizationInstance.java
index 3a4be45b..3cd24289 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/StaticVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/StaticVisualizationInstance.java
@@ -32,14 +32,14 @@ import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
*
* @author Erich Schubert
*/
-public class StaticVisualization extends AbstractVisualization {
+public class StaticVisualizationInstance extends AbstractVisualization {
/**
* Unchanging precomputed visualization.
*
* @param task Task to visualize
* @param element Element containing the resulting visualization
*/
- public StaticVisualization(VisualizationTask task, Element element) {
+ public StaticVisualizationInstance(VisualizationTask task, Element element) {
super(task);
this.layer = element;
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/VisualizerUtil.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/VisualizerUtil.java
index 1bc9b332..389aec52 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/VisualizerUtil.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/VisualizerUtil.java
@@ -47,6 +47,14 @@ import de.lmu.ifi.dbs.elki.visualization.VisualizerContext;
*/
public final class VisualizerUtil {
/**
+ * Fake constructor: do not instantiate.
+ *
+ */
+ private VisualizerUtil() {
+ // Do not instantiate.
+ }
+
+ /**
* Find the visualizer context in a result tree.
*
* @param baseResult base result to start searching at.
@@ -54,33 +62,14 @@ public final class VisualizerUtil {
*/
public static VisualizerContext getContext(HierarchicalResult baseResult) {
List<VisualizerContext> contexts = ResultUtil.filterResults(baseResult, VisualizerContext.class);
- if(!contexts.isEmpty()) {
+ if (!contexts.isEmpty()) {
return contexts.get(0);
- }
- else {
+ } else {
return null;
}
}
/**
- * Utility function to test for Visualizer visibility.
- *
- * @param task Visualization task
- * @return true when visible
- */
- public static boolean isVisible(VisualizationTask task) {
- // Currently enabled?
- Boolean enabled = task.getGenerics(VisualizationTask.META_VISIBLE, Boolean.class);
- if(enabled == null) {
- enabled = task.getGenerics(VisualizationTask.META_VISIBLE_DEFAULT, Boolean.class);
- }
- if(enabled == null) {
- enabled = true;
- }
- return enabled;
- }
-
- /**
* Utility function to change Visualizer visibility.
*
* @param task Visualization task
@@ -88,10 +77,9 @@ public final class VisualizerUtil {
*/
public static void setVisible(VisualizationTask task, boolean visibility) {
VisualizerContext context = task.getContext();
- if(context != null) {
+ if (context != null) {
setVisible(context, task, visibility);
- }
- else {
+ } else {
LoggingUtil.warning("setVisible called without context in task.", new Throwable());
}
}
@@ -105,78 +93,49 @@ public final class VisualizerUtil {
*/
public static void setVisible(VisualizerContext context, VisualizationTask task, boolean visibility) {
// Hide other tools
- if(visibility && VisualizerUtil.isTool(task)) {
+ if (visibility && task.tool) {
final List<VisualizationTask> visualizers = ResultUtil.filterResults(context.getResult(), VisualizationTask.class);
- for(VisualizationTask other : visualizers) {
- if(other != task && VisualizerUtil.isTool(other) && VisualizerUtil.isVisible(other)) {
- other.put(VisualizationTask.META_VISIBLE, false);
+ for (VisualizationTask other : visualizers) {
+ if (other != task && other.tool && other.visible) {
+ other.visible = false;
context.getHierarchy().resultChanged(other);
}
}
}
- task.put(VisualizationTask.META_VISIBLE, visibility);
+ task.visible = visibility;
context.getHierarchy().resultChanged(task);
}
/**
- * Utility function to test for a visualizer being a "tool".
- *
- * @param vis Visualizer to test
- * @return true for a tool
- */
- public static boolean isTool(VisualizationTask vis) {
- // Currently enabled?
- Boolean tool = vis.getGenerics(VisualizationTask.META_TOOL, Boolean.class);
- return (tool != null) && tool;
- }
-
- /**
- * Utility function to test for a visualizer being "no export".
- *
- * @param vis Visualizer to test
- * @return true when not to export
- */
- public static boolean isNoExport(VisualizationTask vis) {
- // Currently enabled?
- Boolean noexport = vis.getGenerics(VisualizationTask.META_NOEXPORT, Boolean.class);
- return (noexport != null) && noexport;
- }
-
- /**
- * Utility function to test for a visualizer having options.
- *
- * @param vis Visualizer to test
- * @return true when it has options
- */
- public static boolean hasOptions(VisualizationTask vis) {
- // Currently enabled?
- Boolean hasoptions = vis.getGenerics(VisualizationTask.META_HAS_OPTIONS, Boolean.class);
- return (hasoptions != null) && hasoptions;
- }
-
- /**
- * Filter for number vector field representations
+ * Filter for number vector field representations.
*
* @param result Result to filter
* @return Iterator over suitable representations
*/
// TODO: move to DatabaseUtil?
- public static Iterator<Relation<? extends NumberVector<?, ?>>> iterateVectorFieldRepresentations(final Result result) {
+ public static Iterator<Relation<? extends NumberVector<?>>> iterateVectorFieldRepresentations(final Result result) {
List<Relation<?>> parent = ResultUtil.filterResults(result, Relation.class);
return new VectorspaceIterator(parent.iterator());
}
-
+
/**
- * Iterate over vectorspace
+ * Iterate over vectorspace.
*
* @author Erich Schubert
*
* @apiviz.exclude
*/
- private static class VectorspaceIterator extends AbstractFilteredIterator<Relation<?>, Relation<? extends NumberVector<?, ?>>> {
- /** Parent iterator */
+ private static class VectorspaceIterator extends AbstractFilteredIterator<Relation<?>, Relation<? extends NumberVector<?>>> {
+ /**
+ * Parent iterator.
+ */
private Iterator<Relation<?>> parent;
+ /**
+ * Constructor.
+ *
+ * @param parent Parent iterator
+ */
public VectorspaceIterator(Iterator<Relation<?>> parent) {
super();
this.parent = parent;
@@ -189,37 +148,15 @@ public final class VisualizerUtil {
@SuppressWarnings("unchecked")
@Override
- protected Relation<? extends NumberVector<?, ?>> testFilter(Relation<?> nextobj) {
+ protected Relation<? extends NumberVector<?>> testFilter(Relation<?> nextobj) {
final SimpleTypeInformation<?> type = nextobj.getDataTypeInformation();
- if(!NumberVector.class.isAssignableFrom(type.getRestrictionClass())) {
+ if (!NumberVector.class.isAssignableFrom(type.getRestrictionClass())) {
return null;
}
- if(!(type instanceof VectorFieldTypeInformation)) {
+ if (!(type instanceof VectorFieldTypeInformation)) {
return null;
}
- return (Relation<? extends NumberVector<?, ?>>) nextobj;
+ return (Relation<? extends NumberVector<?>>) nextobj;
}
};
-
- /**
- * Test whether a thumbnail is enabled for this visualizer.
- *
- * @param vis Visualizer
- * @return boolean
- */
- public static boolean thumbnailEnabled(VisualizationTask vis) {
- Boolean nothumb = vis.getGenerics(VisualizationTask.META_NOTHUMB, Boolean.class);
- return (nothumb == null) || !nothumb;
- }
-
- /**
- * Test whether a detail plot is available for this task.
- *
- * @param vis Task
- * @return boolean
- */
- public static boolean detailsEnabled(VisualizationTask vis) {
- Boolean nodetail = vis.getGenerics(VisualizationTask.META_NODETAIL, Boolean.class);
- return (nodetail == null) || !nodetail;
- }
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/histogram/ColoredHistogramVisualizer.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/histogram/ColoredHistogramVisualizer.java
index e300a3c4..7a52c8b3 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/histogram/ColoredHistogramVisualizer.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/histogram/ColoredHistogramVisualizer.java
@@ -28,19 +28,20 @@ import java.util.Collection;
import org.apache.batik.util.SVGConstants;
import org.w3c.dom.Element;
+import de.lmu.ifi.dbs.elki.data.DoubleVector;
import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.logging.LoggingUtil;
import de.lmu.ifi.dbs.elki.math.DoubleMinMax;
-import de.lmu.ifi.dbs.elki.math.histograms.AggregatingHistogram;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector;
import de.lmu.ifi.dbs.elki.math.scales.LinearScale;
import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
import de.lmu.ifi.dbs.elki.result.SamplingResult;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
+import de.lmu.ifi.dbs.elki.utilities.datastructures.histogram.DoubleArrayStaticHistogram;
import de.lmu.ifi.dbs.elki.utilities.exceptions.ObjectNotFoundException;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
@@ -48,7 +49,6 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterEqualCons
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.Flag;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
-import de.lmu.ifi.dbs.elki.utilities.pairs.DoubleObjPair;
import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
import de.lmu.ifi.dbs.elki.visualization.colors.ColorLibrary;
import de.lmu.ifi.dbs.elki.visualization.css.CSSClass;
@@ -72,279 +72,301 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.thumbs.ThumbnailVisualizati
* of the database's objects.
*
* @author Remigius Wojdanowski
+ * @author Erich Schubert
*
- * @apiviz.has NumberVector oneway - - visualizes
- *
- * @param <NV> Type of the DatabaseObject being visualized.
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-// FIXME: make non-static, react to database changes!
-// FIXME: cache histogram instead of recomputing it.
-public class ColoredHistogramVisualizer<NV extends NumberVector<NV, ?>> extends AbstractHistogramVisualization {
+public class ColoredHistogramVisualizer extends AbstractVisFactory {
/**
* Name for this visualizer.
*/
private static final String CNAME = "Histograms";
/**
- * Generic tag to indicate the type of element. Used in IDs, CSS-Classes etc.
+ * Settings
*/
- public static final String BIN = "bin";
+ protected Parameterizer settings;
/**
- * Internal storage of the curves flag.
+ * Number of bins to use in histogram.
*/
- private boolean curves;
+ private static final int DEFAULT_BINS = 50;
/**
- * Number of bins to use in the histogram.
+ * Constructor.
+ *
+ * @param settings Settings
*/
- private int bins;
+ public ColoredHistogramVisualizer(Parameterizer settings) {
+ super();
+ this.settings = settings;
+ thumbmask |= ThumbnailVisualization.ON_DATA | ThumbnailVisualization.ON_STYLE;
+ }
- /**
- * The database we visualize
- */
- private Relation<NV> relation;
+ @Override
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance<DoubleVector>(task);
+ }
- /**
- * The style policy
- */
- private StyleResult style;
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ // Find a style result to visualize:
+ Collection<StyleResult> styleres = ResultUtil.filterResults(result, StyleResult.class);
+ for (StyleResult c : styleres) {
+ Collection<HistogramProjector<?>> ps = ResultUtil.filterResults(baseResult, HistogramProjector.class);
+ for (HistogramProjector<?> p : ps) {
+ // register self
+ final VisualizationTask task = new VisualizationTask(CNAME, c, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_DATA;
+ baseResult.getHierarchy().add(c, task);
+ baseResult.getHierarchy().add(p, task);
+ }
+ }
+ }
- /**
- * Sampling result
- */
- private SamplingResult sample;
+ @Override
+ public boolean allowThumbnails(VisualizationTask task) {
+ // Don't use thumbnails
+ return false;
+ }
/**
- * Constructor.
+ * Instance
+ *
+ * @author Remigius Wojdanowski
*
- * @param task Visualization task
- * @param curves Curves flag
- * @param bins Number of bins
+ * @apiviz.has NumberVector oneway - - visualizes
+ *
+ * @param <NV> Type of the DatabaseObject being visualized.
*/
- public ColoredHistogramVisualizer(VisualizationTask task, boolean curves, int bins) {
- super(task);
- this.curves = curves;
- this.bins = bins;
- this.relation = task.getRelation();
- this.style = task.getResult();
- this.sample = ResultUtil.getSamplingResult(relation);
- context.addResultListener(this);
- }
+ // FIXME: make non-static, react to database changes!
+ // FIXME: cache histogram instead of recomputing it.
+ public class Instance<NV extends NumberVector<?>> extends AbstractHistogramVisualization {
+ /**
+ * Generic tag to indicate the type of element. Used in IDs, CSS-Classes
+ * etc.
+ */
+ public static final String BIN = "bin";
- @Override
- public void destroy() {
- context.removeResultListener(this);
- super.destroy();
- }
+ /**
+ * The database we visualize
+ */
+ private Relation<NV> relation;
- @Override
- protected void redraw() {
- double margin = context.getStyleLibrary().getSize(StyleLibrary.MARGIN);
- layer = SVGUtil.svgElement(svgp.getDocument(), SVGConstants.SVG_G_TAG);
- double xsize = Projection.SCALE * task.getWidth() / task.getHeight();
- double ysize = Projection.SCALE;
-
- final String transform = SVGUtil.makeMarginTransform(task.getWidth(), task.getHeight(), xsize, ysize, margin);
- SVGUtil.setAtt(layer, SVGConstants.SVG_TRANSFORM_ATTRIBUTE, transform);
-
- // Styling policy
- final StylingPolicy spol = style.getStylingPolicy();
- final ClassStylingPolicy cspol;
- if(spol instanceof ClassStylingPolicy) {
- cspol = (ClassStylingPolicy) spol;
+ /**
+ * The style policy
+ */
+ private StyleResult style;
+
+ /**
+ * Sampling result
+ */
+ private SamplingResult sample;
+
+ /**
+ * Constructor.
+ *
+ * @param task Visualization task
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
+ this.relation = task.getRelation();
+ this.style = task.getResult();
+ this.sample = ResultUtil.getSamplingResult(relation);
+ context.addResultListener(this);
}
- else {
- cspol = null;
+
+ @Override
+ public void destroy() {
+ context.removeResultListener(this);
+ super.destroy();
}
- // TODO also use min style?
- setupCSS(svgp, (cspol != null) ? cspol.getMaxStyle() : 0);
-
- // Create histograms
- final int off = (cspol != null) ? cspol.getMinStyle() : 0;
- final int numc = (cspol != null) ? (cspol.getMaxStyle() - cspol.getMinStyle()) : 0;
- DoubleMinMax minmax = new DoubleMinMax();
- final double frac = 1. / relation.size(); // TODO: sampling?
- final int cols = numc + 1;
- AggregatingHistogram<double[], double[]> histogram = new AggregatingHistogram<double[], double[]>(bins, -.5, .5, new AggregatingHistogram.Adapter<double[], double[]>() {
- @Override
- public double[] aggregate(double[] existing, double[] data) {
- for(int i = 0; i < existing.length; i++) {
- existing[i] += data[i];
- }
- return existing;
- }
- @Override
- public double[] make() {
- return new double[cols];
+ @Override
+ protected void redraw() {
+ double margin = style.getStyleLibrary().getSize(StyleLibrary.MARGIN);
+ layer = SVGUtil.svgElement(svgp.getDocument(), SVGConstants.SVG_G_TAG);
+ double xsize = Projection.SCALE * task.getWidth() / task.getHeight();
+ double ysize = Projection.SCALE;
+
+ final String transform = SVGUtil.makeMarginTransform(task.getWidth(), task.getHeight(), xsize, ysize, margin);
+ SVGUtil.setAtt(layer, SVGConstants.SVG_TRANSFORM_ATTRIBUTE, transform);
+
+ // Styling policy
+ final StylingPolicy spol = style.getStylingPolicy();
+ final ClassStylingPolicy cspol;
+ if (spol instanceof ClassStylingPolicy) {
+ cspol = (ClassStylingPolicy) spol;
+ } else {
+ cspol = null;
}
- });
-
- if(cspol != null) {
- for(int snum = 0; snum < numc; snum++) {
+ // TODO also use min style?
+ setupCSS(svgp, (cspol != null) ? cspol.getMaxStyle() : 0);
+
+ // Create histograms
+ final int off = (cspol != null) ? cspol.getMinStyle() : 0;
+ final int numc = (cspol != null) ? (cspol.getMaxStyle() - cspol.getMinStyle()) : 0;
+ DoubleMinMax minmax = new DoubleMinMax();
+ final double frac = 1. / relation.size(); // TODO: sampling?
+ final int cols = numc + 1;
+ DoubleArrayStaticHistogram histogram = new DoubleArrayStaticHistogram(settings.bins, -.5, .5, cols);
+
+ if (cspol != null) {
+ for (int snum = 0; snum < numc; snum++) {
+ double[] inc = new double[cols];
+ inc[0] = frac;
+ inc[snum + 1] = frac;
+ for (DBIDIter iter = cspol.iterateClass(snum + off); iter.valid(); iter.advance()) {
+ if (!sample.getSample().contains(iter)) {
+ continue; // TODO: can we test more efficiently than this?
+ }
+ try {
+ double pos = proj.fastProjectDataToRenderSpace(relation.get(iter)) / Projection.SCALE;
+ histogram.increment(pos, inc);
+ } catch (ObjectNotFoundException e) {
+ // Ignore. The object was probably deleted from the database
+ }
+ }
+ }
+ } else {
+ // Actual data distribution.
double[] inc = new double[cols];
inc[0] = frac;
- inc[snum + 1] = frac;
- for(DBIDIter iter = cspol.iterateClass(snum + off); iter.valid(); iter.advance()) {
- if(!sample.getSample().contains(iter)) {
- continue; // TODO: can we test more efficiently than this?
- }
- try {
- double pos = proj.fastProjectDataToRenderSpace(relation.get(iter)) / Projection.SCALE;
- histogram.aggregate(pos, inc);
- }
- catch(ObjectNotFoundException e) {
- // Ignore. The object was probably deleted from the database
- }
+ for (DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
+ double pos = proj.fastProjectDataToRenderSpace(relation.get(iditer)) / Projection.SCALE;
+ histogram.increment(pos, inc);
}
}
- }
- else {
- // Actual data distribution.
- double[] inc = new double[cols];
- inc[0] = frac;
- for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
- double pos = proj.fastProjectDataToRenderSpace(relation.get(iditer)) / Projection.SCALE;
- histogram.aggregate(pos, inc);
- }
- }
- // for scaling, get the maximum occurring value in the bins:
- for(DoubleObjPair<double[]> bin : histogram) {
- for(double val : bin.second) {
- minmax.put(val);
+ // for scaling, get the maximum occurring value in the bins:
+ for (DoubleArrayStaticHistogram.Iter iter = histogram.iter(); iter.valid(); iter.advance()) {
+ for (double val : iter.getValue()) {
+ minmax.put(val);
+ }
}
- }
- LinearScale yscale = new LinearScale(0, minmax.getMax());
- LinearScale xscale = new LinearScale(histogram.getCoverMinimum(), histogram.getCoverMaximum());
-
- // Axis. TODO: Add an AxisVisualizer for this?
- try {
- SVGSimpleLinearAxis.drawAxis(svgp, layer, yscale, 0, ysize, 0, 0, SVGSimpleLinearAxis.LabelStyle.LEFTHAND, context.getStyleLibrary());
-
- // draw axes that are non-trivial
- final int dimensionality = DatabaseUtil.dimensionality(relation);
- double orig = proj.fastProjectScaledToRender(new Vector(dimensionality));
- for(int d = 0; d < dimensionality; d++) {
- Vector v = new Vector(dimensionality);
- v.set(d, 1);
- // projected endpoint of axis
- double ax = proj.fastProjectScaledToRender(v);
- if(ax != orig) {
- final double left = (orig / Projection.SCALE + 0.5) * xsize;
- final double right = (ax / Projection.SCALE + 0.5) * xsize;
- SVGSimpleLinearAxis.drawAxis(svgp, layer, proj.getScale(d), left, ysize, right, ysize, SVGSimpleLinearAxis.LabelStyle.RIGHTHAND, context.getStyleLibrary());
+ LinearScale yscale = new LinearScale(0, minmax.getMax());
+ LinearScale xscale = new LinearScale(histogram.getCoverMinimum(), histogram.getCoverMaximum());
+
+ // Axis. TODO: Add an AxisVisualizer for this?
+ try {
+ SVGSimpleLinearAxis.drawAxis(svgp, layer, yscale, 0, ysize, 0, 0, SVGSimpleLinearAxis.LabelStyle.LEFTHAND, style.getStyleLibrary());
+
+ // draw axes that are non-trivial
+ final int dimensionality = RelationUtil.dimensionality(relation);
+ double orig = proj.fastProjectScaledToRender(new Vector(dimensionality));
+ for (int d = 0; d < dimensionality; d++) {
+ Vector v = new Vector(dimensionality);
+ v.set(d, 1);
+ // projected endpoint of axis
+ double ax = proj.fastProjectScaledToRender(v);
+ if (ax < orig || ax > orig) {
+ final double left = (orig / Projection.SCALE + 0.5) * xsize;
+ final double right = (ax / Projection.SCALE + 0.5) * xsize;
+ SVGSimpleLinearAxis.drawAxis(svgp, layer, proj.getScale(d), left, ysize, right, ysize, SVGSimpleLinearAxis.LabelStyle.RIGHTHAND, style.getStyleLibrary());
+ }
}
+ } catch (CSSNamingConflict e) {
+ LoggingUtil.exception("CSS class exception in axis class.", e);
}
- }
- catch(CSSNamingConflict e) {
- LoggingUtil.exception("CSS class exception in axis class.", e);
- }
- double binwidth = histogram.getBinsize();
- // Visualizing
- if(!curves) {
- for(DoubleObjPair<double[]> bin : histogram) {
- double lpos = xscale.getScaled(bin.first - binwidth / 2);
- double rpos = xscale.getScaled(bin.first + binwidth / 2);
- double stack = 0.0;
- final int start = numc > 0 ? 1 : 0;
- for(int key = start; key < cols; key++) {
- double val = yscale.getScaled(bin.getSecond()[key]);
- Element row = SVGUtil.svgRect(svgp.getDocument(), xsize * lpos, ysize * (1 - (val + stack)), xsize * (rpos - lpos), ysize * val);
- stack = stack + val;
- SVGUtil.addCSSClass(row, BIN + (off + key - 1));
- layer.appendChild(row);
+ // Visualizing
+ if (!settings.curves) {
+ for (DoubleArrayStaticHistogram.Iter iter = histogram.iter(); iter.valid(); iter.advance()) {
+ double lpos = xscale.getScaled(iter.getLeft());
+ double rpos = xscale.getScaled(iter.getRight());
+ double stack = 0.0;
+ final int start = numc > 0 ? 1 : 0;
+ for (int key = start; key < cols; key++) {
+ double val = yscale.getScaled(iter.getValue()[key]);
+ Element row = SVGUtil.svgRect(svgp.getDocument(), xsize * lpos, ysize * (1 - (val + stack)), xsize * (rpos - lpos), ysize * val);
+ stack = stack + val;
+ SVGUtil.addCSSClass(row, BIN + (off + key - 1));
+ layer.appendChild(row);
+ }
+ }
+ } else {
+ double left = xscale.getScaled(histogram.getCoverMinimum());
+ double right = left;
+
+ SVGPath[] paths = new SVGPath[cols];
+ double[] lasty = new double[cols];
+ for (int i = 0; i < cols; i++) {
+ paths[i] = new SVGPath(xsize * left, ysize * 1);
+ lasty[i] = 0;
}
- }
- }
- else {
- double left = xscale.getScaled(histogram.getCoverMinimum());
- double right = left;
-
- SVGPath[] paths = new SVGPath[cols];
- double[] lasty = new double[cols];
- for(int i = 0; i < cols; i++) {
- paths[i] = new SVGPath(xsize * left, ysize * 1);
- lasty[i] = 0;
- }
- // draw histogram lines
- for(DoubleObjPair<double[]> bin : histogram) {
- left = xscale.getScaled(bin.first - binwidth / 2);
- right = xscale.getScaled(bin.first + binwidth / 2);
- for(int i = 0; i < cols; i++) {
- double val = yscale.getScaled(bin.getSecond()[i]);
- if(lasty[i] != val) {
- paths[i].lineTo(xsize * left, ysize * (1 - lasty[i]));
- paths[i].lineTo(xsize * left, ysize * (1 - val));
- paths[i].lineTo(xsize * right, ysize * (1 - val));
- lasty[i] = val;
+ // draw histogram lines
+ for (DoubleArrayStaticHistogram.Iter iter = histogram.iter(); iter.valid(); iter.advance()) {
+ left = xscale.getScaled(iter.getLeft());
+ right = xscale.getScaled(iter.getRight());
+ for (int i = 0; i < cols; i++) {
+ double val = yscale.getScaled(iter.getValue()[i]);
+ if (lasty[i] > val || lasty[i] < val) {
+ paths[i].lineTo(xsize * left, ysize * (1 - lasty[i]));
+ paths[i].lineTo(xsize * left, ysize * (1 - val));
+ paths[i].lineTo(xsize * right, ysize * (1 - val));
+ lasty[i] = val;
+ }
}
}
- }
- // close and insert all lines.
- for(int i = 0; i < cols; i++) {
- if(lasty[i] != 0) {
- paths[i].lineTo(xsize * right, ysize * (1 - lasty[i]));
+ // close and insert all lines.
+ for (int i = 0; i < cols; i++) {
+ if (lasty[i] != 0) {
+ paths[i].lineTo(xsize * right, ysize * (1 - lasty[i]));
+ }
+ paths[i].lineTo(xsize * right, ysize * 1);
+ Element elem = paths[i].makeElement(svgp);
+ SVGUtil.addCSSClass(elem, BIN + (off + i - 1));
+ layer.appendChild(elem);
}
- paths[i].lineTo(xsize * right, ysize * 1);
- Element elem = paths[i].makeElement(svgp);
- SVGUtil.addCSSClass(elem, BIN + (off + i - 1));
- layer.appendChild(elem);
}
}
- }
- /**
- * Generate the needed CSS classes.
- *
- * @param svgp Plot context
- * @param numc Number of classes we need.
- */
- private void setupCSS(SVGPlot svgp, int numc) {
- ColorLibrary colors = context.getStyleLibrary().getColorSet(StyleLibrary.PLOT);
+ /**
+ * Generate the needed CSS classes.
+ *
+ * @param svgp Plot context
+ * @param numc Number of classes we need.
+ */
+ private void setupCSS(SVGPlot svgp, int numc) {
+ ColorLibrary colors = style.getStyleLibrary().getColorSet(StyleLibrary.PLOT);
+
+ CSSClass allInOne = new CSSClass(svgp, BIN + -1);
+ if (!settings.curves) {
+ allInOne.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_BLACK_VALUE);
+ allInOne.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, 1.0);
+ } else {
+ allInOne.setStatement(SVGConstants.CSS_STROKE_PROPERTY, SVGConstants.CSS_BLACK_VALUE);
+ allInOne.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getStyleLibrary().getLineWidth(StyleLibrary.PLOT));
+ allInOne.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
+ }
+ svgp.addCSSClassOrLogError(allInOne);
- CSSClass allInOne = new CSSClass(svgp, BIN + -1);
- if(!curves) {
- allInOne.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_BLACK_VALUE);
- allInOne.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, 1.0);
- }
- else {
- allInOne.setStatement(SVGConstants.CSS_STROKE_PROPERTY, SVGConstants.CSS_BLACK_VALUE);
- allInOne.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT));
- allInOne.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
- }
- svgp.addCSSClassOrLogError(allInOne);
+ for (int clusterID = 0; clusterID < numc; clusterID++) {
+ CSSClass bin = new CSSClass(svgp, BIN + clusterID);
- for(int clusterID = 0; clusterID < numc; clusterID++) {
- CSSClass bin = new CSSClass(svgp, BIN + clusterID);
+ if (!settings.curves) {
+ bin.setStatement(SVGConstants.CSS_FILL_PROPERTY, colors.getColor(clusterID));
+ } else {
+ bin.setStatement(SVGConstants.CSS_STROKE_PROPERTY, colors.getColor(clusterID));
+ bin.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getStyleLibrary().getLineWidth(StyleLibrary.PLOT));
+ bin.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
+ }
- if(!curves) {
- bin.setStatement(SVGConstants.CSS_FILL_PROPERTY, colors.getColor(clusterID));
- }
- else {
- bin.setStatement(SVGConstants.CSS_STROKE_PROPERTY, colors.getColor(clusterID));
- bin.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT));
- bin.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
+ svgp.addCSSClassOrLogError(bin);
}
-
- svgp.addCSSClassOrLogError(bin);
}
}
/**
- * Visualizer factory for 1D histograms
+ * Parameterization class.
*
* @author Erich Schubert
*
- * @apiviz.stereotype factory
- * @apiviz.uses ColoredHistogramVisualizer oneway - - «create»
- *
- * @param <NV> Number vector type
+ * @apiviz.exclude
*/
- public static class Factory<NV extends NumberVector<NV, ?>> extends AbstractVisFactory {
+ public static class Parameterizer extends AbstractParameterizer {
/**
* Flag to specify the "curves" rendering style.
*
@@ -352,7 +374,7 @@ public class ColoredHistogramVisualizer<NV extends NumberVector<NV, ?>> extends
* Key: {@code -histogram.curves}
* </p>
*/
- public static final OptionID STYLE_CURVES_ID = OptionID.getOrCreateOptionID("projhistogram.curves", "Use curves instead of the stacked histogram style.");
+ public static final OptionID STYLE_CURVES_ID = new OptionID("projhistogram.curves", "Use curves instead of the stacked histogram style.");
/**
* Parameter to specify the number of bins to use in histogram.
@@ -361,98 +383,35 @@ public class ColoredHistogramVisualizer<NV extends NumberVector<NV, ?>> extends
* Key: {@code -projhistogram.bins} Default: 20
* </p>
*/
- public static final OptionID HISTOGRAM_BINS_ID = OptionID.getOrCreateOptionID("projhistogram.bins", "Number of bins in the distribution histogram");
+ public static final OptionID HISTOGRAM_BINS_ID = new OptionID("projhistogram.bins", "Number of bins in the distribution histogram");
/**
* Internal storage of the curves flag.
*/
- private boolean curves;
-
- /**
- * Number of bins to use in histogram.
- */
- private static final int DEFAULT_BINS = 50;
+ protected boolean curves = false;
/**
* Number of bins to use in the histogram.
*/
- private int bins = DEFAULT_BINS;
-
- /**
- * Constructor.
- *
- * @param curves
- * @param bins
- */
- public Factory(boolean curves, int bins) {
- super();
- this.curves = curves;
- this.bins = bins;
- thumbmask |= ThumbnailVisualization.ON_DATA | ThumbnailVisualization.ON_STYLE;
- }
-
- @Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new ColoredHistogramVisualizer<NV>(task, curves, bins);
- }
+ protected int bins = DEFAULT_BINS;
@Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- // Find a style result to visualize:
- Collection<StyleResult> styleres = ResultUtil.filterResults(result, StyleResult.class);
- for(StyleResult c : styleres) {
- Collection<HistogramProjector<?>> ps = ResultUtil.filterResults(baseResult, HistogramProjector.class);
- for(HistogramProjector<?> p : ps) {
- // register self
- final VisualizationTask task = new VisualizationTask(CNAME, c, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA);
- baseResult.getHierarchy().add(c, task);
- baseResult.getHierarchy().add(p, task);
- }
+ protected void makeOptions(Parameterization config) {
+ super.makeOptions(config);
+ Flag curvesF = new Flag(STYLE_CURVES_ID);
+ if (config.grab(curvesF)) {
+ curves = curvesF.isTrue();
+ }
+ IntParameter binsP = new IntParameter(HISTOGRAM_BINS_ID, DEFAULT_BINS);
+ binsP.addConstraint(new GreaterEqualConstraint(2));
+ if (config.grab(binsP)) {
+ bins = binsP.intValue();
}
}
@Override
- public boolean allowThumbnails(VisualizationTask task) {
- // Don't use thumbnails
- return false;
- }
-
- /**
- * Parameterization class.
- *
- * @author Erich Schubert
- *
- * @apiviz.exclude
- */
- public static class Parameterizer<NV extends NumberVector<NV, ?>> extends AbstractParameterizer {
- /**
- * Internal storage of the curves flag.
- */
- private boolean curves;
-
- /**
- * Number of bins to use in the histogram.
- */
- private int bins = DEFAULT_BINS;
-
- @Override
- protected void makeOptions(Parameterization config) {
- super.makeOptions(config);
- Flag STYLE_CURVES_FLAG = new Flag(STYLE_CURVES_ID);
- if(config.grab(STYLE_CURVES_FLAG)) {
- curves = STYLE_CURVES_FLAG.getValue();
- }
- IntParameter HISTOGRAM_BINS_PARAM = new IntParameter(HISTOGRAM_BINS_ID, new GreaterEqualConstraint(2), DEFAULT_BINS);
- if(config.grab(HISTOGRAM_BINS_PARAM)) {
- bins = HISTOGRAM_BINS_PARAM.getValue();
- }
- }
-
- @Override
- protected Factory<NV> makeInstance() {
- return new Factory<NV>(curves, bins);
- }
+ protected ColoredHistogramVisualizer makeInstance() {
+ return new ColoredHistogramVisualizer(this);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/AbstractOPTICSVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/AbstractOPTICSVisualization.java
index a04a4f7f..329fc64a 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/AbstractOPTICSVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/AbstractOPTICSVisualization.java
@@ -30,7 +30,7 @@ import org.apache.batik.util.SVGConstants;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
import de.lmu.ifi.dbs.elki.result.optics.ClusterOrderEntry;
import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
-import de.lmu.ifi.dbs.elki.visualization.projector.OPTICSProjector;
+import de.lmu.ifi.dbs.elki.visualization.projections.OPTICSProjection;
import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisualization;
@@ -40,7 +40,7 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisualization;
*
* @author Erich Schubert
*
- * @apiviz.uses OPTICSProjector
+ * @apiviz.uses OPTICSProjection
*
* @param <D>
*/
@@ -48,7 +48,7 @@ public abstract class AbstractOPTICSVisualization<D extends Distance<D>> extends
/**
* The plot
*/
- final protected OPTICSProjector<D> optics;
+ final protected OPTICSProjection<D> optics;
/**
* Width of plot (in display units)
@@ -67,7 +67,7 @@ public abstract class AbstractOPTICSVisualization<D extends Distance<D>> extends
*/
public AbstractOPTICSVisualization(VisualizationTask task) {
super(task);
- this.optics = task.getResult();
+ this.optics = task.getProj();
}
/**
@@ -76,9 +76,9 @@ public abstract class AbstractOPTICSVisualization<D extends Distance<D>> extends
protected void makeLayerElement() {
plotwidth = StyleLibrary.SCALE;
plotheight = StyleLibrary.SCALE / optics.getOPTICSPlot(context).getRatio();
- final double margin = context.getStyleLibrary().getSize(StyleLibrary.MARGIN);
+ final double margin = context.getStyleResult().getStyleLibrary().getSize(StyleLibrary.MARGIN);
layer = SVGUtil.svgElement(svgp.getDocument(), SVGConstants.SVG_G_TAG);
- final String transform = SVGUtil.makeMarginTransform(task.getWidth(), task.getHeight(), plotwidth, plotheight, margin / 2);
+ final String transform = SVGUtil.makeMarginTransform(task.getWidth(), task.getHeight(), plotwidth, plotheight, margin * .5);
SVGUtil.setAtt(layer, SVGConstants.SVG_TRANSFORM_ATTRIBUTE, transform);
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSClusterVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSClusterVisualization.java
index 2369588b..e771e380 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSClusterVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSClusterVisualization.java
@@ -55,15 +55,14 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
*
* @author Erich Schubert
*
- * @apiviz.uses Clustering oneway - - «visualizes»
- *
- * @param <D> Distance type (actually unused)
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class OPTICSClusterVisualization<D extends Distance<D>> extends AbstractOPTICSVisualization<D> {
+public class OPTICSClusterVisualization extends AbstractVisFactory {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(OPTICSClusterVisualization.class);
+ private static final Logging LOG = Logging.getLogger(OPTICSClusterVisualization.class);
/**
* A short name characterizing this Visualizer.
@@ -71,30 +70,37 @@ public class OPTICSClusterVisualization<D extends Distance<D>> extends AbstractO
private static final String NAME = "OPTICS Cluster Ranges";
/**
- * CSS class for markers
+ * Constructor, adhering to
+ * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
*/
- protected static final String CSS_BRACKET = "opticsBracket";
+ public OPTICSClusterVisualization() {
+ super();
+ }
- /**
- * Optics clustering we visualize
- */
- public static final String CLUSTERING = "OPTICSClustering";
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<OPTICSProjector<?>> ops = ResultUtil.filterResults(result, OPTICSProjector.class);
+ for(OPTICSProjector<?> p : ops) {
+ final Clustering<OPTICSModel> ocl = findOPTICSClustering(baseResult);
+ if(ocl != null) {
+ final VisualizationTask task = new VisualizationTask(NAME, ocl, null, this);
+ task.level = VisualizationTask.LEVEL_DATA;
+ baseResult.getHierarchy().add(p, task);
+ }
+ }
+ // TODO: also run when a new clustering is added, instead of just new
+ // projections?
+ }
- /**
- * Our clustering
- */
- Clustering<OPTICSModel> clus;
+ @Override
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance<DoubleDistance>(task);
+ }
- /**
- * Constructor.
- *
- * @param task Visualization task
- */
- public OPTICSClusterVisualization(VisualizationTask task) {
- super(task);
- this.clus = task.getGenerics(CLUSTERING, Clustering.class);
- context.addResultListener(this);
- incrementalRedraw();
+ @Override
+ public boolean allowThumbnails(VisualizationTask task) {
+ // Don't use thumbnails
+ return false;
}
/**
@@ -115,118 +121,109 @@ public class OPTICSClusterVisualization<D extends Distance<D>> extends AbstractO
if(firstcluster.getModel() instanceof OPTICSModel) {
return (Clustering<OPTICSModel>) clus;
}
- } catch(Exception e) {
+ }
+ catch(Exception e) {
// Empty clustering? Shouldn't happen.
- logger.warning("Clustering with no cluster detected.", e);
+ LOG.warning("Clustering with no cluster detected.", e);
}
}
return null;
}
- @Override
- protected void redraw() {
- makeLayerElement();
- addCSSClasses();
-
- ColorLibrary colors = context.getStyleLibrary().getColorSet(StyleLibrary.PLOT);
- HashMap<Cluster<?>, String> colormap = new HashMap<Cluster<?>, String>();
- int cnum = 0;
- for (Cluster<?> c : clus.getAllClusters()) {
- colormap.put(c, colors.getColor(cnum));
- cnum++;
- }
- drawClusters(clus.getToplevelClusters(), 1, colormap);
- }
-
/**
- * Recursively draw clusters
- *
- * @param clusters Current set of clusters
- * @param depth Recursion depth
- * @param colormap Color mapping
- */
- private void drawClusters(List<Cluster<OPTICSModel>> clusters, int depth, Map<Cluster<?>,String> colormap) {
- final double scale = StyleLibrary.SCALE;
-
- for(Cluster<OPTICSModel> cluster : clusters) {
- try {
- OPTICSModel model = cluster.getModel();
- final double x1 = plotwidth * ((model.getStartIndex() + .25) / this.optics.getResult().getClusterOrder().size());
- final double x2 = plotwidth * ((model.getEndIndex() + .75) / this.optics.getResult().getClusterOrder().size());
- final double y = plotheight + depth * scale * 0.01;
- Element e = svgp.svgLine(x1, y, x2, y);
- SVGUtil.addCSSClass(e, CSS_BRACKET);
- String color = colormap.get(cluster);
- if (color != null) {
- SVGUtil.setAtt(e, SVGConstants.SVG_STYLE_ATTRIBUTE, SVGConstants.CSS_STROKE_PROPERTY+":"+color);
- }
- layer.appendChild(e);
- }
- catch(ClassCastException e) {
- logger.warning("Expected OPTICSModel, got: " + cluster.getModel().getClass().getSimpleName());
- }
- // Descend
- final List<Cluster<OPTICSModel>> children = cluster.getChildren();
- if(children != null) {
- drawClusters(children, depth + 1, colormap);
- }
- }
- }
-
- /**
- * Adds the required CSS-Classes
- */
- private void addCSSClasses() {
- // Class for the markers
- if(!svgp.getCSSClassManager().contains(CSS_BRACKET)) {
- final CSSClass cls = new CSSClass(this, CSS_BRACKET);
- cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, context.getStyleLibrary().getColor(StyleLibrary.PLOT));
- cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT));
- svgp.addCSSClassOrLogError(cls);
- }
- }
-
- /**
- * Factory class for OPTICS plot selections.
+ * Instance.
*
* @author Erich Schubert
*
- * @apiviz.stereotype factory
- * @apiviz.uses OPTICSPlotSelectionVisualization oneway - - «create»
+ * @apiviz.uses Clustering oneway - - «visualizes»
+ *
+ * @param <D> Distance type (actually unused)
*/
- public static class Factory extends AbstractVisFactory {
+ public class Instance<D extends Distance<D>> extends AbstractOPTICSVisualization<D> {
+ /**
+ * CSS class for markers
+ */
+ protected static final String CSS_BRACKET = "opticsBracket";
+
+ /**
+ * Our clustering
+ */
+ Clustering<OPTICSModel> clus;
+
/**
- * Constructor, adhering to
- * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
+ * Constructor.
+ *
+ * @param task Visualization task
*/
- public Factory() {
- super();
+ public Instance(VisualizationTask task) {
+ super(task);
+ this.clus = task.getResult();
+ context.addResultListener(this);
+ incrementalRedraw();
}
@Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<OPTICSProjector<?>> ops = ResultUtil.filterResults(result, OPTICSProjector.class);
- for(OPTICSProjector<?> p : ops) {
- final Clustering<OPTICSModel> ocl = findOPTICSClustering(baseResult);
- if(ocl != null) {
- final VisualizationTask task = new VisualizationTask(NAME, p, null, this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA);
- task.put(CLUSTERING, ocl);
- baseResult.getHierarchy().add(p, task);
- }
+ protected void redraw() {
+ makeLayerElement();
+ addCSSClasses();
+
+ ColorLibrary colors = context.getStyleResult().getStyleLibrary().getColorSet(StyleLibrary.PLOT);
+ HashMap<Cluster<?>, String> colormap = new HashMap<Cluster<?>, String>();
+ int cnum = 0;
+ for(Cluster<?> c : clus.getAllClusters()) {
+ colormap.put(c, colors.getColor(cnum));
+ cnum++;
}
- // TODO: also run when a new clustering is added, instead of just new projections?
+ drawClusters(clus.getToplevelClusters(), 1, colormap);
}
- @Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new OPTICSClusterVisualization<DoubleDistance>(task);
+ /**
+ * Recursively draw clusters
+ *
+ * @param clusters Current set of clusters
+ * @param depth Recursion depth
+ * @param colormap Color mapping
+ */
+ private void drawClusters(List<Cluster<OPTICSModel>> clusters, int depth, Map<Cluster<?>, String> colormap) {
+ final double scale = StyleLibrary.SCALE;
+
+ for(Cluster<OPTICSModel> cluster : clusters) {
+ try {
+ OPTICSModel model = cluster.getModel();
+ final double x1 = plotwidth * ((model.getStartIndex() + .25) / this.optics.getResult().getClusterOrder().size());
+ final double x2 = plotwidth * ((model.getEndIndex() + .75) / this.optics.getResult().getClusterOrder().size());
+ final double y = plotheight + depth * scale * 0.01;
+ Element e = svgp.svgLine(x1, y, x2, y);
+ SVGUtil.addCSSClass(e, CSS_BRACKET);
+ String color = colormap.get(cluster);
+ if(color != null) {
+ SVGUtil.setAtt(e, SVGConstants.SVG_STYLE_ATTRIBUTE, SVGConstants.CSS_STROKE_PROPERTY + ":" + color);
+ }
+ layer.appendChild(e);
+ }
+ catch(ClassCastException e) {
+ LOG.warning("Expected OPTICSModel, got: " + cluster.getModel().getClass().getSimpleName());
+ }
+ // Descend
+ final List<Cluster<OPTICSModel>> children = cluster.getChildren();
+ if(children != null) {
+ drawClusters(children, depth + 1, colormap);
+ }
+ }
}
- @Override
- public boolean allowThumbnails(VisualizationTask task) {
- // Don't use thumbnails
- return false;
+ /**
+ * Adds the required CSS-Classes
+ */
+ private void addCSSClasses() {
+ // Class for the markers
+ if(!svgp.getCSSClassManager().contains(CSS_BRACKET)) {
+ final CSSClass cls = new CSSClass(this, CSS_BRACKET);
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, style.getColor(StyleLibrary.PLOT));
+ cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.PLOT));
+ svgp.addCSSClassOrLogError(cls);
+ }
}
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSPlotCutVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSPlotCutVisualization.java
index 2bd5c63a..739e0ccd 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSPlotCutVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSPlotCutVisualization.java
@@ -52,247 +52,250 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
/**
* Visualizes a cut in an OPTICS Plot to select an Epsilon value and generate a
- * new clustering result
+ * new clustering result.
*
* @author Heidi Kolb
+ * @author Erich Schubert
*
- * @param <D> distance type
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class OPTICSPlotCutVisualization<D extends Distance<D>> extends AbstractOPTICSVisualization<D> implements DragableArea.DragListener {
+public class OPTICSPlotCutVisualization extends AbstractVisFactory {
/**
* A short name characterizing this Visualizer.
*/
private static final String NAME = "OPTICS Cut";
- /**
- * CSS-Styles
- */
- protected static final String CSS_LINE = "opticsPlotLine";
-
- /**
- * CSS-Styles
- */
- protected final static String CSS_EPSILON = "opticsPlotEpsilonValue";
-
- /**
- * The current epsilon value.
- */
- private double epsilon = 0.0;
-
- /**
- * Sensitive (clickable) area
- */
- private DragableArea eventarea = null;
-
- /**
- * The label element
- */
- private Element elemText = null;
-
- /**
- * The line element
- */
- private Element elementLine = null;
-
- /**
- * The drag handle element
- */
- private Element elementPoint = null;
-
- /**
- * Constructor.
- *
- * @param task Task
- */
- public OPTICSPlotCutVisualization(VisualizationTask task) {
- super(task);
+ public OPTICSPlotCutVisualization() {
+ super();
}
@Override
- protected void redraw() {
- incrementalRedraw();
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<OPTICSProjector<?>> ops = ResultUtil.filterResults(result, OPTICSProjector.class);
+ for(OPTICSProjector<?> p : ops) {
+ final VisualizationTask task = new VisualizationTask(NAME, p, null, this);
+ task.level = VisualizationTask.LEVEL_INTERACTIVE;
+ baseResult.getHierarchy().add(p, task);
+ }
}
@Override
- protected void incrementalRedraw() {
- if(layer == null) {
- makeLayerElement();
- addCSSClasses();
- }
-
- // TODO make the number of digits configurable
- final String label = (epsilon != 0.0) ? FormatUtil.format(epsilon, 4) : "";
- // compute absolute y-value of bar
- final double yAct = plotheight - getYFromEpsilon(epsilon);
-
- if(elemText == null) {
- elemText = svgp.svgText(StyleLibrary.SCALE * 1.05, yAct, label);
- SVGUtil.setAtt(elemText, SVGConstants.SVG_CLASS_ATTRIBUTE, CSS_EPSILON);
- layer.appendChild(elemText);
- }
- else {
- elemText.setTextContent(label);
- SVGUtil.setAtt(elemText, SVGConstants.SVG_Y_ATTRIBUTE, yAct);
- }
-
- // line and handle
- if(elementLine == null) {
- elementLine = svgp.svgLine(0, yAct, StyleLibrary.SCALE * 1.04, yAct);
- SVGUtil.addCSSClass(elementLine, CSS_LINE);
- layer.appendChild(elementLine);
- }
- else {
- SVGUtil.setAtt(elementLine, SVG12Constants.SVG_Y1_ATTRIBUTE, yAct);
- SVGUtil.setAtt(elementLine, SVG12Constants.SVG_Y2_ATTRIBUTE, yAct);
- }
- if(elementPoint == null) {
- elementPoint = svgp.svgCircle(StyleLibrary.SCALE * 1.04, yAct, StyleLibrary.SCALE * 0.004);
- SVGUtil.addCSSClass(elementPoint, CSS_LINE);
- layer.appendChild(elementPoint);
- }
- else {
- SVGUtil.setAtt(elementPoint, SVG12Constants.SVG_CY_ATTRIBUTE, yAct);
- }
-
- if(eventarea == null) {
- eventarea = new DragableArea(svgp, StyleLibrary.SCALE, 0, StyleLibrary.SCALE * 0.1, plotheight, this);
- layer.appendChild(eventarea.getElement());
- }
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance<DoubleDistance>(task);
}
@Override
- public void destroy() {
- super.destroy();
- eventarea.destroy();
+ public boolean allowThumbnails(VisualizationTask task) {
+ // Don't use thumbnails
+ return false;
}
/**
- * Get epsilon from y-value
+ * Instance.
*
- * @param y y-Value
- * @return epsilon
- */
- protected double getEpsilonFromY(double y) {
- if(y < 0) {
- y = 0;
- }
- if(y > plotheight) {
- y = plotheight;
- }
- return optics.getOPTICSPlot(context).getScale().getUnscaled(y / plotheight);
- }
-
- /**
- * Get y-value from epsilon
+ * @author Heidi Kolb
+ * @author Erich Schubert
*
- * @param epsilon epsilon
- * @return y-Value
+ * @param <D> distance type
*/
- protected double getYFromEpsilon(double epsilon) {
- double y = optics.getOPTICSPlot(context).getScale().getScaled(epsilon) * plotheight;
- if(y < 0) {
- y = 0;
+ public class Instance<D extends Distance<D>> extends AbstractOPTICSVisualization<D> implements DragableArea.DragListener {
+ /**
+ * CSS-Styles
+ */
+ protected static final String CSS_LINE = "opticsPlotLine";
+
+ /**
+ * CSS-Styles
+ */
+ protected static final String CSS_EPSILON = "opticsPlotEpsilonValue";
+
+ /**
+ * The current epsilon value.
+ */
+ private double epsilon = 0.0;
+
+ /**
+ * Sensitive (clickable) area
+ */
+ private DragableArea eventarea = null;
+
+ /**
+ * The label element
+ */
+ private Element elemText = null;
+
+ /**
+ * The line element
+ */
+ private Element elementLine = null;
+
+ /**
+ * The drag handle element
+ */
+ private Element elementPoint = null;
+
+ /**
+ * Constructor.
+ *
+ * @param task Task
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
}
- if(y > plotheight) {
- y = plotheight;
+
+ @Override
+ protected void redraw() {
+ incrementalRedraw();
}
- return y;
- }
- @Override
- public boolean startDrag(SVGPoint start, Event evt) {
- epsilon = getEpsilonFromY(plotheight - start.getY());
- // opvis.unsetEpsilonExcept(this);
- synchronizedRedraw();
- return true;
- }
+ @Override
+ protected void incrementalRedraw() {
+ if(layer == null) {
+ makeLayerElement();
+ addCSSClasses();
+ }
- @Override
- public boolean duringDrag(SVGPoint start, SVGPoint end, Event evt, boolean inside) {
- if(inside) {
- epsilon = getEpsilonFromY(plotheight - end.getY());
- }
- // opvis.unsetEpsilonExcept(this);
- synchronizedRedraw();
- return true;
- }
+ // TODO make the number of digits configurable
+ final String label = (epsilon > 0.0) ? FormatUtil.format(epsilon, 4) : "";
+ // compute absolute y-value of bar
+ final double yAct = plotheight - getYFromEpsilon(epsilon);
- @Override
- public boolean endDrag(SVGPoint start, SVGPoint end, Event evt, boolean inside) {
- if(inside) {
- epsilon = getEpsilonFromY(plotheight - end.getY());
- // opvis.unsetEpsilonExcept(this);
+ if(elemText == null) {
+ elemText = svgp.svgText(StyleLibrary.SCALE * 1.05, yAct, label);
+ SVGUtil.setAtt(elemText, SVGConstants.SVG_CLASS_ATTRIBUTE, CSS_EPSILON);
+ layer.appendChild(elemText);
+ }
+ else {
+ elemText.setTextContent(label);
+ SVGUtil.setAtt(elemText, SVGConstants.SVG_Y_ATTRIBUTE, yAct);
+ }
- // FIXME: replace an existing optics cut result!
- final ClusterOrderResult<D> order = optics.getResult();
- Clustering<Model> cl = OPTICSCut.makeOPTICSCut(order, optics.getOPTICSPlot(context).getDistanceAdapter(), epsilon);
- order.addChildResult(cl);
+ // line and handle
+ if(elementLine == null) {
+ elementLine = svgp.svgLine(0, yAct, StyleLibrary.SCALE * 1.04, yAct);
+ SVGUtil.addCSSClass(elementLine, CSS_LINE);
+ layer.appendChild(elementLine);
+ }
+ else {
+ SVGUtil.setAtt(elementLine, SVG12Constants.SVG_Y1_ATTRIBUTE, yAct);
+ SVGUtil.setAtt(elementLine, SVG12Constants.SVG_Y2_ATTRIBUTE, yAct);
+ }
+ if(elementPoint == null) {
+ elementPoint = svgp.svgCircle(StyleLibrary.SCALE * 1.04, yAct, StyleLibrary.SCALE * 0.004);
+ SVGUtil.addCSSClass(elementPoint, CSS_LINE);
+ layer.appendChild(elementPoint);
+ }
+ else {
+ SVGUtil.setAtt(elementPoint, SVG12Constants.SVG_CY_ATTRIBUTE, yAct);
+ }
+
+ if(eventarea == null) {
+ eventarea = new DragableArea(svgp, StyleLibrary.SCALE, 0, StyleLibrary.SCALE * 0.1, plotheight, this);
+ layer.appendChild(eventarea.getElement());
+ }
}
- context.getHierarchy().resultChanged(this.task);
- // synchronizedRedraw();
- return true;
- }
- /**
- * Reset the epsilon value.
- */
- public void unsetEpsilon() {
- epsilon = 0.0;
- }
+ @Override
+ public void destroy() {
+ super.destroy();
+ eventarea.destroy();
+ }
- /**
- * Adds the required CSS-Classes
- */
- private void addCSSClasses() {
- // Class for the epsilon-value
- if(!svgp.getCSSClassManager().contains(CSS_EPSILON)) {
- final CSSClass label = new CSSClass(svgp, CSS_EPSILON);
- label.setStatement(SVGConstants.CSS_FILL_PROPERTY, context.getStyleLibrary().getTextColor(StyleLibrary.AXIS_LABEL));
- label.setStatement(SVGConstants.CSS_FONT_FAMILY_PROPERTY, context.getStyleLibrary().getFontFamily(StyleLibrary.AXIS_LABEL));
- label.setStatement(SVGConstants.CSS_FONT_SIZE_PROPERTY, context.getStyleLibrary().getTextSize(StyleLibrary.AXIS_LABEL));
- svgp.addCSSClassOrLogError(label);
+ /**
+ * Get epsilon from y-value
+ *
+ * @param y y-Value
+ * @return epsilon
+ */
+ protected double getEpsilonFromY(double y) {
+ if(y < 0) {
+ y = 0;
+ }
+ if(y > plotheight) {
+ y = plotheight;
+ }
+ return optics.getOPTICSPlot(context).getScale().getUnscaled(y / plotheight);
}
- // Class for the epsilon cut line
- if(!svgp.getCSSClassManager().contains(CSS_LINE)) {
- final CSSClass lcls = new CSSClass(svgp, CSS_LINE);
- lcls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, context.getStyleLibrary().getColor(StyleLibrary.PLOT));
- lcls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, 0.5 * context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT));
- svgp.addCSSClassOrLogError(lcls);
+
+ /**
+ * Get y-value from epsilon
+ *
+ * @param epsilon epsilon
+ * @return y-Value
+ */
+ protected double getYFromEpsilon(double epsilon) {
+ double y = optics.getOPTICSPlot(context).getScale().getScaled(epsilon) * plotheight;
+ if(y < 0) {
+ y = 0;
+ }
+ if(y > plotheight) {
+ y = plotheight;
+ }
+ return y;
}
- }
- /**
- * Factory class
- *
- * @author Erich Schubert
- *
- * @apiviz.stereotype factory
- * @apiviz.uses OPTICSPlotCutVisualization oneway - - «create»
- */
- public static class Factory extends AbstractVisFactory {
- public Factory() {
- super();
+ @Override
+ public boolean startDrag(SVGPoint start, Event evt) {
+ epsilon = getEpsilonFromY(plotheight - start.getY());
+ // opvis.unsetEpsilonExcept(this);
+ synchronizedRedraw();
+ return true;
}
@Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<OPTICSProjector<?>> ops = ResultUtil.filterResults(result, OPTICSProjector.class);
- for(OPTICSProjector<?> p : ops) {
- final VisualizationTask task = new VisualizationTask(NAME, p, null, this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_INTERACTIVE);
- baseResult.getHierarchy().add(p, task);
+ public boolean duringDrag(SVGPoint start, SVGPoint end, Event evt, boolean inside) {
+ if(inside) {
+ epsilon = getEpsilonFromY(plotheight - end.getY());
}
+ // opvis.unsetEpsilonExcept(this);
+ synchronizedRedraw();
+ return true;
}
@Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new OPTICSPlotCutVisualization<DoubleDistance>(task);
+ public boolean endDrag(SVGPoint start, SVGPoint end, Event evt, boolean inside) {
+ if(inside) {
+ epsilon = getEpsilonFromY(plotheight - end.getY());
+ // opvis.unsetEpsilonExcept(this);
+
+ // FIXME: replace an existing optics cut result!
+ final ClusterOrderResult<D> order = optics.getResult();
+ Clustering<Model> cl = OPTICSCut.makeOPTICSCut(order, optics.getOPTICSPlot(context).getDistanceAdapter(), epsilon);
+ order.addChildResult(cl);
+ }
+ context.getHierarchy().resultChanged(this.task);
+ // synchronizedRedraw();
+ return true;
}
- @Override
- public boolean allowThumbnails(VisualizationTask task) {
- // Don't use thumbnails
- return false;
+ /**
+ * Reset the epsilon value.
+ */
+ public void unsetEpsilon() {
+ epsilon = 0.0;
+ }
+
+ /**
+ * Adds the required CSS-Classes
+ */
+ private void addCSSClasses() {
+ // Class for the epsilon-value
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ if(!svgp.getCSSClassManager().contains(CSS_EPSILON)) {
+ final CSSClass label = new CSSClass(svgp, CSS_EPSILON);
+ label.setStatement(SVGConstants.CSS_FILL_PROPERTY, style.getTextColor(StyleLibrary.AXIS_LABEL));
+ label.setStatement(SVGConstants.CSS_FONT_FAMILY_PROPERTY, style.getFontFamily(StyleLibrary.AXIS_LABEL));
+ label.setStatement(SVGConstants.CSS_FONT_SIZE_PROPERTY, style.getTextSize(StyleLibrary.AXIS_LABEL));
+ svgp.addCSSClassOrLogError(label);
+ }
+ // Class for the epsilon cut line
+ if(!svgp.getCSSClassManager().contains(CSS_LINE)) {
+ final CSSClass lcls = new CSSClass(svgp, CSS_LINE);
+ lcls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, style.getColor(StyleLibrary.PLOT));
+ lcls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, 0.5 * style.getLineWidth(StyleLibrary.PLOT));
+ svgp.addCSSClassOrLogError(lcls);
+ }
}
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSPlotSelectionVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSPlotSelectionVisualization.java
index 87b24df2..bd2d0946 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSPlotSelectionVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSPlotSelectionVisualization.java
@@ -56,17 +56,16 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
/**
* Handle the marker in an OPTICS plot.
*
- * @author Heidi Kolb
+ * @author Erich Schubert
*
- * @apiviz.uses DBIDSelection oneway - 1 visualizes
- *
- * @param <D> distance type
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class OPTICSPlotSelectionVisualization<D extends Distance<D>> extends AbstractOPTICSVisualization<D> implements DragableArea.DragListener {
+public class OPTICSPlotSelectionVisualization extends AbstractVisFactory {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(OPTICSPlotSelectionVisualization.class);
+ private static final Logging LOG = Logging.getLogger(OPTICSPlotSelectionVisualization.class);
/**
* A short name characterizing this Visualizer.
@@ -74,16 +73,6 @@ public class OPTICSPlotSelectionVisualization<D extends Distance<D>> extends Abs
private static final String NAME = "OPTICS Selection";
/**
- * CSS class for markers
- */
- protected static final String CSS_MARKER = "opticsPlotMarker";
-
- /**
- * CSS class for markers
- */
- protected static final String CSS_RANGEMARKER = "opticsPlotRangeMarker";
-
- /**
* Input modes
*
* @apiviz.exclude
@@ -94,278 +83,290 @@ public class OPTICSPlotSelectionVisualization<D extends Distance<D>> extends Abs
}
/**
- * Element for the events
- */
- private Element etag;
-
- /**
- * Element for the marker
- */
- private Element mtag;
-
- /**
- * Constructor.
- *
- * @param task Visualization task
- */
- public OPTICSPlotSelectionVisualization(VisualizationTask task) {
- super(task);
- context.addResultListener(this);
- incrementalRedraw();
- }
-
- @Override
- protected void redraw() {
- makeLayerElement();
- addCSSClasses();
-
- mtag = svgp.svgElement(SVGConstants.SVG_G_TAG);
- addMarker();
-
- DragableArea drag = new DragableArea(svgp, 0 - plotwidth * 0.1, 0, plotwidth * 1.1, plotheight, this);
- etag = drag.getElement();
- // mtag first, etag must be the top Element
- layer.appendChild(mtag);
- layer.appendChild(etag);
- }
-
- /**
- * Add marker for the selected IDs to mtag
- */
- public void addMarker() {
- List<ClusterOrderEntry<D>> order = getClusterOrder();
- // TODO: replace mtag!
- DBIDSelection selContext = context.getSelection();
- if(selContext != null) {
- DBIDs selection = DBIDUtil.ensureSet(selContext.getSelectedIds());
-
- final double width = plotwidth / order.size();
- int begin = -1;
- for(int j = 0; j < order.size(); j++) {
- DBID id = order.get(j).getID();
- if(selection.contains(id)) {
- if(begin == -1) {
- begin = j;
- }
- }
- else {
- if(begin != -1) {
- Element marker = addMarkerRect(begin * width, (j - begin) * width);
- SVGUtil.addCSSClass(marker, CSS_MARKER);
- mtag.appendChild(marker);
- begin = -1;
- }
- }
- }
- // tail
- if(begin != -1) {
- Element marker = addMarkerRect(begin * width, (order.size() - begin) * width);
- SVGUtil.addCSSClass(marker, CSS_MARKER);
- mtag.appendChild(marker);
- }
- }
- }
-
- /**
- * Create a rectangle as marker (Marker higher than plot!)
- *
- * @param x1 X-Value for the marker
- * @param width Width of an entry
- * @return SVG-Element svg-rectangle
+ * Constructor, adhering to
+ * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
*/
- public Element addMarkerRect(double x1, double width) {
- return svgp.svgRect(x1, 0, width, plotheight);
+ public OPTICSPlotSelectionVisualization() {
+ super();
}
@Override
- public boolean startDrag(SVGPoint startPoint, Event evt) {
- List<ClusterOrderEntry<D>> order = getClusterOrder();
- int mouseActIndex = getSelectedIndex(order, startPoint);
- if(mouseActIndex >= 0 && mouseActIndex < order.size()) {
- double width = plotwidth / order.size();
- double x1 = mouseActIndex * width;
- Element marker = addMarkerRect(x1, width);
- SVGUtil.setCSSClass(marker, CSS_RANGEMARKER);
- mtag.appendChild(marker);
- return true;
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<OPTICSProjector<?>> ops = ResultUtil.filterResults(result, OPTICSProjector.class);
+ for(OPTICSProjector<?> p : ops) {
+ final VisualizationTask task = new VisualizationTask(NAME, p, null, this);
+ task.level = VisualizationTask.LEVEL_INTERACTIVE;
+ baseResult.getHierarchy().add(p, task);
}
- return false;
}
@Override
- public boolean duringDrag(SVGPoint startPoint, SVGPoint dragPoint, Event evt, boolean inside) {
- List<ClusterOrderEntry<D>> order = getClusterOrder();
- int mouseDownIndex = getSelectedIndex(order, startPoint);
- int mouseActIndex = getSelectedIndex(order, dragPoint);
- final int begin = Math.max(Math.min(mouseDownIndex, mouseActIndex), 0);
- final int end = Math.min(Math.max(mouseDownIndex, mouseActIndex), order.size());
- double width = plotwidth / order.size();
- double x1 = begin * width;
- double x2 = (end * width) + width;
- mtag.removeChild(mtag.getLastChild());
- Element marker = addMarkerRect(x1, x2 - x1);
- SVGUtil.setCSSClass(marker, CSS_RANGEMARKER);
- mtag.appendChild(marker);
- return true;
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance<DoubleDistance>(task);
}
@Override
- public boolean endDrag(SVGPoint startPoint, SVGPoint dragPoint, Event evt, boolean inside) {
- List<ClusterOrderEntry<D>> order = getClusterOrder();
- int mouseDownIndex = getSelectedIndex(order, startPoint);
- int mouseActIndex = getSelectedIndex(order, dragPoint);
- Mode mode = getInputMode(evt);
- final int begin = Math.max(Math.min(mouseDownIndex, mouseActIndex), 0);
- final int end = Math.min(Math.max(mouseDownIndex, mouseActIndex), order.size());
- updateSelection(mode, begin, end);
- return true;
+ public boolean allowThumbnails(VisualizationTask task) {
+ // Don't use thumbnails
+ return false;
}
/**
- * Get the current input mode, on each mouse event.
+ * Instance.
*
- * @param evt Mouse event.
- * @return Input mode
- */
- private Mode getInputMode(Event evt) {
- if(evt instanceof DOMMouseEvent) {
- DOMMouseEvent domme = (DOMMouseEvent) evt;
- // TODO: visual indication of mode possible?
- if(domme.getShiftKey()) {
- return Mode.ADD;
- }
- else if(domme.getCtrlKey()) {
- return Mode.INVERT;
- }
- else {
- return Mode.REPLACE;
- }
- }
- // Default mode is replace.
- return Mode.REPLACE;
- }
-
- /**
- * Gets the Index of the ClusterOrderEntry where the event occurred
+ * @author Heidi Kolb
+ * @author Erich Schubert
*
- * @param order List of ClusterOrderEntries
- * @param cPt clicked point
- * @return Index of the object
- */
- private int getSelectedIndex(List<ClusterOrderEntry<D>> order, SVGPoint cPt) {
- int mouseActIndex = (int) ((cPt.getX() / plotwidth) * order.size());
- return mouseActIndex;
- }
-
- /**
- * Updates the selection for the given ClusterOrderEntry.
+ * @apiviz.uses DBIDSelection oneway - 1 visualizes
*
- * @param mode Input mode
- * @param begin first index to select
- * @param end last index to select
+ * @param <D> distance type
*/
- protected void updateSelection(Mode mode, int begin, int end) {
- List<ClusterOrderEntry<D>> order = getClusterOrder();
- if(begin < 0 || begin > end || end >= order.size()) {
- logger.warning("Invalid range in updateSelection: " + begin + " .. " + end);
- return;
- }
+ public class Instance<D extends Distance<D>> extends AbstractOPTICSVisualization<D> implements DragableArea.DragListener {
+ /**
+ * CSS class for markers
+ */
+ protected static final String CSS_MARKER = "opticsPlotMarker";
- DBIDSelection selContext = context.getSelection();
- HashSetModifiableDBIDs selection;
- if(selContext == null || mode == Mode.REPLACE) {
- selection = DBIDUtil.newHashSet();
+ /**
+ * CSS class for markers
+ */
+ protected static final String CSS_RANGEMARKER = "opticsPlotRangeMarker";
+
+ /**
+ * Element for the events
+ */
+ private Element etag;
+
+ /**
+ * Element for the marker
+ */
+ private Element mtag;
+
+ /**
+ * Constructor.
+ *
+ * @param task Visualization task
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
+ context.addResultListener(this);
+ incrementalRedraw();
}
- else {
- selection = DBIDUtil.newHashSet(selContext.getSelectedIds());
+
+ @Override
+ protected void redraw() {
+ makeLayerElement();
+ addCSSClasses();
+
+ mtag = svgp.svgElement(SVGConstants.SVG_G_TAG);
+ addMarker();
+
+ DragableArea drag = new DragableArea(svgp, 0 - plotwidth * 0.1, 0, plotwidth * 1.1, plotheight, this);
+ etag = drag.getElement();
+ // mtag first, etag must be the top Element
+ layer.appendChild(mtag);
+ layer.appendChild(etag);
}
- for(int i = begin; i <= end; i++) {
- DBID id = order.get(i).getID();
- if(mode == Mode.INVERT) {
- if(!selection.contains(id)) {
- selection.add(id);
+ /**
+ * Add marker for the selected IDs to mtag
+ */
+ public void addMarker() {
+ List<ClusterOrderEntry<D>> order = getClusterOrder();
+ // TODO: replace mtag!
+ DBIDSelection selContext = context.getSelection();
+ if(selContext != null) {
+ DBIDs selection = DBIDUtil.ensureSet(selContext.getSelectedIds());
+
+ final double width = plotwidth / order.size();
+ int begin = -1;
+ for(int j = 0; j < order.size(); j++) {
+ DBID id = order.get(j).getID();
+ if(selection.contains(id)) {
+ if(begin == -1) {
+ begin = j;
+ }
+ }
+ else {
+ if(begin != -1) {
+ Element marker = addMarkerRect(begin * width, (j - begin) * width);
+ SVGUtil.addCSSClass(marker, CSS_MARKER);
+ mtag.appendChild(marker);
+ begin = -1;
+ }
+ }
}
- else {
- selection.remove(id);
+ // tail
+ if(begin != -1) {
+ Element marker = addMarkerRect(begin * width, (order.size() - begin) * width);
+ SVGUtil.addCSSClass(marker, CSS_MARKER);
+ mtag.appendChild(marker);
}
}
- else {
- // In REPLACE and ADD, add objects.
- // The difference was done before by not re-using the selection.
- // Since we are using a set, we can just add in any case.
- selection.add(id);
+ }
+
+ /**
+ * Create a rectangle as marker (Marker higher than plot!)
+ *
+ * @param x1 X-Value for the marker
+ * @param width Width of an entry
+ * @return SVG-Element svg-rectangle
+ */
+ public Element addMarkerRect(double x1, double width) {
+ return svgp.svgRect(x1, 0, width, plotheight);
+ }
+
+ @Override
+ public boolean startDrag(SVGPoint startPoint, Event evt) {
+ List<ClusterOrderEntry<D>> order = getClusterOrder();
+ int mouseActIndex = getSelectedIndex(order, startPoint);
+ if(mouseActIndex >= 0 && mouseActIndex < order.size()) {
+ double width = plotwidth / order.size();
+ double x1 = mouseActIndex * width;
+ Element marker = addMarkerRect(x1, width);
+ SVGUtil.setCSSClass(marker, CSS_RANGEMARKER);
+ mtag.appendChild(marker);
+ return true;
}
+ return false;
}
- context.setSelection(new DBIDSelection(selection));
- }
- /**
- * Adds the required CSS-Classes
- */
- private void addCSSClasses() {
- // Class for the markers
- if(!svgp.getCSSClassManager().contains(CSS_MARKER)) {
- final CSSClass cls = new CSSClass(this, CSS_MARKER);
- cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_BLUE_VALUE);
- cls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, "0.2");
- svgp.addCSSClassOrLogError(cls);
+ @Override
+ public boolean duringDrag(SVGPoint startPoint, SVGPoint dragPoint, Event evt, boolean inside) {
+ List<ClusterOrderEntry<D>> order = getClusterOrder();
+ int mouseDownIndex = getSelectedIndex(order, startPoint);
+ int mouseActIndex = getSelectedIndex(order, dragPoint);
+ final int begin = Math.max(Math.min(mouseDownIndex, mouseActIndex), 0);
+ final int end = Math.min(Math.max(mouseDownIndex, mouseActIndex), order.size());
+ double width = plotwidth / order.size();
+ double x1 = begin * width;
+ double x2 = (end * width) + width;
+ mtag.removeChild(mtag.getLastChild());
+ Element marker = addMarkerRect(x1, x2 - x1);
+ SVGUtil.setCSSClass(marker, CSS_RANGEMARKER);
+ mtag.appendChild(marker);
+ return true;
}
- // Class for the range marking
- if(!svgp.getCSSClassManager().contains(CSS_RANGEMARKER)) {
- final CSSClass rcls = new CSSClass(this, CSS_RANGEMARKER);
- rcls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_RED_VALUE);
- rcls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, "0.2");
- svgp.addCSSClassOrLogError(rcls);
+ @Override
+ public boolean endDrag(SVGPoint startPoint, SVGPoint dragPoint, Event evt, boolean inside) {
+ List<ClusterOrderEntry<D>> order = getClusterOrder();
+ int mouseDownIndex = getSelectedIndex(order, startPoint);
+ int mouseActIndex = getSelectedIndex(order, dragPoint);
+ Mode mode = getInputMode(evt);
+ final int begin = Math.max(Math.min(mouseDownIndex, mouseActIndex), 0);
+ final int end = Math.min(Math.max(mouseDownIndex, mouseActIndex), order.size());
+ updateSelection(mode, begin, end);
+ return true;
}
- }
- @Override
- public void resultChanged(Result current) {
- if(current instanceof SelectionResult) {
- synchronizedRedraw();
- return;
+ /**
+ * Get the current input mode, on each mouse event.
+ *
+ * @param evt Mouse event.
+ * @return Input mode
+ */
+ private Mode getInputMode(Event evt) {
+ if(evt instanceof DOMMouseEvent) {
+ DOMMouseEvent domme = (DOMMouseEvent) evt;
+ // TODO: visual indication of mode possible?
+ if(domme.getShiftKey()) {
+ return Mode.ADD;
+ }
+ else if(domme.getCtrlKey()) {
+ return Mode.INVERT;
+ }
+ else {
+ return Mode.REPLACE;
+ }
+ }
+ // Default mode is replace.
+ return Mode.REPLACE;
}
- super.resultChanged(current);
- }
- /**
- * Factory class for OPTICS plot selections.
- *
- * @author Erich Schubert
- *
- * @apiviz.stereotype factory
- * @apiviz.uses OPTICSPlotSelectionVisualization oneway - - «create»
- */
- public static class Factory extends AbstractVisFactory {
/**
- * Constructor, adhering to
- * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
+ * Gets the Index of the ClusterOrderEntry where the event occurred
+ *
+ * @param order List of ClusterOrderEntries
+ * @param cPt clicked point
+ * @return Index of the object
*/
- public Factory() {
- super();
+ private int getSelectedIndex(List<ClusterOrderEntry<D>> order, SVGPoint cPt) {
+ int mouseActIndex = (int) ((cPt.getX() / plotwidth) * order.size());
+ return mouseActIndex;
}
- @Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<OPTICSProjector<?>> ops = ResultUtil.filterResults(result, OPTICSProjector.class);
- for(OPTICSProjector<?> p : ops) {
- final VisualizationTask task = new VisualizationTask(NAME, p, null, this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_INTERACTIVE);
- baseResult.getHierarchy().add(p, task);
+ /**
+ * Updates the selection for the given ClusterOrderEntry.
+ *
+ * @param mode Input mode
+ * @param begin first index to select
+ * @param end last index to select
+ */
+ protected void updateSelection(Mode mode, int begin, int end) {
+ List<ClusterOrderEntry<D>> order = getClusterOrder();
+ if(begin < 0 || begin > end || end >= order.size()) {
+ LOG.warning("Invalid range in updateSelection: " + begin + " .. " + end);
+ return;
+ }
+
+ DBIDSelection selContext = context.getSelection();
+ HashSetModifiableDBIDs selection;
+ if(selContext == null || mode == Mode.REPLACE) {
+ selection = DBIDUtil.newHashSet();
+ }
+ else {
+ selection = DBIDUtil.newHashSet(selContext.getSelectedIds());
}
+
+ for(int i = begin; i <= end; i++) {
+ DBID id = order.get(i).getID();
+ if(mode == Mode.INVERT) {
+ if(!selection.contains(id)) {
+ selection.add(id);
+ }
+ else {
+ selection.remove(id);
+ }
+ }
+ else {
+ // In REPLACE and ADD, add objects.
+ // The difference was done before by not re-using the selection.
+ // Since we are using a set, we can just add in any case.
+ selection.add(id);
+ }
+ }
+ context.setSelection(new DBIDSelection(selection));
}
- @Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new OPTICSPlotSelectionVisualization<DoubleDistance>(task);
+ /**
+ * Adds the required CSS-Classes
+ */
+ private void addCSSClasses() {
+ // Class for the markers
+ if(!svgp.getCSSClassManager().contains(CSS_MARKER)) {
+ final CSSClass cls = new CSSClass(this, CSS_MARKER);
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_BLUE_VALUE);
+ cls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, "0.2");
+ svgp.addCSSClassOrLogError(cls);
+ }
+
+ // Class for the range marking
+ if(!svgp.getCSSClassManager().contains(CSS_RANGEMARKER)) {
+ final CSSClass rcls = new CSSClass(this, CSS_RANGEMARKER);
+ rcls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_RED_VALUE);
+ rcls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, "0.2");
+ svgp.addCSSClassOrLogError(rcls);
+ }
}
@Override
- public boolean allowThumbnails(VisualizationTask task) {
- // Don't use thumbnails
- return false;
+ public void resultChanged(Result current) {
+ if(current instanceof SelectionResult) {
+ synchronizedRedraw();
+ return;
+ }
+ super.resultChanged(current);
}
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSPlotVisualizer.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSPlotVisualizer.java
index d93c98cb..cde2d89d 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSPlotVisualizer.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSPlotVisualizer.java
@@ -38,6 +38,7 @@ import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
import de.lmu.ifi.dbs.elki.visualization.css.CSSClassManager.CSSNamingConflict;
import de.lmu.ifi.dbs.elki.visualization.opticsplot.OPTICSPlot;
import de.lmu.ifi.dbs.elki.visualization.projector.OPTICSProjector;
+import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGSimpleLinearAxis;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
@@ -48,87 +49,88 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
*
* @author Erich Schubert
*
- * @param <D> Distance type
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class OPTICSPlotVisualizer<D extends Distance<D>> extends AbstractOPTICSVisualization<D> {
+public class OPTICSPlotVisualizer extends AbstractVisFactory {
/**
* Name for this visualizer.
*/
private static final String NAME = "OPTICS Plot";
/**
- * Constructor.
- *
- * @param task Visualization task
+ * Constructor, adhering to
+ * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
*/
- public OPTICSPlotVisualizer(VisualizationTask task) {
- super(task);
+ public OPTICSPlotVisualizer() {
+ super();
}
@Override
- protected void redraw() {
- makeLayerElement();
- // addCSSClasses();
-
- OPTICSPlot<D> opticsplot = optics.getOPTICSPlot(context);
- String ploturi = opticsplot.getSVGPlotURI();
-
- Element itag = svgp.svgElement(SVGConstants.SVG_IMAGE_TAG);
- SVGUtil.setAtt(itag, SVGConstants.SVG_IMAGE_RENDERING_ATTRIBUTE, SVGConstants.SVG_OPTIMIZE_SPEED_VALUE);
- SVGUtil.setAtt(itag, SVGConstants.SVG_X_ATTRIBUTE, 0);
- SVGUtil.setAtt(itag, SVGConstants.SVG_Y_ATTRIBUTE, 0);
- SVGUtil.setAtt(itag, SVGConstants.SVG_WIDTH_ATTRIBUTE, plotwidth);
- SVGUtil.setAtt(itag, SVGConstants.SVG_HEIGHT_ATTRIBUTE, plotheight);
- itag.setAttributeNS(SVGConstants.XLINK_NAMESPACE_URI, SVGConstants.XLINK_HREF_QNAME, ploturi);
-
- layer.appendChild(itag);
-
- try {
- SVGSimpleLinearAxis.drawAxis(svgp, layer, opticsplot.getScale(), 0, plotheight, 0, 0, SVGSimpleLinearAxis.LabelStyle.LEFTHAND, context.getStyleLibrary());
- SVGSimpleLinearAxis.drawAxis(svgp, layer, opticsplot.getScale(), plotwidth, plotheight, plotwidth, 0, SVGSimpleLinearAxis.LabelStyle.RIGHTHAND, context.getStyleLibrary());
- }
- catch(CSSNamingConflict e) {
- LoggingUtil.exception("CSS naming conflict for axes on OPTICS plot", e);
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<OPTICSProjector<?>> ops = ResultUtil.filterResults(result, OPTICSProjector.class);
+ for(OPTICSProjector<?> p : ops) {
+ // Add plots, attach visualizer
+ final VisualizationTask task = new VisualizationTask(NAME, p, null, this);
+ task.level = VisualizationTask.LEVEL_DATA;
+ baseResult.getHierarchy().add(p, task);
}
}
+ @Override
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance<DoubleDistance>(task);
+ }
+
+ @Override
+ public boolean allowThumbnails(VisualizationTask task) {
+ // Don't use thumbnails
+ return false;
+ }
+
/**
- * Factory class for OPTICS plot.
+ * Instance.
*
* @author Erich Schubert
*
- * @apiviz.stereotype factory
- * @apiviz.uses OPTICSPlotVisualizer oneway - - «create»
+ * @param <D> Distance type
*/
- public static class Factory extends AbstractVisFactory {
+ public class Instance<D extends Distance<D>> extends AbstractOPTICSVisualization<D> {
/**
- * Constructor, adhering to
- * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
+ * Constructor.
+ *
+ * @param task Visualization task
*/
- public Factory() {
- super();
+ public Instance(VisualizationTask task) {
+ super(task);
}
@Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<OPTICSProjector<?>> ops = ResultUtil.filterResults(result, OPTICSProjector.class);
- for(OPTICSProjector<?> p : ops) {
- // Add plots, attach visualizer
- final VisualizationTask task = new VisualizationTask(NAME, p, null, this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA);
- baseResult.getHierarchy().add(p, task);
+ protected void redraw() {
+ makeLayerElement();
+ // addCSSClasses();
+
+ OPTICSPlot<D> opticsplot = optics.getOPTICSPlot(context);
+ String ploturi = opticsplot.getSVGPlotURI();
+
+ Element itag = svgp.svgElement(SVGConstants.SVG_IMAGE_TAG);
+ SVGUtil.setAtt(itag, SVGConstants.SVG_IMAGE_RENDERING_ATTRIBUTE, SVGConstants.SVG_OPTIMIZE_SPEED_VALUE);
+ SVGUtil.setAtt(itag, SVGConstants.SVG_X_ATTRIBUTE, 0);
+ SVGUtil.setAtt(itag, SVGConstants.SVG_Y_ATTRIBUTE, 0);
+ SVGUtil.setAtt(itag, SVGConstants.SVG_WIDTH_ATTRIBUTE, plotwidth);
+ SVGUtil.setAtt(itag, SVGConstants.SVG_HEIGHT_ATTRIBUTE, plotheight);
+ itag.setAttributeNS(SVGConstants.XLINK_NAMESPACE_URI, SVGConstants.XLINK_HREF_QNAME, ploturi);
+
+ layer.appendChild(itag);
+
+ try {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ SVGSimpleLinearAxis.drawAxis(svgp, layer, opticsplot.getScale(), 0, plotheight, 0, 0, SVGSimpleLinearAxis.LabelStyle.LEFTHAND, style);
+ SVGSimpleLinearAxis.drawAxis(svgp, layer, opticsplot.getScale(), plotwidth, plotheight, plotwidth, 0, SVGSimpleLinearAxis.LabelStyle.RIGHTHAND, style);
+ }
+ catch(CSSNamingConflict e) {
+ LoggingUtil.exception("CSS naming conflict for axes on OPTICS plot", e);
}
- }
-
- @Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new OPTICSPlotVisualizer<DoubleDistance>(task);
- }
-
- @Override
- public boolean allowThumbnails(VisualizationTask task) {
- // Don't use thumbnails
- return false;
}
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSSteepAreaVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSSteepAreaVisualization.java
index 4e557a74..e6e00960 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSSteepAreaVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSSteepAreaVisualization.java
@@ -56,40 +56,47 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
*
* @author Erich Schubert
*
- * @apiviz.uses
- * de.lmu.ifi.dbs.elki.algorithm.clustering.OPTICSXi.SteepAreaResult
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class OPTICSSteepAreaVisualization<D extends Distance<D>> extends AbstractOPTICSVisualization<D> {
+public class OPTICSSteepAreaVisualization extends AbstractVisFactory {
/**
* A short name characterizing this Visualizer.
*/
private static final String NAME = "OPTICS Steep Areas";
/**
- * CSS class for markers
+ * Constructor, adhering to
+ * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
*/
- protected static final String CSS_STEEP_UP = "opticsSteepUp";
+ public OPTICSSteepAreaVisualization() {
+ super();
+ }
- /**
- * CSS class for markers
- */
- protected static final String CSS_STEEP_DOWN = "opticsSteepDown";
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<OPTICSProjector<?>> ops = ResultUtil.filterResults(result, OPTICSProjector.class);
+ for(OPTICSProjector<?> p : ops) {
+ final SteepAreaResult steep = findSteepAreaResult(p.getResult());
+ if(steep != null) {
+ final VisualizationTask task = new VisualizationTask(NAME, p, null, this);
+ task.level = VisualizationTask.LEVEL_DATA + 1;
+ task.initDefaultVisibility(false);
+ baseResult.getHierarchy().add(p, task);
+ baseResult.getHierarchy().add(steep, task);
+ }
+ }
+ }
- /**
- * Our clustering
- */
- OPTICSXi.SteepAreaResult areas;
+ @Override
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance<DoubleDistance>(task);
+ }
- /**
- * Constructor.
- *
- * @param task Visualization task
- */
- public OPTICSSteepAreaVisualization(VisualizationTask task) {
- super(task);
- this.areas = findSteepAreaResult(this.optics.getResult());
- context.addResultListener(this);
- incrementalRedraw();
+ @Override
+ public boolean allowThumbnails(VisualizationTask task) {
+ // Don't use thumbnails
+ return false;
}
/**
@@ -107,116 +114,110 @@ public class OPTICSSteepAreaVisualization<D extends Distance<D>> extends Abstrac
return null;
}
- @Override
- protected void redraw() {
- makeLayerElement();
- addCSSClasses();
-
- final OPTICSPlot<D> opticsplot = optics.getOPTICSPlot(context);
- final List<ClusterOrderEntry<D>> co = getClusterOrder();
- final OPTICSDistanceAdapter<D> adapter = opticsplot.getDistanceAdapter();
- final LinearScale scale = opticsplot.getScale();
-
- for(OPTICSXi.SteepArea area : areas) {
- final int st = area.getStartIndex();
- final int en = area.getEndIndex();
- // Note: make sure we are using doubles!
- final double x1 = (st + .25) / co.size();
- final double x2 = (en + .75) / co.size();
- final double d1 = adapter.getDoubleForEntry(co.get(st));
- final double d2 = adapter.getDoubleForEntry(co.get(en));
- final double y1 = (!Double.isInfinite(d1) && !Double.isNaN(d1)) ? (1. - scale.getScaled(d1)) : 0.;
- final double y2 = (!Double.isInfinite(d2) && !Double.isNaN(d2)) ? (1. - scale.getScaled(d2)) : 0.;
- Element e = svgp.svgLine(plotwidth * x1, plotheight * y1, plotwidth * x2, plotheight * y2);
- if(area instanceof OPTICSXi.SteepDownArea) {
- SVGUtil.addCSSClass(e, CSS_STEEP_DOWN);
- }
- else {
- SVGUtil.addCSSClass(e, CSS_STEEP_UP);
- }
- layer.appendChild(e);
- }
- }
-
/**
- * Adds the required CSS-Classes
- */
- private void addCSSClasses() {
- // Class for the markers
- if(!svgp.getCSSClassManager().contains(CSS_STEEP_DOWN)) {
- final CSSClass cls = new CSSClass(this, CSS_STEEP_DOWN);
- Color color = SVGUtil.stringToColor(context.getStyleLibrary().getColor(StyleLibrary.PLOT));
- if(color == null) {
- color = Color.BLACK;
- }
- color = new Color((int) (color.getRed() * 0.8), (int) (color.getGreen() * 0.8 + 0.2 * 256), (int) (color.getBlue() * 0.8));
- cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, SVGUtil.colorToString(color));
- cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT));
- svgp.addCSSClassOrLogError(cls);
- }
- if(!svgp.getCSSClassManager().contains(CSS_STEEP_UP)) {
- final CSSClass cls = new CSSClass(this, CSS_STEEP_UP);
- Color color = SVGUtil.stringToColor(context.getStyleLibrary().getColor(StyleLibrary.PLOT));
- if(color == null) {
- color = Color.BLACK;
- }
- color = new Color((int) (color.getRed() * 0.8 + 0.2 * 256), (int) (color.getGreen() * 0.8), (int) (color.getBlue() * 0.8));
- cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, SVGUtil.colorToString(color));
- cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT));
- svgp.addCSSClassOrLogError(cls);
- }
- }
-
- @Override
- public void resultChanged(Result current) {
- if(current instanceof SelectionResult) {
- synchronizedRedraw();
- return;
- }
- super.resultChanged(current);
- }
-
- /**
- * Factory class for OPTICS plot selections.
+ * Instance
*
* @author Erich Schubert
*
- * @apiviz.stereotype factory
- * @apiviz.uses OPTICSPlotSelectionVisualization oneway - - «create»
+ * @apiviz.uses
+ * de.lmu.ifi.dbs.elki.algorithm.clustering.OPTICSXi.SteepAreaResult
*/
- public static class Factory extends AbstractVisFactory {
+ public class Instance<D extends Distance<D>> extends AbstractOPTICSVisualization<D> {
+ /**
+ * CSS class for markers
+ */
+ protected static final String CSS_STEEP_UP = "opticsSteepUp";
+
+ /**
+ * CSS class for markers
+ */
+ protected static final String CSS_STEEP_DOWN = "opticsSteepDown";
+
/**
- * Constructor, adhering to
- * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
+ * Our clustering
*/
- public Factory() {
- super();
+ OPTICSXi.SteepAreaResult areas;
+
+ /**
+ * Constructor.
+ *
+ * @param task Visualization task
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
+ this.areas = findSteepAreaResult(this.optics.getResult());
+ context.addResultListener(this);
+ incrementalRedraw();
}
@Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<OPTICSProjector<?>> ops = ResultUtil.filterResults(result, OPTICSProjector.class);
- for(OPTICSProjector<?> p : ops) {
- final SteepAreaResult steep = findSteepAreaResult(p.getResult());
- if(steep != null) {
- final VisualizationTask task = new VisualizationTask(NAME, p, null, this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA + 1);
- task.put(VisualizationTask.META_VISIBLE_DEFAULT, false);
- baseResult.getHierarchy().add(p, task);
- baseResult.getHierarchy().add(steep, task);
+ protected void redraw() {
+ makeLayerElement();
+ addCSSClasses();
+
+ final OPTICSPlot<D> opticsplot = optics.getOPTICSPlot(context);
+ final List<ClusterOrderEntry<D>> co = getClusterOrder();
+ final OPTICSDistanceAdapter<D> adapter = opticsplot.getDistanceAdapter();
+ final LinearScale scale = opticsplot.getScale();
+
+ for(OPTICSXi.SteepArea area : areas) {
+ final int st = area.getStartIndex();
+ final int en = area.getEndIndex();
+ // Note: make sure we are using doubles!
+ final double x1 = (st + .25) / co.size();
+ final double x2 = (en + .75) / co.size();
+ final double d1 = adapter.getDoubleForEntry(co.get(st));
+ final double d2 = adapter.getDoubleForEntry(co.get(en));
+ final double y1 = (!Double.isInfinite(d1) && !Double.isNaN(d1)) ? (1. - scale.getScaled(d1)) : 0.;
+ final double y2 = (!Double.isInfinite(d2) && !Double.isNaN(d2)) ? (1. - scale.getScaled(d2)) : 0.;
+ Element e = svgp.svgLine(plotwidth * x1, plotheight * y1, plotwidth * x2, plotheight * y2);
+ if(area instanceof OPTICSXi.SteepDownArea) {
+ SVGUtil.addCSSClass(e, CSS_STEEP_DOWN);
}
+ else {
+ SVGUtil.addCSSClass(e, CSS_STEEP_UP);
+ }
+ layer.appendChild(e);
}
}
- @Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new OPTICSSteepAreaVisualization<DoubleDistance>(task);
+ /**
+ * Adds the required CSS-Classes
+ */
+ private void addCSSClasses() {
+ // Class for the markers
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ if(!svgp.getCSSClassManager().contains(CSS_STEEP_DOWN)) {
+ final CSSClass cls = new CSSClass(this, CSS_STEEP_DOWN);
+ Color color = SVGUtil.stringToColor(style.getColor(StyleLibrary.PLOT));
+ if(color == null) {
+ color = Color.BLACK;
+ }
+ color = new Color((int) (color.getRed() * 0.8), (int) (color.getGreen() * 0.8 + 0.2 * 256.), (int) (color.getBlue() * 0.8));
+ cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, SVGUtil.colorToString(color));
+ cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.PLOT));
+ svgp.addCSSClassOrLogError(cls);
+ }
+ if(!svgp.getCSSClassManager().contains(CSS_STEEP_UP)) {
+ final CSSClass cls = new CSSClass(this, CSS_STEEP_UP);
+ Color color = SVGUtil.stringToColor(style.getColor(StyleLibrary.PLOT));
+ if(color == null) {
+ color = Color.BLACK;
+ }
+ color = new Color((int) (color.getRed() * 0.8 + 0.2 * 256.), (int) (color.getGreen() * 0.8), (int) (color.getBlue() * 0.8));
+ cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, SVGUtil.colorToString(color));
+ cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.PLOT));
+ svgp.addCSSClassOrLogError(cls);
+ }
}
@Override
- public boolean allowThumbnails(VisualizationTask task) {
- // Don't use thumbnails
- return false;
+ public void resultChanged(Result current) {
+ if(current instanceof SelectionResult) {
+ synchronizedRedraw();
+ return;
+ }
+ super.resultChanged(current);
}
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/package-info.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/package-info.java
index 208d8cb7..d5dad85b 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/package-info.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/package-info.java
@@ -2,7 +2,8 @@
* <p>Visualizers for various results</p>
*
* @apiviz.exclude de.lmu.ifi.dbs.elki.utilities.datastructures.AnyMap
- * @apiviz.exclude de.lmu.ifi.dbs.elki.visualization.visualizers\.vis.*\..*
+ * @apiviz.exclude Visualization.Factory
+ * @apiviz.exclude de.lmu.ifi.dbs.elki.visualization.gui.*
*/
/*
This file is part of ELKI:
@@ -26,4 +27,4 @@ GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package de.lmu.ifi.dbs.elki.visualization.visualizers; \ No newline at end of file
+package de.lmu.ifi.dbs.elki.visualization.visualizers;
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/pairsegments/CircleSegmentsVisualizer.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/pairsegments/CircleSegmentsVisualizer.java
index f0655ab6..400b8ebb 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/pairsegments/CircleSegmentsVisualizer.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/pairsegments/CircleSegmentsVisualizer.java
@@ -73,655 +73,667 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.thumbs.ThumbnailVisualizati
* In: Proc. 28th International Conference on Data Engineering (ICDE) 2012
* </p>
*
+ * <p>
+ * Details on the experimental setup can be found at: <a
+ * href="http://elki.dbs.ifi.lmu.de/wiki/Examples/ClusterEvaluation"
+ * >wiki/Examples/ClusterEvaluation</a>
+ * </p>
+ *
* @author Sascha Goldhofer
* @author Erich Schubert
*
* @apiviz.landmark
*
- * @apiviz.uses Segments
- * @apiviz.has SegmentsStylingPolicy
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-@Reference(title = "Evaluation of Clusterings – Metrics and Visual Support", authors = "Elke Achtert, Sascha Goldhofer, Hans-Peter Kriegel, Erich Schubert, Arthur Zimek", booktitle = "Proc. 28th International Conference on Data Engineering (ICDE) 2012", url = "http://elki.dbs.ifi.lmu.de/wiki/PairSegments")
-public class CircleSegmentsVisualizer extends AbstractVisualization implements ResultListener {
+@Reference(title = "Evaluation of Clusterings – Metrics and Visual Support", authors = "Elke Achtert, Sascha Goldhofer, Hans-Peter Kriegel, Erich Schubert, Arthur Zimek", booktitle = "Proc. 28th International Conference on Data Engineering (ICDE) 2012", url = "http://dx.doi.org/10.1109/ICDE.2012.128")
+public class CircleSegmentsVisualizer extends AbstractVisFactory {
/**
* Class logger
*/
- private static final Logging logger = Logging.getLogger(CircleSegmentsVisualizer.class);
+ private static final Logging LOG = Logging.getLogger(CircleSegmentsVisualizer.class);
/**
* CircleSegments visualizer name
*/
private static final String NAME = "CircleSegments";
- /** Minimum width (radian) of Segment */
- private final static double SEGMENT_MIN_ANGLE = 0.01;
+ /**
+ * Constructor
+ */
+ public CircleSegmentsVisualizer() {
+ super();
+ this.thumbmask |= ThumbnailVisualization.ON_STYLE;
+ }
- /** Gap (radian) between segments */
- private final static double SEGMENT_MIN_SEP_ANGLE = 0.005;
+ @Override
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
+ }
- /** Offset from center to first ring */
- private final static double RADIUS_INNER = 0.04 * StyleLibrary.SCALE;
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ // If no comparison result found abort
+ List<Segments> segments = ResultUtil.filterResults(result, Segments.class);
+ for(Segments segmentResult : segments) {
+ SegmentsStylingPolicy policy;
+ List<SegmentsStylingPolicy> styles = ResultUtil.filterResults(segmentResult, SegmentsStylingPolicy.class);
+ if(!styles.isEmpty()) {
+ policy = styles.get(0);
+ }
+ else {
+ policy = new SegmentsStylingPolicy(segmentResult);
+ baseResult.getHierarchy().add(segmentResult, policy);
+ }
+ // create task for visualization
+ final VisualizationTask task = new VisualizationTask(NAME, policy, null, this);
+ task.width = 2.0;
+ task.height = 2.0;
+ task.level = VisualizationTask.LEVEL_INTERACTIVE;
+ baseResult.getHierarchy().add(segmentResult, task);
+ }
+ }
- /** Margin between two rings */
- private final static double RADIUS_DISTANCE = 0.01 * StyleLibrary.SCALE;
+ /**
+ * Instance
+ *
+ * @author Sascha Goldhofer
+ * @author Erich Schubert
+ *
+ * @apiviz.uses Segments
+ * @apiviz.has SegmentsStylingPolicy
+ *
+ */
+ public class Instance extends AbstractVisualization implements ResultListener {
+ /** Minimum width (radian) of Segment */
+ private static final double SEGMENT_MIN_ANGLE = 0.01;
- /** Radius of whole CircleSegments except selection border */
- private final static double RADIUS_OUTER = 0.47 * StyleLibrary.SCALE;
+ /** Gap (radian) between segments */
+ private static final double SEGMENT_MIN_SEP_ANGLE = 0.005;
- /** Radius of highlight selection (outer ring) */
- private final static double RADIUS_SELECTION = 0.02 * StyleLibrary.SCALE;
+ /** Offset from center to first ring */
+ private static final double RADIUS_INNER = 0.04 * StyleLibrary.SCALE;
- /**
- * CSS class name for the clusterings.
- */
- private static final String CLR_CLUSTER_CLASS_PREFIX = "clusterSegment";
+ /** Margin between two rings */
+ private static final double RADIUS_DISTANCE = 0.01 * StyleLibrary.SCALE;
- /**
- * CSS border class of a cluster
- */
- public static final String CLR_BORDER_CLASS = "clusterBorder";
+ /** Radius of whole CircleSegments except selection border */
+ private static final double RADIUS_OUTER = 0.47 * StyleLibrary.SCALE;
- /**
- * CSS hover class for clusters of hovered segment
- */
- public static final String CLR_UNPAIRED_CLASS = "clusterUnpaired";
+ /** Radius of highlight selection (outer ring) */
+ private static final double RADIUS_SELECTION = 0.02 * StyleLibrary.SCALE;
- /**
- * CSS hover class of a segment cluster
- */
- public static final String CLR_HOVER_CLASS = "clusterHover";
+ /**
+ * CSS class name for the clusterings.
+ */
+ private static final String CLR_CLUSTER_CLASS_PREFIX = "clusterSegment";
- /**
- * CSS class of selected Segment
- */
- public static final String SEG_UNPAIRED_SELECTED_CLASS = "unpairedSegmentSelected";
+ /**
+ * CSS border class of a cluster
+ */
+ public static final String CLR_BORDER_CLASS = "clusterBorder";
- /**
- * Style prefix
- */
- public static final String STYLE = "segments";
+ /**
+ * CSS hover class for clusters of hovered segment
+ */
+ public static final String CLR_UNPAIRED_CLASS = "clusterUnpaired";
- /**
- * Style for border lines
- */
- public static final String STYLE_BORDER = STYLE + ".border";
+ /**
+ * CSS hover class of a segment cluster
+ */
+ public static final String CLR_HOVER_CLASS = "clusterHover";
- /**
- * Style for hover effect
- */
- public static final String STYLE_HOVER = STYLE + ".hover";
+ /**
+ * CSS class of selected Segment
+ */
+ public static final String SEG_UNPAIRED_SELECTED_CLASS = "unpairedSegmentSelected";
- /**
- * First color for producing segment-cluster colors
- */
- public static final String STYLE_GRADIENT_FIRST = STYLE + ".cluster.first";
+ /**
+ * Style prefix
+ */
+ public static final String STYLE = "segments";
- /**
- * Second color for producing segment-cluster colors
- */
- public static final String STYLE_GRADIENT_SECOND = STYLE + ".cluster.second";
+ /**
+ * Style for border lines
+ */
+ public static final String STYLE_BORDER = STYLE + ".border";
- /**
- * Segmentation of Clusterings
- */
- protected final Segments segments;
+ /**
+ * Style for hover effect
+ */
+ public static final String STYLE_HOVER = STYLE + ".hover";
- /**
- * The two main layers
- */
- private Element visLayer, ctrlLayer;
+ /**
+ * First color for producing segment-cluster colors
+ */
+ public static final String STYLE_GRADIENT_FIRST = STYLE + ".cluster.first";
- /**
- * Map to connect segments to their visual elements
- */
- public Map<Segment, List<Element>> segmentToElements = new HashMap<Segment, List<Element>>();
+ /**
+ * Second color for producing segment-cluster colors
+ */
+ public static final String STYLE_GRADIENT_SECOND = STYLE + ".cluster.second";
- /**
- * Show unclustered Pairs in CircleSegments
- */
- boolean showUnclusteredPairs = false;
+ /**
+ * Segmentation of Clusterings
+ */
+ protected final Segments segments;
- /**
- * Styling policy
- */
- protected final SegmentsStylingPolicy policy;
+ /**
+ * The two main layers
+ */
+ private Element visLayer, ctrlLayer;
- /**
- * Flag to disallow an incremental redraw
- */
- private boolean noIncrementalRedraw = true;
+ /**
+ * Map to connect segments to their visual elements
+ */
+ public Map<Segment, List<Element>> segmentToElements = new HashMap<Segment, List<Element>>();
- /**
- * Constructor
- */
- public CircleSegmentsVisualizer(VisualizationTask task) {
- super(task);
- policy = task.getResult();
- segments = policy.segments;
- // FIXME: handle this more generally.
- policy.setStyleLibrary(context.getStyleLibrary());
- // Listen for result changes (Selection changed)
- context.addResultListener(this);
- }
+ /**
+ * Show unclustered Pairs in CircleSegments
+ */
+ boolean showUnclusteredPairs = false;
- public void toggleUnclusteredPairs(boolean show) {
- noIncrementalRedraw = true;
- showUnclusteredPairs = show;
- synchronizedRedraw();
- }
+ /**
+ * Styling policy
+ */
+ protected final SegmentsStylingPolicy policy;
- @Override
- public void resultChanged(Result current) {
- super.resultChanged(current);
- // Redraw on style result changes.
- if(current == context.getStyleResult()) {
- // When switching to a different policy, unhighlight segments.
- if(context.getStyleResult().getStylingPolicy() != policy) {
- policy.deselectAllSegments();
- }
- synchronizedRedraw();
+ /**
+ * Flag to disallow an incremental redraw
+ */
+ private boolean noIncrementalRedraw = true;
+
+ /**
+ * Constructor
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
+ policy = task.getResult();
+ segments = policy.segments;
+ // FIXME: handle this more generally.
+ policy.setStyleLibrary(context.getStyleResult().getStyleLibrary());
+ // Listen for result changes (Selection changed)
+ context.addResultListener(this);
}
- }
- @Override
- protected void incrementalRedraw() {
- if(noIncrementalRedraw) {
- super.incrementalRedraw();
+ public void toggleUnclusteredPairs(boolean show) {
+ noIncrementalRedraw = true;
+ showUnclusteredPairs = show;
+ synchronizedRedraw();
}
- else {
- redrawSelection();
+
+ @Override
+ public void resultChanged(Result current) {
+ super.resultChanged(current);
+ // Redraw on style result changes.
+ if(current == context.getStyleResult()) {
+ // When switching to a different policy, unhighlight segments.
+ if(context.getStyleResult().getStylingPolicy() != policy) {
+ policy.deselectAllSegments();
+ }
+ synchronizedRedraw();
+ }
}
- }
- @Override
- public void redraw() {
- logger.debug("Full redraw");
- noIncrementalRedraw = false; // Done that.
-
- // initialize css (needs clusterSize!)
- addCSSClasses(segments.getHighestClusterCount());
-
- layer = svgp.svgElement(SVGConstants.SVG_G_TAG);
- visLayer = svgp.svgElement(SVGConstants.SVG_G_TAG);
- // Setup scaling for canvas: 0 to StyleLibrary.SCALE (usually 100 to avoid a
- // Java drawing bug!)
- String transform = SVGUtil.makeMarginTransform(task.width, task.height, StyleLibrary.SCALE, StyleLibrary.SCALE, 0) + " translate(" + (StyleLibrary.SCALE * .5) + " " + (StyleLibrary.SCALE * .5) + ")";
- visLayer.setAttribute(SVGConstants.SVG_TRANSFORM_ATTRIBUTE, transform);
- ctrlLayer = svgp.svgElement(SVGConstants.SVG_G_TAG);
-
- // and create svg elements
- drawSegments();
-
- //
- // Build Interface
- //
- SVGCheckbox checkbox = new SVGCheckbox(showUnclusteredPairs, "Show unclustered pairs");
- checkbox.addCheckBoxListener(new ChangeListener() {
- @Override
- public void stateChanged(ChangeEvent e) {
- toggleUnclusteredPairs(((SVGCheckbox) e.getSource()).isChecked());
+ @Override
+ protected void incrementalRedraw() {
+ if(noIncrementalRedraw) {
+ super.incrementalRedraw();
+ }
+ else {
+ redrawSelection();
}
- });
+ }
- // Add ring:clustering info
- Element clrInfo = drawClusteringInfo();
- Element c = checkbox.renderCheckBox(svgp, 1, 5 + Double.parseDouble(clrInfo.getAttribute(SVGConstants.SVG_HEIGHT_ATTRIBUTE)), 11);
- ctrlLayer.appendChild(clrInfo);
- ctrlLayer.appendChild(c);
+ @Override
+ public void redraw() {
+ LOG.debug("Full redraw");
+ noIncrementalRedraw = false; // Done that.
- ctrlLayer.setAttribute(SVGConstants.SVG_TRANSFORM_ATTRIBUTE, "scale(" + (0.25 / StyleLibrary.SCALE) + ")");
+ // initialize css (needs clusterSize!)
+ addCSSClasses(segments.getHighestClusterCount());
- layer.appendChild(visLayer);
- layer.appendChild(ctrlLayer);
- }
+ layer = svgp.svgElement(SVGConstants.SVG_G_TAG);
+ visLayer = svgp.svgElement(SVGConstants.SVG_G_TAG);
+ // Setup scaling for canvas: 0 to StyleLibrary.SCALE (usually 100 to avoid
+ // a
+ // Java drawing bug!)
+ String transform = SVGUtil.makeMarginTransform(task.width, task.height, StyleLibrary.SCALE, StyleLibrary.SCALE, 0) + " translate(" + (StyleLibrary.SCALE * .5) + " " + (StyleLibrary.SCALE * .5) + ")";
+ visLayer.setAttribute(SVGConstants.SVG_TRANSFORM_ATTRIBUTE, transform);
+ ctrlLayer = svgp.svgElement(SVGConstants.SVG_G_TAG);
- /**
- * Define and add required CSS classes
- */
- protected void addCSSClasses(int maxClusterSize) {
- StyleLibrary style = context.getStyleLibrary();
-
- // Cluster separation lines
- CSSClass cssReferenceBorder = new CSSClass(this.getClass(), CLR_BORDER_CLASS);
- cssReferenceBorder.setStatement(SVGConstants.SVG_FILL_ATTRIBUTE, style.getColor(STYLE_BORDER));
- svgp.addCSSClassOrLogError(cssReferenceBorder);
-
- // Hover effect for clusters
- CSSClass cluster_hover = new CSSClass(this.getClass(), CLR_HOVER_CLASS);
- // Note: !important is needed to override the regular color assignment
- cluster_hover.setStatement(SVGConstants.SVG_FILL_ATTRIBUTE, style.getColor(STYLE_HOVER) + " !important");
- cluster_hover.setStatement(SVGConstants.SVG_CURSOR_TAG, SVGConstants.SVG_POINTER_VALUE);
- svgp.addCSSClassOrLogError(cluster_hover);
-
- // Unpaired cluster segment
- CSSClass cluster_unpaired = new CSSClass(this.getClass(), CLR_UNPAIRED_CLASS);
- cluster_unpaired.setStatement(SVGConstants.SVG_FILL_ATTRIBUTE, style.getBackgroundColor(STYLE));
- cluster_unpaired.setStatement(SVGConstants.SVG_STROKE_ATTRIBUTE, SVGConstants.CSS_NONE_VALUE);
- svgp.addCSSClassOrLogError(cluster_unpaired);
-
- // Selected unpaired cluster segment
- CSSClass cluster_unpaired_s = new CSSClass(this.getClass(), SEG_UNPAIRED_SELECTED_CLASS);
- cluster_unpaired_s.setStatement(SVGConstants.SVG_FILL_ATTRIBUTE, style.getColor(STYLE_HOVER) + " !important");
- svgp.addCSSClassOrLogError(cluster_unpaired_s);
-
- // create Color shades for clusters
- String firstcol = style.getColor(STYLE_GRADIENT_FIRST);
- String secondcol = style.getColor(STYLE_GRADIENT_SECOND);
- String[] clusterColorShades = makeGradient(maxClusterSize, new String[] { firstcol, secondcol });
-
- for(int i = 0; i < maxClusterSize; i++) {
- CSSClass clusterClasses = new CSSClass(CircleSegmentsVisualizer.class, CLR_CLUSTER_CLASS_PREFIX + "_" + i);
- clusterClasses.setStatement(SVGConstants.SVG_FILL_ATTRIBUTE, clusterColorShades[i]);
- clusterClasses.setStatement(SVGConstants.SVG_STROKE_ATTRIBUTE, SVGConstants.SVG_NONE_VALUE);
- svgp.addCSSClassOrLogError(clusterClasses);
- }
- }
+ // and create svg elements
+ drawSegments();
- /**
- * Create the segments
- */
- private void drawSegments() {
- final int clusterings = segments.getClusterings();
+ //
+ // Build Interface
+ //
+ SVGCheckbox checkbox = new SVGCheckbox(showUnclusteredPairs, "Show unclustered pairs");
+ checkbox.addCheckBoxListener(new ChangeListener() {
+ @Override
+ public void stateChanged(ChangeEvent e) {
+ toggleUnclusteredPairs(((SVGCheckbox) e.getSource()).isChecked());
+ }
+ });
- // Reinitialize
- this.segmentToElements.clear();
+ // Add ring:clustering info
+ Element clrInfo = drawClusteringInfo();
+ Element c = checkbox.renderCheckBox(svgp, 1, 5 + Double.parseDouble(clrInfo.getAttribute(SVGConstants.SVG_HEIGHT_ATTRIBUTE)), 11);
+ ctrlLayer.appendChild(clrInfo);
+ ctrlLayer.appendChild(c);
- double angle_pair = (MathUtil.TWOPI - (SEGMENT_MIN_SEP_ANGLE * segments.size())) / segments.getPairCount(showUnclusteredPairs);
- final int pair_min_count = (int) Math.ceil(SEGMENT_MIN_ANGLE / angle_pair);
+ ctrlLayer.setAttribute(SVGConstants.SVG_TRANSFORM_ATTRIBUTE, "scale(" + (0.25 / StyleLibrary.SCALE) + ")");
- // number of segments needed to be resized
- int cluster_min_count = 0;
- for(Segment segment : segments) {
- if(segment.getPairCount() <= pair_min_count) {
- cluster_min_count++;
+ layer.appendChild(visLayer);
+ layer.appendChild(ctrlLayer);
+ }
+
+ /**
+ * Define and add required CSS classes
+ */
+ protected void addCSSClasses(int maxClusterSize) {
+ StyleLibrary style = context.getStyleResult().getStyleLibrary();
+
+ // Cluster separation lines
+ CSSClass cssReferenceBorder = new CSSClass(this.getClass(), CLR_BORDER_CLASS);
+ cssReferenceBorder.setStatement(SVGConstants.SVG_FILL_ATTRIBUTE, style.getColor(STYLE_BORDER));
+ svgp.addCSSClassOrLogError(cssReferenceBorder);
+
+ // Hover effect for clusters
+ CSSClass cluster_hover = new CSSClass(this.getClass(), CLR_HOVER_CLASS);
+ // Note: !important is needed to override the regular color assignment
+ cluster_hover.setStatement(SVGConstants.SVG_FILL_ATTRIBUTE, style.getColor(STYLE_HOVER) + " !important");
+ cluster_hover.setStatement(SVGConstants.SVG_CURSOR_TAG, SVGConstants.SVG_POINTER_VALUE);
+ svgp.addCSSClassOrLogError(cluster_hover);
+
+ // Unpaired cluster segment
+ CSSClass cluster_unpaired = new CSSClass(this.getClass(), CLR_UNPAIRED_CLASS);
+ cluster_unpaired.setStatement(SVGConstants.SVG_FILL_ATTRIBUTE, style.getBackgroundColor(STYLE));
+ cluster_unpaired.setStatement(SVGConstants.SVG_STROKE_ATTRIBUTE, SVGConstants.CSS_NONE_VALUE);
+ svgp.addCSSClassOrLogError(cluster_unpaired);
+
+ // Selected unpaired cluster segment
+ CSSClass cluster_unpaired_s = new CSSClass(this.getClass(), SEG_UNPAIRED_SELECTED_CLASS);
+ cluster_unpaired_s.setStatement(SVGConstants.SVG_FILL_ATTRIBUTE, style.getColor(STYLE_HOVER) + " !important");
+ svgp.addCSSClassOrLogError(cluster_unpaired_s);
+
+ // create Color shades for clusters
+ String firstcol = style.getColor(STYLE_GRADIENT_FIRST);
+ String secondcol = style.getColor(STYLE_GRADIENT_SECOND);
+ String[] clusterColorShades = makeGradient(maxClusterSize, new String[] { firstcol, secondcol });
+
+ for(int i = 0; i < maxClusterSize; i++) {
+ CSSClass clusterClasses = new CSSClass(CircleSegmentsVisualizer.class, CLR_CLUSTER_CLASS_PREFIX + "_" + i);
+ clusterClasses.setStatement(SVGConstants.SVG_FILL_ATTRIBUTE, clusterColorShades[i]);
+ clusterClasses.setStatement(SVGConstants.SVG_STROKE_ATTRIBUTE, SVGConstants.SVG_NONE_VALUE);
+ svgp.addCSSClassOrLogError(clusterClasses);
}
}
- // update width of a pair
- angle_pair = (MathUtil.TWOPI - (SEGMENT_MIN_SEP_ANGLE * segments.size() + cluster_min_count * SEGMENT_MIN_ANGLE)) / (segments.getPairCount(showUnclusteredPairs) - cluster_min_count);
- double radius_delta = (RADIUS_OUTER - RADIUS_INNER - clusterings * RADIUS_DISTANCE) / clusterings;
- double border_width = SEGMENT_MIN_SEP_ANGLE;
+ /**
+ * Create the segments
+ */
+ private void drawSegments() {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ final int clusterings = segments.getClusterings();
- int refClustering = 0;
- int refSegment = Segment.UNCLUSTERED;
- double offsetAngle = 0.0;
+ // Reinitialize
+ this.segmentToElements.clear();
- for(final Segment segment : segments) {
- long currentPairCount = segment.getPairCount();
+ double angle_pair = (MathUtil.TWOPI - (SEGMENT_MIN_SEP_ANGLE * segments.size())) / segments.getPairCount(showUnclusteredPairs);
+ final int pair_min_count = (int) Math.ceil(SEGMENT_MIN_ANGLE / angle_pair);
- // resize small segments if below minimum
- double alpha = SEGMENT_MIN_ANGLE;
- if(currentPairCount > pair_min_count) {
- alpha = angle_pair * currentPairCount;
+ // number of segments needed to be resized
+ int cluster_min_count = 0;
+ for(Segment segment : segments) {
+ if(segment.getPairCount() <= pair_min_count) {
+ cluster_min_count++;
+ }
}
- // ITERATE OVER ALL SEGMENT-CLUSTERS
-
- ArrayList<Element> elems = new ArrayList<Element>(clusterings);
- segmentToElements.put(segment, elems);
- // draw segment for every clustering
+ // update width of a pair
+ angle_pair = (MathUtil.TWOPI - (SEGMENT_MIN_SEP_ANGLE * segments.size() + cluster_min_count * SEGMENT_MIN_ANGLE)) / (segments.getPairCount(showUnclusteredPairs) - cluster_min_count);
+ double radius_delta = (RADIUS_OUTER - RADIUS_INNER - clusterings * RADIUS_DISTANCE) / clusterings;
+ double border_width = SEGMENT_MIN_SEP_ANGLE;
- for(int i = 0; i < clusterings; i++) {
- double currentRadius = i * (radius_delta + RADIUS_DISTANCE) + RADIUS_INNER;
+ int refClustering = 0;
+ int refSegment = Segment.UNCLUSTERED;
+ double offsetAngle = 0.0;
- // Add border if the next segment is a different cluster in the
- // reference clustering
- if((refSegment != segment.get(refClustering)) && refClustering == i) {
- Element border = SVGUtil.svgCircleSegment(svgp, 0, 0, offsetAngle - SEGMENT_MIN_SEP_ANGLE, border_width, currentRadius, RADIUS_OUTER - RADIUS_DISTANCE);
- border.setAttribute(SVGConstants.SVG_CLASS_ATTRIBUTE, CLR_BORDER_CLASS);
- visLayer.appendChild(border);
+ for(final Segment segment : segments) {
+ long currentPairCount = segment.getPairCount();
- if(segment.get(refClustering) == Segment.UNCLUSTERED) {
- refClustering = Math.min(refClustering + 1, clusterings - 1);
- }
- refSegment = segment.get(refClustering);
+ // resize small segments if below minimum
+ double alpha = SEGMENT_MIN_ANGLE;
+ if(currentPairCount > pair_min_count) {
+ alpha = angle_pair * currentPairCount;
}
- int cluster = segment.get(i);
+ // ITERATE OVER ALL SEGMENT-CLUSTERS
- // create ring segment
- Element segelement = SVGUtil.svgCircleSegment(svgp, 0, 0, offsetAngle, alpha, currentRadius, currentRadius + radius_delta);
- elems.add(segelement);
+ ArrayList<Element> elems = new ArrayList<Element>(clusterings);
+ segmentToElements.put(segment, elems);
+ // draw segment for every clustering
- // MouseEvents on segment cluster
- EventListener listener = new SegmentListenerProxy(segment, i);
- EventTarget targ = (EventTarget) segelement;
- targ.addEventListener(SVGConstants.SVG_MOUSEOVER_EVENT_TYPE, listener, false);
- targ.addEventListener(SVGConstants.SVG_MOUSEOUT_EVENT_TYPE, listener, false);
- targ.addEventListener(SVGConstants.SVG_CLICK_EVENT_TYPE, listener, false);
+ for(int i = 0; i < clusterings; i++) {
+ double currentRadius = i * (radius_delta + RADIUS_DISTANCE) + RADIUS_INNER;
- // Coloring based on clusterID
- if(cluster >= 0) {
- segelement.setAttribute(SVGConstants.SVG_CLASS_ATTRIBUTE, CLR_CLUSTER_CLASS_PREFIX + "_" + cluster);
- }
- // if its an unpaired cluster set color to white
- else {
- segelement.setAttribute(SVGConstants.SVG_CLASS_ATTRIBUTE, CLR_UNPAIRED_CLASS);
- }
+ // Add border if the next segment is a different cluster in the
+ // reference clustering
+ if((refSegment != segment.get(refClustering)) && refClustering == i) {
+ Element border = SVGUtil.svgCircleSegment(svgp, 0, 0, offsetAngle - SEGMENT_MIN_SEP_ANGLE, border_width, currentRadius, RADIUS_OUTER - RADIUS_DISTANCE);
+ border.setAttribute(SVGConstants.SVG_CLASS_ATTRIBUTE, CLR_BORDER_CLASS);
+ visLayer.appendChild(border);
- visLayer.appendChild(segelement);
- }
+ if(segment.get(refClustering) == Segment.UNCLUSTERED) {
+ refClustering = Math.min(refClustering + 1, clusterings - 1);
+ }
+ refSegment = segment.get(refClustering);
+ }
- //
- // Add a extended strip for each segment to emphasis selection
- // (easier to track thin segments and their color coding and
- // differentiates them from cluster border lines)
- //
+ int cluster = segment.get(i);
- double currentRadius = clusterings * (radius_delta + RADIUS_DISTANCE) + RADIUS_INNER;
- Element extension = SVGUtil.svgCircleSegment(svgp, 0, 0, offsetAngle, alpha, currentRadius, currentRadius + RADIUS_SELECTION);
- extension.setAttribute(SVGConstants.SVG_CLASS_ATTRIBUTE, CLR_UNPAIRED_CLASS);
- elems.add(extension);
+ // create ring segment
+ Element segelement = SVGUtil.svgCircleSegment(svgp, 0, 0, offsetAngle, alpha, currentRadius, currentRadius + radius_delta);
+ elems.add(segelement);
- if(segment.isUnpaired()) {
- if(policy.isSelected(segment)) {
- SVGUtil.addCSSClass(extension, SEG_UNPAIRED_SELECTED_CLASS);
- }
- else {
- // Remove highlight
- SVGUtil.removeCSSClass(extension, SEG_UNPAIRED_SELECTED_CLASS);
- }
- }
- else {
- int idx = policy.indexOfSegment(segment);
- if(idx >= 0) {
- String color = context.getStyleLibrary().getColorSet(StyleLibrary.PLOT).getColor(idx);
- extension.setAttribute(SVGConstants.SVG_STYLE_ATTRIBUTE, SVGConstants.CSS_FILL_PROPERTY + ":" + color);
- }
- else {
- // Remove styling
- extension.removeAttribute(SVGConstants.SVG_STYLE_ATTRIBUTE);
+ // MouseEvents on segment cluster
+ EventListener listener = new SegmentListenerProxy(segment, i);
+ EventTarget targ = (EventTarget) segelement;
+ targ.addEventListener(SVGConstants.SVG_MOUSEOVER_EVENT_TYPE, listener, false);
+ targ.addEventListener(SVGConstants.SVG_MOUSEOUT_EVENT_TYPE, listener, false);
+ targ.addEventListener(SVGConstants.SVG_CLICK_EVENT_TYPE, listener, false);
+
+ // Coloring based on clusterID
+ if(cluster >= 0) {
+ segelement.setAttribute(SVGConstants.SVG_CLASS_ATTRIBUTE, CLR_CLUSTER_CLASS_PREFIX + "_" + cluster);
+ }
+ // if its an unpaired cluster set color to white
+ else {
+ segelement.setAttribute(SVGConstants.SVG_CLASS_ATTRIBUTE, CLR_UNPAIRED_CLASS);
+ }
+
+ visLayer.appendChild(segelement);
}
- }
- visLayer.appendChild(extension);
+ //
+ // Add a extended strip for each segment to emphasis selection
+ // (easier to track thin segments and their color coding and
+ // differentiates them from cluster border lines)
+ //
- // calculate angle for next segment
- offsetAngle += alpha + SEGMENT_MIN_SEP_ANGLE;
- }
- }
+ double currentRadius = clusterings * (radius_delta + RADIUS_DISTANCE) + RADIUS_INNER;
+ Element extension = SVGUtil.svgCircleSegment(svgp, 0, 0, offsetAngle, alpha, currentRadius, currentRadius + RADIUS_SELECTION);
+ extension.setAttribute(SVGConstants.SVG_CLASS_ATTRIBUTE, CLR_UNPAIRED_CLASS);
+ elems.add(extension);
- private void redrawSelection() {
- logger.debug("Updating selection only.");
- for(Entry<Segment, List<Element>> entry : segmentToElements.entrySet()) {
- Segment segment = entry.getKey();
- // The selection marker is the extra element in the list
- Element extension = entry.getValue().get(segments.getClusterings());
- if(segment.isUnpaired()) {
- if(policy.isSelected(segment)) {
- SVGUtil.addCSSClass(extension, SEG_UNPAIRED_SELECTED_CLASS);
+ if(segment.isUnpaired()) {
+ if(policy.isSelected(segment)) {
+ SVGUtil.addCSSClass(extension, SEG_UNPAIRED_SELECTED_CLASS);
+ }
+ else {
+ // Remove highlight
+ SVGUtil.removeCSSClass(extension, SEG_UNPAIRED_SELECTED_CLASS);
+ }
}
else {
- // Remove highlight
- SVGUtil.removeCSSClass(extension, SEG_UNPAIRED_SELECTED_CLASS);
+ int idx = policy.indexOfSegment(segment);
+ if(idx >= 0) {
+ String color = style.getColorSet(StyleLibrary.PLOT).getColor(idx);
+ extension.setAttribute(SVGConstants.SVG_STYLE_ATTRIBUTE, SVGConstants.CSS_FILL_PROPERTY + ":" + color);
+ }
+ else {
+ // Remove styling
+ extension.removeAttribute(SVGConstants.SVG_STYLE_ATTRIBUTE);
+ }
}
+
+ visLayer.appendChild(extension);
+
+ // calculate angle for next segment
+ offsetAngle += alpha + SEGMENT_MIN_SEP_ANGLE;
}
- else {
- int idx = policy.indexOfSegment(segment);
- if(idx >= 0) {
- String color = context.getStyleLibrary().getColorSet(StyleLibrary.PLOT).getColor(idx);
- extension.setAttribute(SVGConstants.SVG_STYLE_ATTRIBUTE, SVGConstants.CSS_FILL_PROPERTY + ":" + color);
+ }
+
+ private void redrawSelection() {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ LOG.debug("Updating selection only.");
+ for(Entry<Segment, List<Element>> entry : segmentToElements.entrySet()) {
+ Segment segment = entry.getKey();
+ // The selection marker is the extra element in the list
+ Element extension = entry.getValue().get(segments.getClusterings());
+ if(segment.isUnpaired()) {
+ if(policy.isSelected(segment)) {
+ SVGUtil.addCSSClass(extension, SEG_UNPAIRED_SELECTED_CLASS);
+ }
+ else {
+ // Remove highlight
+ SVGUtil.removeCSSClass(extension, SEG_UNPAIRED_SELECTED_CLASS);
+ }
}
else {
- // Remove styling
- extension.removeAttribute(SVGConstants.SVG_STYLE_ATTRIBUTE);
+ int idx = policy.indexOfSegment(segment);
+ if(idx >= 0) {
+ String color = style.getColorSet(StyleLibrary.PLOT).getColor(idx);
+ extension.setAttribute(SVGConstants.SVG_STYLE_ATTRIBUTE, SVGConstants.CSS_FILL_PROPERTY + ":" + color);
+ }
+ else {
+ // Remove styling
+ extension.removeAttribute(SVGConstants.SVG_STYLE_ATTRIBUTE);
+ }
}
}
}
- }
- /**
- * Creates a gradient over a set of colors
- *
- * @param shades number of colors in the gradient
- * @param colors colors for the gradient
- * @return array of colors for CSS
- */
- protected static String[] makeGradient(int shades, String[] colors) {
- if(shades <= colors.length) {
- return colors;
- }
+ /**
+ * Creates a gradient over a set of colors
+ *
+ * @param shades number of colors in the gradient
+ * @param colors colors for the gradient
+ * @return array of colors for CSS
+ */
+ protected String[] makeGradient(int shades, String[] colors) {
+ if(shades <= colors.length) {
+ return colors;
+ }
- // Convert SVG colors into AWT colors for math
- Color[] cols = new Color[colors.length];
- for(int i = 0; i < colors.length; i++) {
- cols[i] = SVGUtil.stringToColor(colors[i]);
- if(cols[i] == null) {
- throw new AbortException("Error parsing color: " + colors[i]);
+ // Convert SVG colors into AWT colors for math
+ Color[] cols = new Color[colors.length];
+ for(int i = 0; i < colors.length; i++) {
+ cols[i] = SVGUtil.stringToColor(colors[i]);
+ if(cols[i] == null) {
+ throw new AbortException("Error parsing color: " + colors[i]);
+ }
}
- }
- // Step size
- double increment = (cols.length - 1.) / shades;
+ // Step size
+ double increment = (cols.length - 1.) / shades;
- String[] colorShades = new String[shades];
+ String[] colorShades = new String[shades];
- for(int s = 0; s < shades; s++) {
- final int ppos = Math.min((int) Math.floor(increment * s), cols.length);
- final int npos = Math.min((int) Math.ceil(increment * s), cols.length);
- if(ppos == npos) {
- colorShades[s] = colors[ppos];
- }
- else {
- Color prev = cols[ppos];
- Color next = cols[npos];
- final double mix = (increment * s - ppos) / (npos - ppos);
- final int r = (int) ((1 - mix) * prev.getRed() + mix * next.getRed());
- final int g = (int) ((1 - mix) * prev.getGreen() + mix * next.getGreen());
- final int b = (int) ((1 - mix) * prev.getBlue() + mix * next.getBlue());
- colorShades[s] = SVGUtil.colorToString(((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF));
+ for(int s = 0; s < shades; s++) {
+ final int ppos = Math.min((int) Math.floor(increment * s), cols.length);
+ final int npos = Math.min((int) Math.ceil(increment * s), cols.length);
+ if(ppos == npos) {
+ colorShades[s] = colors[ppos];
+ }
+ else {
+ Color prev = cols[ppos];
+ Color next = cols[npos];
+ final double mix = (increment * s - ppos) / (npos - ppos);
+ final int r = (int) ((1 - mix) * prev.getRed() + mix * next.getRed());
+ final int g = (int) ((1 - mix) * prev.getGreen() + mix * next.getGreen());
+ final int b = (int) ((1 - mix) * prev.getBlue() + mix * next.getBlue());
+ colorShades[s] = SVGUtil.colorToString(((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF));
+ }
}
+
+ return colorShades;
}
- return colorShades;
- }
+ protected Element drawClusteringInfo() {
+ Element thumbnail = SVGUtil.svgElement(svgp.getDocument(), SVGConstants.SVG_G_TAG);
- protected Element drawClusteringInfo() {
- Element thumbnail = SVGUtil.svgElement(svgp.getDocument(), SVGConstants.SVG_G_TAG);
+ // build thumbnail
+ int startRadius = 4;
+ int singleHeight = 12;
+ int margin = 4;
+ int radius = segments.getClusterings() * (singleHeight + margin) + startRadius;
- // build thumbnail
- int startRadius = 4;
- int singleHeight = 12;
- int margin = 4;
- int radius = segments.getClusterings() * (singleHeight + margin) + startRadius;
+ SVGUtil.setAtt(thumbnail, SVGConstants.SVG_HEIGHT_ATTRIBUTE, radius);
- SVGUtil.setAtt(thumbnail, SVGConstants.SVG_HEIGHT_ATTRIBUTE, radius);
+ for(int i = 0; i < segments.getClusterings(); i++) {
+ double innerRadius = i * singleHeight + margin * i + startRadius;
+ Element clr = SVGUtil.svgCircleSegment(svgp, radius - startRadius, radius - startRadius, Math.PI * 1.5, Math.PI * 0.5, innerRadius, innerRadius + singleHeight);
+ // FIXME: Use StyleLibrary
+ clr.setAttribute(SVGConstants.SVG_FILL_ATTRIBUTE, "#d4e4f1");
+ clr.setAttribute(SVGConstants.SVG_STROKE_ATTRIBUTE, "#a0a0a0");
+ clr.setAttribute(SVGConstants.SVG_STROKE_WIDTH_ATTRIBUTE, "1.0");
- for(int i = 0; i < segments.getClusterings(); i++) {
- double innerRadius = i * singleHeight + margin * i + startRadius;
- Element clr = SVGUtil.svgCircleSegment(svgp, radius - startRadius, radius - startRadius, Math.PI * 1.5, Math.PI * 0.5, innerRadius, innerRadius + singleHeight);
- // FIXME: Use StyleLibrary
- clr.setAttribute(SVGConstants.SVG_FILL_ATTRIBUTE, "#d4e4f1");
- clr.setAttribute(SVGConstants.SVG_STROKE_ATTRIBUTE, "#a0a0a0");
- clr.setAttribute(SVGConstants.SVG_STROKE_WIDTH_ATTRIBUTE, "1.0");
+ String labelText = segments.getClusteringDescription(i);
+ Element label = svgp.svgText(radius + startRadius, radius - innerRadius - startRadius, labelText);
+ thumbnail.appendChild(label);
- String labelText = segments.getClusteringDescription(i);
- Element label = svgp.svgText(radius + startRadius, radius - innerRadius - startRadius, labelText);
- thumbnail.appendChild(label);
+ thumbnail.appendChild(clr);
+ }
- thumbnail.appendChild(clr);
+ return thumbnail;
}
- return thumbnail;
- }
-
- protected void segmentHover(Segment segment, int ringid, boolean active) {
- if(active) {
- // abort if this are the unclustered pairs
- if(segment.isNone()) {
- return;
- }
- if(logger.isDebugging()) {
- logger.debug("Hover on segment: " + segment + " unpaired: " + segment.isUnpaired());
- }
+ protected void segmentHover(Segment segment, int ringid, boolean active) {
+ if(active) {
+ // abort if this are the unclustered pairs
+ if(segment.isNone()) {
+ return;
+ }
+ if(LOG.isDebugging()) {
+ LOG.debug("Hover on segment: " + segment + " unpaired: " + segment.isUnpaired());
+ }
- if(!segment.isUnpaired()) {
- //
- // STANDARD CLUSTER SEGMENT
- // highlight all ring segments in this clustering and this cluster
- //
- // highlight all corresponding ring Segments
- for(Entry<Segment, List<Element>> entry : segmentToElements.entrySet()) {
- Segment other = entry.getKey();
- // Same cluster in same clustering?
- if(other.get(ringid) != segment.get(ringid)) {
- continue;
+ if(!segment.isUnpaired()) {
+ //
+ // STANDARD CLUSTER SEGMENT
+ // highlight all ring segments in this clustering and this cluster
+ //
+ // highlight all corresponding ring Segments
+ for(Entry<Segment, List<Element>> entry : segmentToElements.entrySet()) {
+ Segment other = entry.getKey();
+ // Same cluster in same clustering?
+ if(other.get(ringid) != segment.get(ringid)) {
+ continue;
+ }
+ Element ringSegment = entry.getValue().get(ringid);
+ SVGUtil.addCSSClass(ringSegment, CLR_HOVER_CLASS);
}
- Element ringSegment = entry.getValue().get(ringid);
- SVGUtil.addCSSClass(ringSegment, CLR_HOVER_CLASS);
}
- }
- else {
- //
- // UNPAIRED SEGMENT
- // highlight all ring segments in this clustering responsible for
- // unpaired
- // segment
- //
- // get the paired segments corresponding to the unpaired segment
- List<Segment> paired = segments.getPairedSegments(segment);
-
- for(Segment other : paired) {
- Element ringSegment = segmentToElements.get(other).get(ringid);
- SVGUtil.addCSSClass(ringSegment, CLR_HOVER_CLASS);
+ else {
+ //
+ // UNPAIRED SEGMENT
+ // highlight all ring segments in this clustering responsible for
+ // unpaired
+ // segment
+ //
+ // get the paired segments corresponding to the unpaired segment
+ List<Segment> paired = segments.getPairedSegments(segment);
+
+ for(Segment other : paired) {
+ Element ringSegment = segmentToElements.get(other).get(ringid);
+ SVGUtil.addCSSClass(ringSegment, CLR_HOVER_CLASS);
+ }
}
}
- }
- else {
- for(List<Element> elems : segmentToElements.values()) {
- for(Element current : elems) {
- SVGUtil.removeCSSClass(current, CLR_HOVER_CLASS);
+ else {
+ for(List<Element> elems : segmentToElements.values()) {
+ for(Element current : elems) {
+ SVGUtil.removeCSSClass(current, CLR_HOVER_CLASS);
+ }
}
}
}
- }
- protected void segmentClick(Segment segment, Event evt, boolean dblClick) {
- MouseEvent mouse = (MouseEvent) evt;
+ protected void segmentClick(Segment segment, Event evt, boolean dblClick) {
+ MouseEvent mouse = (MouseEvent) evt;
- // CTRL (add) pressed?
- boolean ctrl = false;
- if(mouse.getCtrlKey()) {
- ctrl = true;
- }
+ // CTRL (add) pressed?
+ boolean ctrl = false;
+ if(mouse.getCtrlKey()) {
+ ctrl = true;
+ }
- // Unselect others on double click
- if(dblClick) {
- policy.deselectAllSegments();
+ // Unselect others on double click
+ if(dblClick) {
+ policy.deselectAllSegments();
+ }
+ policy.select(segment, ctrl);
+ // update stylePolicy
+ context.getStyleResult().setStylingPolicy(policy);
+ // fire changed event to trigger redraw
+ context.getHierarchy().resultChanged(context.getStyleResult());
}
- policy.select(segment, ctrl);
- // update stylePolicy
- context.getStyleResult().setStylingPolicy(policy);
- // fire changed event to trigger redraw
- context.getHierarchy().resultChanged(context.getStyleResult());
- }
- /**
- * Proxy element to connect signals.
- *
- * @author Erich Schubert
- *
- * @apiviz.exclude
- */
- private class SegmentListenerProxy implements EventListener {
/**
- * Mouse double click time window in milliseconds
+ * Proxy element to connect signals.
*
- * TODO: does Batik have double click events?
- */
- public static final int EVT_DBLCLICK_DELAY = 350;
-
- /**
- * Segment we are attached to
- */
- private Segment id;
-
- /**
- * Segment ring we are
- */
- private int ringid;
-
- /**
- * For detecting double clicks.
- */
- private long lastClick = 0;
-
- /**
- * Constructor.
+ * @author Erich Schubert
*
- * @param id Segment id
- * @param ringid Ring id
+ * @apiviz.exclude
*/
- public SegmentListenerProxy(Segment id, int ringid) {
- super();
- this.id = id;
- this.ringid = ringid;
- }
-
- @Override
- public void handleEvent(Event evt) {
- if(SVGConstants.SVG_MOUSEOVER_EVENT_TYPE.equals(evt.getType())) {
- segmentHover(id, ringid, true);
+ private class SegmentListenerProxy implements EventListener {
+ /**
+ * Mouse double click time window in milliseconds
+ *
+ * TODO: does Batik have double click events?
+ */
+ public static final int EVT_DBLCLICK_DELAY = 350;
+
+ /**
+ * Segment we are attached to
+ */
+ private Segment id;
+
+ /**
+ * Segment ring we are
+ */
+ private int ringid;
+
+ /**
+ * For detecting double clicks.
+ */
+ private long lastClick = 0;
+
+ /**
+ * Constructor.
+ *
+ * @param id Segment id
+ * @param ringid Ring id
+ */
+ public SegmentListenerProxy(Segment id, int ringid) {
+ super();
+ this.id = id;
+ this.ringid = ringid;
}
- if(SVGConstants.SVG_MOUSEOUT_EVENT_TYPE.equals(evt.getType())) {
- segmentHover(id, ringid, false);
- }
- if(SVGConstants.SVG_CLICK_EVENT_TYPE.equals(evt.getType())) {
- // Check Double Click
- boolean dblClick = false;
- long time = java.util.Calendar.getInstance().getTimeInMillis();
- if(time - lastClick <= EVT_DBLCLICK_DELAY) {
- dblClick = true;
- }
- lastClick = time;
-
- segmentClick(id, evt, dblClick);
- }
- }
- }
-
- /**
- * Factory for visualizers for a circle segment
- *
- * @author Sascha Goldhofer
- *
- * @apiviz.stereotype factory
- * @apiviz.uses CircleSegmentsVisualizer oneway - - «create»
- */
- public static class Factory extends AbstractVisFactory {
- /**
- * Constructor
- */
- public Factory() {
- super();
- this.thumbmask |= ThumbnailVisualization.ON_STYLE;
- }
- @Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new CircleSegmentsVisualizer(task);
- }
+ @Override
+ public void handleEvent(Event evt) {
+ if(SVGConstants.SVG_MOUSEOVER_EVENT_TYPE.equals(evt.getType())) {
+ segmentHover(id, ringid, true);
+ }
+ if(SVGConstants.SVG_MOUSEOUT_EVENT_TYPE.equals(evt.getType())) {
+ segmentHover(id, ringid, false);
+ }
+ if(SVGConstants.SVG_CLICK_EVENT_TYPE.equals(evt.getType())) {
+ // Check Double Click
+ boolean dblClick = false;
+ long time = java.util.Calendar.getInstance().getTimeInMillis();
+ if(time - lastClick <= EVT_DBLCLICK_DELAY) {
+ dblClick = true;
+ }
+ lastClick = time;
- @Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- // If no comparison result found abort
- List<Segments> segments = ResultUtil.filterResults(result, Segments.class);
- for(Segments segmentResult : segments) {
- SegmentsStylingPolicy policy;
- List<SegmentsStylingPolicy> styles = ResultUtil.filterResults(segmentResult, SegmentsStylingPolicy.class);
- if (!styles.isEmpty()) {
- policy = styles.get(0);
- } else {
- policy = new SegmentsStylingPolicy(segmentResult);
- baseResult.getHierarchy().add(segmentResult, policy);
+ segmentClick(id, evt, dblClick);
}
- // create task for visualization
- final VisualizationTask task = new VisualizationTask(NAME, policy, null, this);
- task.width = 2.0;
- task.height = 2.0;
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_INTERACTIVE);
- baseResult.getHierarchy().add(segmentResult, task);
}
}
- };
+ }
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/AbstractParallelVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/AbstractParallelVisualization.java
index babcb864..a2039126 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/AbstractParallelVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/AbstractParallelVisualization.java
@@ -44,7 +44,7 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisualization;
*
* @param <NV> Vector type in relation
*/
-public abstract class AbstractParallelVisualization<NV extends NumberVector<?, ?>> extends AbstractVisualization {
+public abstract class AbstractParallelVisualization<NV extends NumberVector<?>> extends AbstractVisualization {
/**
* The current projection
*/
@@ -80,9 +80,8 @@ public abstract class AbstractParallelVisualization<NV extends NumberVector<?, ?
this.proj = task.getProj();
this.relation = task.getRelation();
- double ratio = task.width / task.height;
-
- margins = new double[] { 0.05 * StyleLibrary.SCALE, 0.1 * StyleLibrary.SCALE, 0.05 * StyleLibrary.SCALE, 0.4 * StyleLibrary.SCALE };
+ margins = new double[] { 0.05 * StyleLibrary.SCALE, 0.1 * StyleLibrary.SCALE, 0.05 * StyleLibrary.SCALE, 0.1 * StyleLibrary.SCALE };
+ double ratio = (task.width * StyleLibrary.SCALE - margins[0] - margins[2]) / (task.height * StyleLibrary.SCALE - margins[1] - margins[3]);
size = new double[] { ratio * StyleLibrary.SCALE, StyleLibrary.SCALE };
recalcAxisPositions();
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/AxisReorderVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/AxisReorderVisualization.java
new file mode 100644
index 00000000..6d77f592
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/AxisReorderVisualization.java
@@ -0,0 +1,296 @@
+package de.lmu.ifi.dbs.elki.visualization.visualizers.parallel;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.Collection;
+
+import org.apache.batik.util.SVGConstants;
+import org.w3c.dom.Element;
+import org.w3c.dom.events.Event;
+import org.w3c.dom.events.EventListener;
+import org.w3c.dom.events.EventTarget;
+
+import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
+import de.lmu.ifi.dbs.elki.result.Result;
+import de.lmu.ifi.dbs.elki.result.ResultUtil;
+import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
+import de.lmu.ifi.dbs.elki.visualization.css.CSSClass;
+import de.lmu.ifi.dbs.elki.visualization.projector.ParallelPlotProjector;
+import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGArrow;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
+
+/**
+ * Interactive SVG-Elements for reordering the axes.
+ *
+ * @author Robert Rödler
+ * @author Erich Schubert
+ *
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
+ */
+public class AxisReorderVisualization extends AbstractVisFactory {
+ /**
+ * A short name characterizing this Visualizer.
+ */
+ private static final String NAME = "Dimension Ordering Tool";
+
+ /**
+ * Constructor, adhering to
+ */
+ public AxisReorderVisualization() {
+ super();
+ }
+
+ @Override
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
+ }
+
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<ParallelPlotProjector<?>> ps = ResultUtil.filterResults(result, ParallelPlotProjector.class);
+ for(ParallelPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, p, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_INTERACTIVE;
+ task.noexport = true;
+ task.thumbnail = false;
+ baseResult.getHierarchy().add(p, task);
+ }
+ }
+
+ /**
+ * Instance for a particular plot.
+ *
+ * @author Robert Rödler
+ * @author Erich Schubert
+ */
+ public class Instance extends AbstractParallelVisualization<NumberVector<?>> {
+ /**
+ * Generic tags to indicate the type of element. Used in IDs, CSS-Classes
+ * etc.
+ */
+ public static final String SELECTDIMENSIONORDER = "SelectDimensionOrder";
+
+ /**
+ * CSS class for a tool button
+ */
+ public static final String SDO_BUTTON = "DObutton";
+
+ /**
+ * CSS class for a button border
+ */
+ public static final String SDO_BORDER = "DOborder";
+
+ /**
+ * CSS class for a button cross
+ */
+ public static final String SDO_ARROW = "DOarrow";
+
+ /**
+ * Currently selected dimension. Use -1 to not have a dimension selected.
+ */
+ private int selecteddim = -1;
+
+ /**
+ * Constructor.
+ *
+ * @param task VisualizationTask
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
+ incrementalRedraw();
+ context.addResultListener(this);
+ }
+
+ @Override
+ protected void redraw() {
+ addCSSClasses(svgp);
+ final int dim = proj.getVisibleDimensions();
+
+ final double controlsize = 0.025 * getSizeY();
+ final double buttonsize = 0.75 * controlsize;
+ final double padding = 0.125 * controlsize;
+ final double arrowsize = .75 * buttonsize;
+ final double ypos = getSizeY() + getMarginTop() * .5 + controlsize;
+ final double spacing = 0.9 * controlsize;
+
+ Element back = svgp.svgRect(-controlsize * .5, ypos, getSizeX() + controlsize, controlsize);
+ SVGUtil.addCSSClass(back, SELECTDIMENSIONORDER);
+ layer.appendChild(back);
+
+ if(selecteddim < 0) {
+ // Nothing selected
+ for(int i = 0; i < dim; i++) {
+ final double xpos = getVisibleAxisX(i);
+ if(i > 0) {
+ Element arrow = SVGArrow.makeArrow(svgp, SVGArrow.LEFT, xpos - spacing, ypos + controlsize * .5, arrowsize);
+ SVGUtil.addCSSClass(arrow, SDO_ARROW);
+ layer.appendChild(arrow);
+ Element button = svgp.svgRect(xpos - spacing - buttonsize * .5, ypos + padding, buttonsize, buttonsize);
+ SVGUtil.addCSSClass(button, SDO_BUTTON);
+ addEventListener(button, i, SVGArrow.LEFT);
+ layer.appendChild(button);
+ }
+ {
+ Element arrow = SVGArrow.makeArrow(svgp, SVGArrow.DOWN, xpos, ypos + controlsize * .5, arrowsize);
+ SVGUtil.addCSSClass(arrow, SDO_ARROW);
+ layer.appendChild(arrow);
+ Element button = svgp.svgRect(xpos - buttonsize * .5, ypos + padding, buttonsize, buttonsize);
+ SVGUtil.addCSSClass(button, SDO_BUTTON);
+ addEventListener(button, i, SVGArrow.DOWN);
+ layer.appendChild(button);
+ }
+ if(i < dim - 1) {
+ Element arrow = SVGArrow.makeArrow(svgp, SVGArrow.RIGHT, xpos + spacing, ypos + controlsize * .5, arrowsize);
+ SVGUtil.addCSSClass(arrow, SDO_ARROW);
+ layer.appendChild(arrow);
+ Element button = svgp.svgRect(xpos + spacing - buttonsize * .5, ypos + padding, buttonsize, buttonsize);
+ SVGUtil.addCSSClass(button, SDO_BUTTON);
+ addEventListener(button, i, SVGArrow.RIGHT);
+ layer.appendChild(button);
+ }
+ }
+ }
+ else {
+ for(int i = 0; i < dim; i++) {
+ {
+ Element arrow = SVGArrow.makeArrow(svgp, SVGArrow.DOWN, getVisibleAxisX(i), ypos + controlsize * .5, arrowsize);
+ SVGUtil.addCSSClass(arrow, SDO_ARROW);
+ layer.appendChild(arrow);
+ Element button = svgp.svgRect(getVisibleAxisX(i) - buttonsize * .5, ypos + padding, buttonsize, buttonsize);
+ SVGUtil.addCSSClass(button, SDO_BUTTON);
+ addEventListener(button, i, SVGArrow.DOWN);
+ layer.appendChild(button);
+ }
+ if(i > 0.) {
+ Element arrow = SVGArrow.makeArrow(svgp, SVGArrow.UP, getVisibleAxisX(i - .5), ypos + controlsize * .5, arrowsize);
+ SVGUtil.addCSSClass(arrow, SDO_ARROW);
+ layer.appendChild(arrow);
+ Element button = svgp.svgRect(getVisibleAxisX(i - .5) - buttonsize * .5, ypos + padding, buttonsize, buttonsize);
+ SVGUtil.addCSSClass(button, SDO_BUTTON);
+ addEventListener(button, i, SVGArrow.UP);
+ layer.appendChild(button);
+ }
+ }
+ }
+ }
+
+ /**
+ * Add an event listener to the Element
+ *
+ * @param tag Element to add the listener
+ * @param i represented axis
+ */
+ private void addEventListener(final Element tag, final int i, final SVGArrow.Direction j) {
+ EventTarget targ = (EventTarget) tag;
+ targ.addEventListener(SVGConstants.SVG_EVENT_CLICK, new EventListener() {
+ @Override
+ public void handleEvent(Event evt) {
+ if(selecteddim < 0) {
+ switch(j){
+ case DOWN:
+ selecteddim = i;
+ break;
+ case LEFT:
+ int prev = i - 1;
+ while(prev >= 0 && !proj.isAxisVisible(prev)) {
+ prev -= 1;
+ }
+ proj.swapAxes(i, prev);
+ break;
+ case RIGHT:
+ int next = i + 1;
+ while(next < proj.getInputDimensionality() - 1 && !proj.isAxisVisible(next)) {
+ next += 1;
+ }
+ proj.swapAxes(i, next);
+ break;
+ default:
+ break;
+ }
+ }
+ else {
+ switch(j){
+ case DOWN:
+ proj.swapAxes(selecteddim, i);
+ selecteddim = -1;
+ break;
+ case UP:
+ if(selecteddim != i) {
+ proj.moveAxis(selecteddim, i);
+ }
+ selecteddim = -1;
+ break;
+ default:
+ break;
+ }
+ }
+ // Notify
+ context.getHierarchy().resultChanged(proj);
+ }
+ }, false);
+ }
+
+ /**
+ * Adds the required CSS-Classes
+ *
+ * @param svgp SVG-Plot
+ */
+ private void addCSSClasses(SVGPlot svgp) {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ if(!svgp.getCSSClassManager().contains(SELECTDIMENSIONORDER)) {
+ CSSClass cls = new CSSClass(this, SELECTDIMENSIONORDER);
+ cls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, 0.1);
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_BLUE_VALUE);
+ svgp.addCSSClassOrLogError(cls);
+ }
+ if(!svgp.getCSSClassManager().contains(SDO_BORDER)) {
+ CSSClass cls = new CSSClass(this, SDO_BORDER);
+ cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, SVGConstants.CSS_GREY_VALUE);
+ cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.PLOT) / 3.0);
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
+ svgp.addCSSClassOrLogError(cls);
+ }
+ if(!svgp.getCSSClassManager().contains(SDO_BUTTON)) {
+ CSSClass cls = new CSSClass(this, SDO_BUTTON);
+ cls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, 0.01);
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_GREY_VALUE);
+ cls.setStatement(SVGConstants.CSS_CURSOR_PROPERTY, SVGConstants.CSS_POINTER_VALUE);
+ svgp.addCSSClassOrLogError(cls);
+ }
+ if(!svgp.getCSSClassManager().contains(SDO_ARROW)) {
+ CSSClass cls = new CSSClass(this, SDO_ARROW);
+ cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, SVGConstants.CSS_DARKGREY_VALUE);
+ cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.PLOT) / 3);
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_BLACK_VALUE);
+ svgp.addCSSClassOrLogError(cls);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/AxisVisibilityVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/AxisVisibilityVisualization.java
new file mode 100644
index 00000000..ea68d660
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/AxisVisibilityVisualization.java
@@ -0,0 +1,293 @@
+package de.lmu.ifi.dbs.elki.visualization.visualizers.parallel;
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.Collection;
+
+import org.apache.batik.util.SVGConstants;
+import org.w3c.dom.Element;
+import org.w3c.dom.events.Event;
+import org.w3c.dom.events.EventListener;
+import org.w3c.dom.events.EventTarget;
+
+import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
+import de.lmu.ifi.dbs.elki.result.Result;
+import de.lmu.ifi.dbs.elki.result.ResultUtil;
+import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
+import de.lmu.ifi.dbs.elki.visualization.css.CSSClass;
+import de.lmu.ifi.dbs.elki.visualization.projector.ParallelPlotProjector;
+import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGPath;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
+
+/**
+ * Layer for controlling axis visbility in parallel coordinates.
+ *
+ * @author Robert Rödler
+ * @author Erich Schubert
+ *
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
+ */
+public class AxisVisibilityVisualization extends AbstractVisFactory {
+ /**
+ * A short name characterizing this Visualizer.
+ */
+ private static final String NAME = "Axis Visibility";
+
+ /**
+ * Constructor, adhering to
+ */
+ public AxisVisibilityVisualization() {
+ super();
+ }
+
+ @Override
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
+ }
+
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<ParallelPlotProjector<?>> ps = ResultUtil.filterResults(result, ParallelPlotProjector.class);
+ for(ParallelPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, p, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_INTERACTIVE;
+ task.noexport = true;
+ task.thumbnail = false;
+ baseResult.getHierarchy().add(p, task);
+ }
+ }
+
+ /**
+ * Instance for a particular data set.
+ *
+ * @author Robert Rödler
+ * @author Erich Schubert
+ */
+ public class Instance extends AbstractParallelVisualization<NumberVector<?>> {
+ /**
+ * Generic tags to indicate the type of element. Used in IDs, CSS-Classes
+ * etc.
+ */
+ public static final String SELECTAXISVISIBILITY = "SelectAxisVisibility";
+
+ /**
+ * CSS class for a tool button
+ */
+ public static final String SAV_BUTTON = "SAVbutton";
+
+ /**
+ * CSS class for a button border
+ */
+ public static final String SAV_BORDER = "SAVborder";
+
+ /**
+ * CSS class for a button cross
+ */
+ public static final String SAV_CROSS = "SAVbuttoncross";
+
+ /**
+ * Active area size
+ */
+ double controlsize;
+
+ /**
+ * Button size
+ */
+ double buttonsize;
+
+ /**
+ * Vertical position
+ */
+ double ypos;
+
+ /**
+ * Constructor.
+ *
+ * @param task VisualizationTask
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
+ incrementalRedraw();
+ context.addResultListener(this);
+ }
+
+ @Override
+ protected void redraw() {
+ final int dim = proj.getInputDimensionality();
+ addCSSClasses(svgp);
+ controlsize = 0.025 * getSizeY();
+ buttonsize = 0.75 * controlsize;
+ ypos = getSizeY() + getMarginTop() * .5;
+
+ // Background
+ Element back = svgp.svgRect(-controlsize * .5, ypos - controlsize * .5 + buttonsize * .5, getSizeX() + controlsize, controlsize);
+ SVGUtil.addCSSClass(back, SELECTAXISVISIBILITY);
+ layer.appendChild(back);
+
+ // Previous visible dimension.
+ for(int i = 0, hidden = 0, vax = 0; i <= dim; i++) {
+ if(i < dim && !proj.isAxisVisible(i)) {
+ hidden += 1;
+ continue;
+ }
+ // Add button for showing hidden dimensions:
+ if(hidden > 0) {
+ makeButtonsForHidden(vax, i - hidden, hidden, dim);
+ hidden = 0;
+ }
+ // Add buttons for current dimension
+ if(i < dim) {
+ makeButtonForVisible(i, vax);
+ vax++;
+ }
+ }
+ }
+
+ /**
+ * Make a button for a visible axis
+ *
+ * @param anum Axis number
+ * @param apos Axis position in plot
+ */
+ protected void makeButtonForVisible(int anum, int apos) {
+ final double xpos = getVisibleAxisX(apos) - buttonsize * .5;
+
+ Element border = svgp.svgRect(xpos, ypos, buttonsize, buttonsize);
+ SVGUtil.addCSSClass(border, SAV_BORDER);
+ layer.appendChild(border);
+
+ SVGPath path = new SVGPath();
+ final double qs = controlsize * .5;
+ final double cs = controlsize * .125;
+ path.moveTo(xpos + cs, ypos + cs);
+ path.relativeLineTo(qs, qs);
+ path.relativeMoveTo(0, -qs);
+ path.relativeLineTo(-qs, qs);
+ Element cross = path.makeElement(svgp);
+ SVGUtil.addCSSClass(cross, SAV_CROSS);
+ layer.appendChild(cross);
+
+ Element rect = svgp.svgRect(xpos, ypos, buttonsize, buttonsize);
+ SVGUtil.addCSSClass(rect, SAV_BUTTON);
+ addEventListener(rect, anum);
+ layer.appendChild(rect);
+ }
+
+ /**
+ * Insert buttons for hidden dimensions.
+ *
+ * @param vnum Column number (= next visible axis number)
+ * @param first First invisible axis
+ * @param count Number of invisible axes
+ * @param dim Number of total dimensions
+ */
+ private void makeButtonsForHidden(final int vnum, final int first, final int count, final int dim) {
+ final double lpos, rpos;
+ if(vnum == 0) {
+ lpos = -getMarginLeft();
+ }
+ else {
+ lpos = getVisibleAxisX(vnum - 1);
+ }
+ if(first + count + 1 >= dim) {
+ rpos = getWidth() + getMarginLeft();
+ }
+ else {
+ rpos = getVisibleAxisX(vnum);
+ }
+ final double step = (rpos - lpos) / (count + 1.0);
+ for(int j = 0; j < count; j++) {
+ final double apos = lpos + (j + 1) * step - buttonsize * .5;
+ Element border = svgp.svgRect(apos, ypos, buttonsize, buttonsize);
+ SVGUtil.addCSSClass(border, SAV_BORDER);
+ layer.appendChild(border);
+
+ Element rect = svgp.svgRect(apos, ypos, buttonsize, buttonsize);
+ SVGUtil.addCSSClass(rect, SAV_BUTTON);
+ addEventListener(rect, first + j);
+ layer.appendChild(rect);
+ }
+ }
+
+ /**
+ * Add an event listener to the Element
+ *
+ * @param tag Element to add the listener
+ * @param axis Axis number (including hidden axes)
+ */
+ private void addEventListener(final Element tag, final int axis) {
+ EventTarget targ = (EventTarget) tag;
+ targ.addEventListener(SVGConstants.SVG_EVENT_CLICK, new EventListener() {
+ @Override
+ public void handleEvent(Event evt) {
+ if(proj.getVisibleDimensions() > 2) {
+ proj.toggleAxisVisible(axis);
+ context.getHierarchy().resultChanged(proj);
+ }
+ }
+ }, false);
+ }
+
+ /**
+ * Adds the required CSS-Classes
+ *
+ * @param svgp SVG-Plot
+ */
+ private void addCSSClasses(SVGPlot svgp) {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ if(!svgp.getCSSClassManager().contains(SELECTAXISVISIBILITY)) {
+ CSSClass cls = new CSSClass(this, SELECTAXISVISIBILITY);
+ cls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, 0.1);
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_BLUE_VALUE);
+ svgp.addCSSClassOrLogError(cls);
+ }
+ if(!svgp.getCSSClassManager().contains(SAV_BORDER)) {
+ CSSClass cls = new CSSClass(this, SAV_BORDER);
+ cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, SVGConstants.CSS_GREY_VALUE);
+ cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.PLOT) * .5);
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
+ svgp.addCSSClassOrLogError(cls);
+ }
+ if(!svgp.getCSSClassManager().contains(SAV_BUTTON)) {
+ CSSClass cls = new CSSClass(this, SAV_BUTTON);
+ cls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, 0.01);
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_GREY_VALUE);
+ cls.setStatement(SVGConstants.CSS_CURSOR_PROPERTY, SVGConstants.CSS_POINTER_VALUE);
+ svgp.addCSSClassOrLogError(cls);
+ }
+ if(!svgp.getCSSClassManager().contains(SAV_CROSS)) {
+ CSSClass cls = new CSSClass(this, SAV_CROSS);
+ cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, SVGConstants.CSS_BLACK_VALUE);
+ cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.PLOT) * .75);
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
+ svgp.addCSSClassOrLogError(cls);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/LineVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/LineVisualization.java
index 8269d8fe..e9726ba6 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/LineVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/LineVisualization.java
@@ -53,163 +53,164 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.thumbs.ThumbnailVisualizati
* Generates data lines.
*
* @author Robert Rödler
+ *
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class LineVisualization extends AbstractParallelVisualization<NumberVector<?, ?>> implements DataStoreListener {
- /**
- * Generic tags to indicate the type of element. Used in IDs, CSS-Classes etc.
- */
- public static final String DATALINE = "Dataline";
-
+public class LineVisualization extends AbstractVisFactory {
/**
- * Sample we visualize.
+ * A short name characterizing this Visualizer.
*/
- private SamplingResult sample;
+ public static final String NAME = "Data lines";
/**
- * Constructor.
- *
- * @param task VisualizationTask
+ * Constructor, adhering to
+ * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
*/
- public LineVisualization(VisualizationTask task) {
- super(task);
- this.sample = ResultUtil.getSamplingResult(relation);
- context.addResultListener(this);
- context.addDataStoreListener(this);
- incrementalRedraw();
+ public LineVisualization() {
+ super();
+ thumbmask |= ThumbnailVisualization.ON_DATA | ThumbnailVisualization.ON_STYLE;
}
@Override
- public void destroy() {
- context.removeDataStoreListener(this);
- context.removeResultListener(this);
- super.destroy();
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
}
@Override
- public void resultChanged(Result current) {
- super.resultChanged(current);
- if(current == sample || current == context.getStyleResult()) {
- synchronizedRedraw();
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<ParallelPlotProjector<?>> ps = ResultUtil.filterResults(result, ParallelPlotProjector.class);
+ for(ParallelPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, p.getRelation(), p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_DATA;
+ baseResult.getHierarchy().add(p, task);
}
}
- @Override
- protected void redraw() {
- StylingPolicy sp = context.getStyleResult().getStylingPolicy();
- addCSSClasses(svgp, sp);
+ /**
+ * Instance for a particular data set.
+ *
+ * @author Robert Rödler
+ */
+ public class Instance extends AbstractParallelVisualization<NumberVector<?>> implements DataStoreListener {
+ /**
+ * Generic tags to indicate the type of element. Used in IDs, CSS-Classes
+ * etc.
+ */
+ public static final String DATALINE = "Dataline";
- DBIDIter ids = sample.getSample().iter();
- if(ids == null || !ids.valid()) {
- ids = relation.iterDBIDs();
+ /**
+ * Sample we visualize.
+ */
+ private SamplingResult sample;
+
+ /**
+ * Constructor.
+ *
+ * @param task VisualizationTask
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
+ this.sample = ResultUtil.getSamplingResult(relation);
+ context.addResultListener(this);
+ context.addDataStoreListener(this);
+ incrementalRedraw();
+ }
+
+ @Override
+ public void destroy() {
+ context.removeDataStoreListener(this);
+ context.removeResultListener(this);
+ super.destroy();
+ }
+
+ @Override
+ public void resultChanged(Result current) {
+ super.resultChanged(current);
+ if(current == sample || current == context.getStyleResult()) {
+ synchronizedRedraw();
+ }
}
- if(sp instanceof ClassStylingPolicy) {
- ClassStylingPolicy csp = (ClassStylingPolicy) sp;
- for(int c = csp.getMinStyle(); c < csp.getMaxStyle(); c++) {
- String key = DATALINE + "_" + c;
- for(DBIDIter iter = csp.iterateClass(c); iter.valid(); iter.advance()) {
- if(!sample.getSample().contains(iter)) {
- continue; // TODO: can we test more efficiently than this?
+
+ @Override
+ protected void redraw() {
+ StylingPolicy sp = context.getStyleResult().getStylingPolicy();
+ addCSSClasses(svgp, sp);
+
+ DBIDIter ids = sample.getSample().iter();
+ if(ids == null || !ids.valid()) {
+ ids = relation.iterDBIDs();
+ }
+ if(sp instanceof ClassStylingPolicy) {
+ ClassStylingPolicy csp = (ClassStylingPolicy) sp;
+ for(int c = csp.getMinStyle(); c < csp.getMaxStyle(); c++) {
+ String key = DATALINE + "_" + c;
+ for(DBIDIter iter = csp.iterateClass(c); iter.valid(); iter.advance()) {
+ if(!sample.getSample().contains(iter)) {
+ continue; // TODO: can we test more efficiently than this?
+ }
+ SVGPath path = new SVGPath();
+ double[] yPos = proj.fastProjectDataToRenderSpace(relation.get(iter));
+ for(int i = 0; i < yPos.length; i++) {
+ path.drawTo(getVisibleAxisX(i), yPos[i]);
+ }
+ Element line = path.makeElement(svgp);
+ SVGUtil.addCSSClass(line, key);
+ layer.appendChild(line);
}
+ }
+ }
+ else {
+ for(; ids.valid(); ids.advance()) {
SVGPath path = new SVGPath();
- double[] yPos = proj.fastProjectDataToRenderSpace(relation.get(iter));
+ double[] yPos = proj.fastProjectDataToRenderSpace(relation.get(ids));
for(int i = 0; i < yPos.length; i++) {
path.drawTo(getVisibleAxisX(i), yPos[i]);
}
Element line = path.makeElement(svgp);
- SVGUtil.addCSSClass(line, key);
+ SVGUtil.addCSSClass(line, DATALINE);
+ // assign color
+ line.setAttribute(SVGConstants.SVG_STYLE_ATTRIBUTE, SVGConstants.CSS_STROKE_PROPERTY + ":" + SVGUtil.colorToString(sp.getColorForDBID(ids)));
layer.appendChild(line);
}
}
}
- else {
- for(; ids.valid(); ids.advance()) {
- SVGPath path = new SVGPath();
- double[] yPos = proj.fastProjectDataToRenderSpace(relation.get(ids));
- for(int i = 0; i < yPos.length; i++) {
- path.drawTo(getVisibleAxisX(i), yPos[i]);
+
+ /**
+ * Adds the required CSS-Classes
+ *
+ * @param svgp SVG-Plot
+ */
+ private void addCSSClasses(SVGPlot svgp, StylingPolicy sp) {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ final LineStyleLibrary lines = style.lines();
+ final double width = .5 * style.getLineWidth(StyleLibrary.PLOT);
+ if(sp instanceof ClassStylingPolicy) {
+ ClassStylingPolicy csp = (ClassStylingPolicy) sp;
+ for(int i = csp.getMinStyle(); i < csp.getMaxStyle(); i++) {
+ String key = DATALINE + "_" + i;
+ if(!svgp.getCSSClassManager().contains(key)) {
+ CSSClass cls = new CSSClass(this, key);
+ cls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+ cls.setStatement(SVGConstants.CSS_STROKE_LINEJOIN_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
+ lines.formatCSSClass(cls, i, width);
+ svgp.addCSSClassOrLogError(cls);
+ }
}
- Element line = path.makeElement(svgp);
- SVGUtil.addCSSClass(line, DATALINE);
- // assign color
- line.setAttribute(SVGConstants.SVG_STYLE_ATTRIBUTE, SVGConstants.CSS_STROKE_PROPERTY + ":" + SVGUtil.colorToString(sp.getColorForDBID(ids)));
- layer.appendChild(line);
}
- }
- }
-
- /**
- * Adds the required CSS-Classes
- *
- * @param svgp SVG-Plot
- */
- private void addCSSClasses(SVGPlot svgp, StylingPolicy sp) {
- final StyleLibrary style = context.getStyleLibrary();
- final LineStyleLibrary lines = style.lines();
- final double width = .5 * style.getLineWidth(StyleLibrary.PLOT);
- if(sp instanceof ClassStylingPolicy) {
- ClassStylingPolicy csp = (ClassStylingPolicy) sp;
- for(int i = csp.getMinStyle(); i < csp.getMaxStyle(); i++) {
- String key = DATALINE + "_" + i;
- if(!svgp.getCSSClassManager().contains(key)) {
- CSSClass cls = new CSSClass(this, key);
+ else {
+ // Class for the distance function
+ if(!svgp.getCSSClassManager().contains(DATALINE)) {
+ CSSClass cls = new CSSClass(this, DATALINE);
cls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
cls.setStatement(SVGConstants.CSS_STROKE_LINEJOIN_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
- lines.formatCSSClass(cls, i, width);
+ lines.formatCSSClass(cls, -1, width);
svgp.addCSSClassOrLogError(cls);
}
}
- }
- else {
- // Class for the distance function
- if(!svgp.getCSSClassManager().contains(DATALINE)) {
- CSSClass cls = new CSSClass(this, DATALINE);
- cls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
- cls.setStatement(SVGConstants.CSS_STROKE_LINEJOIN_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
- cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
- lines.formatCSSClass(cls, -1, width);
- svgp.addCSSClassOrLogError(cls);
- }
- }
- svgp.updateStyleElement();
- }
-
- /**
- * Factory for axis visualizations
- *
- * @author Robert Rödler
- *
- * @apiviz.stereotype factory
- * @apiviz.uses LineVisualization oneway - - «create»
- */
- public static class Factory extends AbstractVisFactory {
- /**
- * A short name characterizing this Visualizer.
- */
- private static final String NAME = "Data lines";
-
- /**
- * Constructor, adhering to
- * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
- */
- public Factory() {
- super();
- thumbmask |= ThumbnailVisualization.ON_DATA | ThumbnailVisualization.ON_STYLE;
- }
-
- @Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new LineVisualization(task);
- }
-
- @Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<ParallelPlotProjector<?>> ps = ResultUtil.filterResults(result, ParallelPlotProjector.class);
- for(ParallelPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, p.getRelation(), p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA);
- baseResult.getHierarchy().add(p, task);
- }
+ svgp.updateStyleElement();
}
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/ParallelAxisVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/ParallelAxisVisualization.java
index 6209db6b..00ef60ed 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/ParallelAxisVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/ParallelAxisVisualization.java
@@ -32,10 +32,10 @@ import org.w3c.dom.events.EventListener;
import org.w3c.dom.events.EventTarget;
import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
import de.lmu.ifi.dbs.elki.visualization.css.CSSClass;
import de.lmu.ifi.dbs.elki.visualization.css.CSSClassManager.CSSNamingConflict;
@@ -52,144 +52,150 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
*
* @author Robert Rödler
*
- * @apiviz.uses SVGSimpleLinearAxis
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-// TODO: split into interactive / non-interactive parts?
-public class ParallelAxisVisualization extends AbstractParallelVisualization<NumberVector<?, ?>> {
+public class ParallelAxisVisualization extends AbstractVisFactory {
/**
- * Axis label class
+ * A short name characterizing this Visualizer.
*/
- public final static String AXIS_LABEL = "paxis-label";
-
- /**
- * Clickable area for the axis.
- */
- public final static String INVERTEDAXIS = "paxis-button";
+ private static final String NAME = "Parallel Axes";
/**
* Constructor.
- *
- * @param task VisualizationTask
*/
- public ParallelAxisVisualization(VisualizationTask task) {
- super(task);
- incrementalRedraw();
- context.addResultListener(this);
+ public ParallelAxisVisualization() {
+ super();
}
@Override
- protected void redraw() {
- addCSSClasses(svgp);
- final int dim = proj.getVisibleDimensions();
- try {
- for(int i = 0; i < dim; i++) {
- final int truedim = proj.getDimForVisibleAxis(i);
- final double axisX = getVisibleAxisX(i);
- if(!proj.isAxisInverted(i)) {
- SVGSimpleLinearAxis.drawAxis(svgp, layer, proj.getAxisScale(i), axisX, getSizeY(), axisX, 0, SVGSimpleLinearAxis.LabelStyle.ENDLABEL, context.getStyleLibrary());
- }
- else {
- SVGSimpleLinearAxis.drawAxis(svgp, layer, proj.getAxisScale(i), axisX, 0, axisX, getSizeY(), SVGSimpleLinearAxis.LabelStyle.ENDLABEL, context.getStyleLibrary());
- }
- // Get axis label
- final String label = DatabaseUtil.getColumnLabel(relation, truedim + 1);
- // Add axis label
- Element text = svgp.svgText(axisX, -.7 * getMarginTop(), label);
- SVGUtil.setCSSClass(text, AXIS_LABEL);
- layer.appendChild(text);
- // TODO: Split into background + clickable layer.
- Element button = svgp.svgRect(axisX - getAxisSep() * .475, -getMarginTop(), .95 * getAxisSep(), .5 * getMarginTop());
- SVGUtil.setCSSClass(button, INVERTEDAXIS);
- addEventListener(button, truedim);
- layer.appendChild(button);
- }
- }
- catch(CSSNamingConflict e) {
- throw new RuntimeException("Conflict in CSS naming for axes.", e);
- }
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
}
- /**
- * Add the main CSS classes.
- *
- * @param svgp Plot to draw to
- */
- private void addCSSClasses(SVGPlot svgp) {
- final StyleLibrary style = context.getStyleLibrary();
- if(!svgp.getCSSClassManager().contains(AXIS_LABEL)) {
- CSSClass cls = new CSSClass(this, AXIS_LABEL);
- cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, style.getTextColor(StyleLibrary.AXIS_LABEL));
- cls.setStatement(SVGConstants.CSS_FONT_FAMILY_PROPERTY, style.getFontFamily(StyleLibrary.AXIS_LABEL));
- cls.setStatement(SVGConstants.CSS_FONT_SIZE_PROPERTY, style.getTextSize(StyleLibrary.AXIS_LABEL));
- cls.setStatement(SVGConstants.CSS_TEXT_ANCHOR_PROPERTY, SVGConstants.SVG_MIDDLE_VALUE);
- svgp.addCSSClassOrLogError(cls);
- }
- if(!svgp.getCSSClassManager().contains(INVERTEDAXIS)) {
- CSSClass cls = new CSSClass(this, INVERTEDAXIS);
- cls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, 0.1);
- cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_GREY_VALUE);
- svgp.addCSSClassOrLogError(cls);
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<ParallelPlotProjector<?>> ps = ResultUtil.filterResults(result, ParallelPlotProjector.class);
+ for(ParallelPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, p, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_BACKGROUND;
+ baseResult.getHierarchy().add(p, task);
}
}
- /**
- * Add an event listener to the Element
- *
- * @param tag Element to add the listener
- * @param i Tool number for the Element
- */
- private void addEventListener(final Element tag, final int i) {
- EventTarget targ = (EventTarget) tag;
- targ.addEventListener(SVGConstants.SVG_EVENT_CLICK, new EventListener() {
- @Override
- public void handleEvent(Event evt) {
- proj.toggleDimInverted(i);
- context.getHierarchy().resultChanged(proj);
- }
- }, false);
+ @Override
+ public boolean allowThumbnails(VisualizationTask task) {
+ // Don't use thumbnails
+ return true;
}
/**
- * Factory for axis visualizations
+ * Instance.
*
* @author Robert Rödler
*
- * @apiviz.stereotype factory
- * @apiviz.uses ParallelAxisVisualization oneway - - «create»
+ * @apiviz.uses SVGSimpleLinearAxis
*/
- public static class Factory extends AbstractVisFactory {
+ // TODO: split into interactive / non-interactive parts?
+ public class Instance extends AbstractParallelVisualization<NumberVector<?>> {
+ /**
+ * Axis label class.
+ */
+ public static final String AXIS_LABEL = "paxis-label";
+
/**
- * A short name characterizing this Visualizer.
+ * Clickable area for the axis.
*/
- private static final String NAME = "Parallel Axes";
+ public static final String INVERTEDAXIS = "paxis-button";
/**
- * Constructor, adhering to
- * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
+ * Constructor.
+ *
+ * @param task VisualizationTask
*/
- public Factory() {
- super();
+ public Instance(VisualizationTask task) {
+ super(task);
+ incrementalRedraw();
+ context.addResultListener(this);
}
@Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new ParallelAxisVisualization(task);
+ protected void redraw() {
+ addCSSClasses(svgp);
+ final int dim = proj.getInputDimensionality();
+ try {
+ for(int i = 0, vdim = 0; i < dim; i++) {
+ if(!proj.isAxisVisible(i)) {
+ continue;
+ }
+ final int truedim = proj.getDimForVisibleAxis(vdim);
+ final double axisX = getVisibleAxisX(vdim);
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ if(!proj.isAxisInverted(vdim)) {
+ SVGSimpleLinearAxis.drawAxis(svgp, layer, proj.getAxisScale(i), axisX, getSizeY(), axisX, 0, SVGSimpleLinearAxis.LabelStyle.ENDLABEL, style);
+ }
+ else {
+ SVGSimpleLinearAxis.drawAxis(svgp, layer, proj.getAxisScale(i), axisX, 0, axisX, getSizeY(), SVGSimpleLinearAxis.LabelStyle.ENDLABEL, style);
+ }
+ // Get axis label
+ final String label = RelationUtil.getColumnLabel(relation, truedim);
+ // Add axis label
+ Element text = svgp.svgText(axisX, -.7 * getMarginTop(), label);
+ SVGUtil.setCSSClass(text, AXIS_LABEL);
+ SVGUtil.setAtt(text, SVGConstants.SVG_TEXT_LENGTH_ATTRIBUTE, getAxisSep() * 0.95);
+ SVGUtil.setAtt(text, SVGConstants.SVG_LENGTH_ADJUST_ATTRIBUTE, SVGConstants.SVG_SPACING_AND_GLYPHS_VALUE);
+ layer.appendChild(text);
+ // TODO: Split into background + clickable layer.
+ Element button = svgp.svgRect(axisX - getAxisSep() * .475, -getMarginTop(), .95 * getAxisSep(), .5 * getMarginTop());
+ SVGUtil.setCSSClass(button, INVERTEDAXIS);
+ addEventListener(button, truedim);
+ layer.appendChild(button);
+ vdim++;
+ }
+ }
+ catch(CSSNamingConflict e) {
+ throw new RuntimeException("Conflict in CSS naming for axes.", e);
+ }
}
- @Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<ParallelPlotProjector<?>> ps = ResultUtil.filterResults(result, ParallelPlotProjector.class);
- for(ParallelPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, p, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_BACKGROUND);
- baseResult.getHierarchy().add(p, task);
+ /**
+ * Add the main CSS classes.
+ *
+ * @param svgp Plot to draw to
+ */
+ private void addCSSClasses(SVGPlot svgp) {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ if(!svgp.getCSSClassManager().contains(AXIS_LABEL)) {
+ CSSClass cls = new CSSClass(this, AXIS_LABEL);
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, style.getTextColor(StyleLibrary.AXIS_LABEL));
+ cls.setStatement(SVGConstants.CSS_FONT_FAMILY_PROPERTY, style.getFontFamily(StyleLibrary.AXIS_LABEL));
+ cls.setStatement(SVGConstants.CSS_FONT_SIZE_PROPERTY, style.getTextSize(StyleLibrary.AXIS_LABEL));
+ cls.setStatement(SVGConstants.CSS_TEXT_ANCHOR_PROPERTY, SVGConstants.SVG_MIDDLE_VALUE);
+ svgp.addCSSClassOrLogError(cls);
+ }
+ if(!svgp.getCSSClassManager().contains(INVERTEDAXIS)) {
+ CSSClass cls = new CSSClass(this, INVERTEDAXIS);
+ cls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, 0.1);
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_GREY_VALUE);
+ svgp.addCSSClassOrLogError(cls);
}
}
- @Override
- public boolean allowThumbnails(VisualizationTask task) {
- // Don't use thumbnails
- return true;
+ /**
+ * Add an event listener to the Element.
+ *
+ * @param tag Element to add the listener
+ * @param i Tool number for the Element
+ */
+ private void addEventListener(final Element tag, final int i) {
+ EventTarget targ = (EventTarget) tag;
+ targ.addEventListener(SVGConstants.SVG_EVENT_CLICK, new EventListener() {
+ @Override
+ public void handleEvent(Event evt) {
+ proj.toggleDimInverted(i);
+ context.getHierarchy().resultChanged(proj);
+ }
+ }, false);
}
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/cluster/ClusterOutlineVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/cluster/ClusterOutlineVisualization.java
index f34b7514..47a6d9a8 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/cluster/ClusterOutlineVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/cluster/ClusterOutlineVisualization.java
@@ -34,7 +34,7 @@ import de.lmu.ifi.dbs.elki.data.Clustering;
import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.data.model.Model;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreListener;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.math.DoubleMinMax;
import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
import de.lmu.ifi.dbs.elki.result.Result;
@@ -56,203 +56,206 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.parallel.AbstractParallelVi
* Generates a SVG-Element that visualizes the area covered by a cluster.
*
* @author Robert Rödler
- * @author Erich Schubert
+ *
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class ClusterOutlineVisualization extends AbstractParallelVisualization<NumberVector<?, ?>> implements DataStoreListener {
+// TODO: make parameterizable: rounded.
+public class ClusterOutlineVisualization extends AbstractVisFactory {
/**
- * Generic tags to indicate the type of element. Used in IDs, CSS-Classes etc.
+ * A short name characterizing this Visualizer.
*/
- public static final String CLUSTERAREA = "Clusteroutline";
+ public static final String NAME = "Cluster Outline";
/**
- * The result we visualize
+ * Currently unused option to enable/disable rounding
*/
- private Clustering<Model> clustering;
+ public static final OptionID ROUNDED_ID = new OptionID("parallel.clusteroutline.rounded", "Draw lines rounded");
/**
- * Flag for using rounded shapes
+ * Currently, always enabled.
*/
- boolean rounded = true;
+ private boolean rounded = true;
/**
- * Constructor.
- *
- * @param task VisualizationTask
+ * Constructor, adhering to
+ * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
*/
- public ClusterOutlineVisualization(VisualizationTask task, boolean rounded) {
- super(task);
- this.clustering = task.getResult();
- this.rounded = rounded;
- context.addDataStoreListener(this);
- context.addResultListener(this);
- incrementalRedraw();
+ public ClusterOutlineVisualization() {
+ super();
}
@Override
- public void destroy() {
- context.removeDataStoreListener(this);
- context.removeResultListener(this);
- super.destroy();
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task, rounded);
}
@Override
- protected void redraw() {
- addCSSClasses(svgp);
- int dim = proj.getVisibleDimensions();
-
- DoubleMinMax[] mms = DoubleMinMax.newArray(dim);
- DoubleMinMax[] midmm = DoubleMinMax.newArray(dim - 1);
-
- Iterator<Cluster<Model>> ci = clustering.getAllClusters().iterator();
- for(int cnum = 0; cnum < clustering.getAllClusters().size(); cnum++) {
- Cluster<?> clus = ci.next();
- for(int i = 0; i < dim; i++) {
- mms[i].reset();
- if(i < dim - 1) {
- midmm[i].reset();
- }
- }
-
- // Process points
- // TODO: do this just once, cache the result somewhere appropriately?
- for(DBID objId : clus.getIDs()) {
- double[] yPos = proj.fastProjectDataToRenderSpace(relation.get(objId));
- for(int i = 0; i < dim; i++) {
- mms[i].put(yPos[i]);
- if(i > 0) {
- midmm[i - 1].put((yPos[i] + yPos[i - 1]) / 2.);
- }
- }
- }
-
- SVGPath path = new SVGPath();
- if(!rounded) {
- // Straight lines
- for(int i = 0; i < dim; i++) {
- path.drawTo(getVisibleAxisX(i), mms[i].getMax());
- if(i < dim - 1) {
- path.drawTo(getVisibleAxisX(i + .5), midmm[i].getMax());
- }
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ // Find clusterings we can visualize:
+ Collection<Clustering<?>> clusterings = ResultUtil.filterResults(result, Clustering.class);
+ for(Clustering<?> c : clusterings) {
+ if(c.getAllClusters().size() > 0) {
+ Collection<ParallelPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ParallelPlotProjector.class);
+ for(ParallelPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, c, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_DATA - 1;
+ task.initDefaultVisibility(false);
+ baseResult.getHierarchy().add(c, task);
+ baseResult.getHierarchy().add(p, task);
}
- for(int i = dim - 1; i >= 0; i--) {
- if(i < dim - 1) {
- path.drawTo(getVisibleAxisX(i + .5), midmm[i].getMin());
- }
- path.drawTo(getVisibleAxisX(i), mms[i].getMin());
- }
- path.close();
- }
- else {
- // Maxima
- path.drawTo(getVisibleAxisX(0), mms[0].getMax());
- for(int i = 1; i < dim; i++) {
- path.quadTo(getVisibleAxisX(i - .5), midmm[i - 1].getMax(), getVisibleAxisX(i), mms[i].getMax());
- }
- // Minima
- path.drawTo(getVisibleAxisX(dim - 1), mms[dim - 1].getMin());
- for(int i = dim - 1; i > 0; i--) {
- path.quadTo(getVisibleAxisX(i - .5), midmm[i - 1].getMin(), getVisibleAxisX(i - 1), mms[i - 1].getMin());
- }
- path.close();
}
-
- Element intervals = path.makeElement(svgp);
- SVGUtil.addCSSClass(intervals, CLUSTERAREA + cnum);
- layer.appendChild(intervals);
}
}
- /**
- * Adds the required CSS-Classes
- *
- * @param svgp SVG-Plot
- */
- private void addCSSClasses(SVGPlot svgp) {
- if(!svgp.getCSSClassManager().contains(CLUSTERAREA)) {
- ColorLibrary colors = context.getStyleLibrary().getColorSet(StyleLibrary.PLOT);
- int clusterID = 0;
-
- for(@SuppressWarnings("unused")
- Cluster<?> cluster : clustering.getAllClusters()) {
- CSSClass cls = new CSSClass(this, CLUSTERAREA + clusterID);
- // cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY,
- // context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT) / 2.0);
- cls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, 0.5);
- final String color;
- if(clustering.getAllClusters().size() == 1) {
- color = SVGConstants.CSS_BLACK_VALUE;
- }
- else {
- color = colors.getColor(clusterID);
- }
- // cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, color);
- cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, color);
-
- svgp.addCSSClassOrLogError(cls);
- clusterID++;
- }
- }
+ @Override
+ public boolean allowThumbnails(VisualizationTask task) {
+ // Don't use thumbnails
+ return false;
}
/**
- * Factory for axis visualizations
+ * Instance
*
* @author Robert Rödler
- *
- * @apiviz.stereotype factory
- * @apiviz.uses ClusterOutlineVisualization oneway - - «create»
+ * @author Erich Schubert
*/
- public static class Factory extends AbstractVisFactory {
+ public class Instance extends AbstractParallelVisualization<NumberVector<?>> implements DataStoreListener {
/**
- * A short name characterizing this Visualizer.
+ * Generic tags to indicate the type of element. Used in IDs, CSS-Classes
+ * etc.
*/
- private static final String NAME = "Cluster Outline";
+ public static final String CLUSTERAREA = "Clusteroutline";
/**
- * Currently unused option to enable/disable rounding
+ * The result we visualize
*/
- public static final OptionID ROUNDED_ID = OptionID.getOrCreateOptionID("parallel.clusteroutline.rounded", "Draw lines rounded");
+ private Clustering<Model> clustering;
/**
- * Currently, always enabled.
+ * Flag for using rounded shapes
*/
- private boolean rounded = true;
+ boolean rounded = true;
/**
- * Constructor, adhering to
- * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
+ * Constructor.
+ *
+ * @param task VisualizationTask
*/
- public Factory() {
- super();
+ public Instance(VisualizationTask task, boolean rounded) {
+ super(task);
+ this.clustering = task.getResult();
+ this.rounded = rounded;
+ context.addDataStoreListener(this);
+ context.addResultListener(this);
+ incrementalRedraw();
}
@Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new ClusterOutlineVisualization(task, rounded);
+ public void destroy() {
+ context.removeDataStoreListener(this);
+ context.removeResultListener(this);
+ super.destroy();
}
@Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- // Find clusterings we can visualize:
- Collection<Clustering<?>> clusterings = ResultUtil.filterResults(result, Clustering.class);
- for(Clustering<?> c : clusterings) {
- if(c.getAllClusters().size() > 0) {
- Collection<ParallelPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ParallelPlotProjector.class);
- for(ParallelPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, c, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA - 1);
- task.put(VisualizationTask.META_VISIBLE_DEFAULT, false);
- baseResult.getHierarchy().add(c, task);
- baseResult.getHierarchy().add(p, task);
+ protected void redraw() {
+ addCSSClasses(svgp);
+ int dim = proj.getVisibleDimensions();
+
+ DoubleMinMax[] mms = DoubleMinMax.newArray(dim);
+ DoubleMinMax[] midmm = DoubleMinMax.newArray(dim - 1);
+
+ Iterator<Cluster<Model>> ci = clustering.getAllClusters().iterator();
+ for(int cnum = 0; cnum < clustering.getAllClusters().size(); cnum++) {
+ Cluster<?> clus = ci.next();
+ for(int i = 0; i < dim; i++) {
+ mms[i].reset();
+ if(i < dim - 1) {
+ midmm[i].reset();
+ }
+ }
+
+ // Process points
+ // TODO: do this just once, cache the result somewhere appropriately?
+ for(DBIDIter id = clus.getIDs().iter(); id.valid(); id.advance()) {
+ double[] yPos = proj.fastProjectDataToRenderSpace(relation.get(id));
+ for(int i = 0; i < dim; i++) {
+ mms[i].put(yPos[i]);
+ if(i > 0) {
+ midmm[i - 1].put((yPos[i] + yPos[i - 1]) / 2.);
+ }
+ }
+ }
+
+ SVGPath path = new SVGPath();
+ if(!rounded) {
+ // Straight lines
+ for(int i = 0; i < dim; i++) {
+ path.drawTo(getVisibleAxisX(i), mms[i].getMax());
+ if(i < dim - 1) {
+ path.drawTo(getVisibleAxisX(i + .5), midmm[i].getMax());
+ }
+ }
+ for(int i = dim - 1; i >= 0; i--) {
+ if(i < dim - 1) {
+ path.drawTo(getVisibleAxisX(i + .5), midmm[i].getMin());
+ }
+ path.drawTo(getVisibleAxisX(i), mms[i].getMin());
}
+ path.close();
}
+ else {
+ // Maxima
+ path.drawTo(getVisibleAxisX(0), mms[0].getMax());
+ for(int i = 1; i < dim; i++) {
+ path.quadTo(getVisibleAxisX(i - .5), midmm[i - 1].getMax(), getVisibleAxisX(i), mms[i].getMax());
+ }
+ // Minima
+ path.drawTo(getVisibleAxisX(dim - 1), mms[dim - 1].getMin());
+ for(int i = dim - 1; i > 0; i--) {
+ path.quadTo(getVisibleAxisX(i - .5), midmm[i - 1].getMin(), getVisibleAxisX(i - 1), mms[i - 1].getMin());
+ }
+ path.close();
+ }
+
+ Element intervals = path.makeElement(svgp);
+ SVGUtil.addCSSClass(intervals, CLUSTERAREA + cnum);
+ layer.appendChild(intervals);
}
}
- @Override
- public boolean allowThumbnails(VisualizationTask task) {
- // Don't use thumbnails
- return false;
+ /**
+ * Adds the required CSS-Classes
+ *
+ * @param svgp SVG-Plot
+ */
+ private void addCSSClasses(SVGPlot svgp) {
+ if(!svgp.getCSSClassManager().contains(CLUSTERAREA)) {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ ColorLibrary colors = style.getColorSet(StyleLibrary.PLOT);
+ int clusterID = 0;
+
+ for(@SuppressWarnings("unused")
+ Cluster<?> cluster : clustering.getAllClusters()) {
+ CSSClass cls = new CSSClass(this, CLUSTERAREA + clusterID);
+ // cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY,
+ // context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT) / 2.0);
+ cls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, 0.5);
+ final String color;
+ if(clustering.getAllClusters().size() == 1) {
+ color = SVGConstants.CSS_BLACK_VALUE;
+ }
+ else {
+ color = colors.getColor(clusterID);
+ }
+ // cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, color);
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, color);
+
+ svgp.addCSSClassOrLogError(cls);
+ clusterID++;
+ }
+ }
}
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/cluster/ClusterParallelMeanVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/cluster/ClusterParallelMeanVisualization.java
index 1808c241..ad3ed503 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/cluster/ClusterParallelMeanVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/cluster/ClusterParallelMeanVisualization.java
@@ -53,162 +53,166 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.parallel.AbstractParallelVi
* Generates a SVG-Element that visualizes cluster means.
*
* @author Robert Rödler
+ *
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class ClusterParallelMeanVisualization extends AbstractParallelVisualization<NumberVector<?, ?>> implements DataStoreListener {
+public class ClusterParallelMeanVisualization extends AbstractVisFactory {
/**
- * Generic tags to indicate the type of element. Used in IDs, CSS-Classes etc.
+ * A short name characterizing this Visualizer.
*/
- public static final String CLUSTERMEAN = "Clustermean";
+ private static final String NAME = "Cluster Means";
/**
- * The result we visualize
- */
- private Clustering<MeanModel<? extends NumberVector<?, ?>>> clustering;
-
- /**
- * Constructor.
- *
- * @param task VisualizationTask
+ * Constructor, adhering to
+ * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}.
*/
- public ClusterParallelMeanVisualization(VisualizationTask task) {
- super(task);
- this.clustering = task.getResult();
- context.addDataStoreListener(this);
- context.addResultListener(this);
- incrementalRedraw();
+ public ClusterParallelMeanVisualization() {
+ super();
}
@Override
- public void destroy() {
- context.removeDataStoreListener(this);
- context.removeResultListener(this);
- super.destroy();
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
}
@Override
- protected void redraw() {
- addCSSClasses(svgp);
-
- Iterator<Cluster<MeanModel<? extends NumberVector<?, ?>>>> ci = clustering.getAllClusters().iterator();
- for(int cnum = 0; cnum < clustering.getAllClusters().size(); cnum++) {
- Cluster<MeanModel<? extends NumberVector<?, ?>>> clus = ci.next();
- NumberVector<?, ?> mean = clus.getModel().getMean();
- if(mean == null) {
- continue;
- }
-
- double[] pmean = proj.fastProjectDataToRenderSpace(mean);
-
- SVGPath path = new SVGPath();
- for(int i = 0; i < pmean.length; i++) {
- path.drawTo(getVisibleAxisX(i), pmean[i]);
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ // Find clusterings we can visualize:
+ Collection<Clustering<?>> clusterings = ResultUtil.filterResults(result, Clustering.class);
+ for (Clustering<?> c : clusterings) {
+ if (c.getAllClusters().size() > 0) {
+ // Does the cluster have a model with cluster means?
+ Clustering<MeanModel<? extends NumberVector<?>>> mcls = findMeanModel(c);
+ if (mcls != null) {
+ Collection<ParallelPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ParallelPlotProjector.class);
+ for (ParallelPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, c, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_DATA + 1;
+ baseResult.getHierarchy().add(c, task);
+ baseResult.getHierarchy().add(p, task);
+ }
+ }
}
-
- Element meanline = path.makeElement(svgp);
- SVGUtil.addCSSClass(meanline, CLUSTERMEAN + cnum);
- layer.appendChild(meanline);
}
}
/**
- * Adds the required CSS-Classes
+ * Test if the given clustering has a mean model.
*
- * @param svgp SVG-Plot
+ * @param c Clustering to inspect
+ * @return the clustering cast to return a mean model, null otherwise.
*/
- private void addCSSClasses(SVGPlot svgp) {
- if(!svgp.getCSSClassManager().contains(CLUSTERMEAN)) {
- ColorLibrary colors = context.getStyleLibrary().getColorSet(StyleLibrary.PLOT);
- int clusterID = 0;
-
- for(@SuppressWarnings("unused")
- Cluster<?> cluster : clustering.getAllClusters()) {
- CSSClass cls = new CSSClass(this, CLUSTERMEAN + clusterID);
- cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT) * 2);
-
- final String color;
- if(clustering.getAllClusters().size() == 1) {
- color = SVGConstants.CSS_BLACK_VALUE;
- }
- else {
- color = colors.getColor(clusterID);
- }
-
- cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, color);
- cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
-
- svgp.addCSSClassOrLogError(cls);
- clusterID++;
- }
+ @SuppressWarnings("unchecked")
+ private static Clustering<MeanModel<? extends NumberVector<?>>> findMeanModel(Clustering<?> c) {
+ if (c.getAllClusters().get(0).getModel() instanceof MeanModel<?>) {
+ return (Clustering<MeanModel<? extends NumberVector<?>>>) c;
}
+ return null;
+ }
+
+ @Override
+ public boolean allowThumbnails(VisualizationTask task) {
+ // Don't use thumbnails
+ return false;
}
/**
- * Factory for axis visualizations
+ * Instance.
*
* @author Robert Rödler
*
- * @apiviz.stereotype factory
- * @apiviz.uses ClusterParallelMeanVisualization oneway - - «create»
- *
*/
- public static class Factory extends AbstractVisFactory {
+ public class Instance extends AbstractParallelVisualization<NumberVector<?>> implements DataStoreListener {
/**
- * A short name characterizing this Visualizer.
+ * Generic tags to indicate the type of element. Used in IDs, CSS-Classes
+ * etc.
*/
- private static final String NAME = "Cluster Means";
+ public static final String CLUSTERMEAN = "Clustermean";
/**
- * Constructor, adhering to
- * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
+ * The result we visualize.
+ */
+ private Clustering<MeanModel<? extends NumberVector<?>>> clustering;
+
+ /**
+ * Constructor.
+ *
+ * @param task VisualizationTask
*/
- public Factory() {
- super();
+ public Instance(VisualizationTask task) {
+ super(task);
+ this.clustering = task.getResult();
+ context.addDataStoreListener(this);
+ context.addResultListener(this);
+ incrementalRedraw();
}
@Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new ClusterParallelMeanVisualization(task);
+ public void destroy() {
+ context.removeDataStoreListener(this);
+ context.removeResultListener(this);
+ super.destroy();
}
@Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- // Find clusterings we can visualize:
- Collection<Clustering<?>> clusterings = ResultUtil.filterResults(result, Clustering.class);
- for(Clustering<?> c : clusterings) {
- if(c.getAllClusters().size() > 0) {
- // Does the cluster have a model with cluster means?
- Clustering<MeanModel<? extends NumberVector<?, ?>>> mcls = findMeanModel(c);
- if(mcls != null) {
- Collection<ParallelPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ParallelPlotProjector.class);
- for (ParallelPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, c, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA + 1);
- baseResult.getHierarchy().add(c, task);
- baseResult.getHierarchy().add(p, task);
- }
- }
+ protected void redraw() {
+ addCSSClasses(svgp);
+
+ Iterator<Cluster<MeanModel<? extends NumberVector<?>>>> ci = clustering.getAllClusters().iterator();
+ for (int cnum = 0; cnum < clustering.getAllClusters().size(); cnum++) {
+ Cluster<MeanModel<? extends NumberVector<?>>> clus = ci.next();
+ if (clus.getModel() == null) {
+ continue;
+ }
+ NumberVector<?> mean = clus.getModel().getMean();
+ if (mean == null) {
+ continue;
+ }
+
+ double[] pmean = proj.fastProjectDataToRenderSpace(mean);
+
+ SVGPath path = new SVGPath();
+ for (int i = 0; i < pmean.length; i++) {
+ path.drawTo(getVisibleAxisX(i), pmean[i]);
}
+
+ Element meanline = path.makeElement(svgp);
+ SVGUtil.addCSSClass(meanline, CLUSTERMEAN + cnum);
+ layer.appendChild(meanline);
}
}
/**
- * Test if the given clustering has a mean model.
+ * Adds the required CSS-Classes.
*
- * @param c Clustering to inspect
- * @return the clustering cast to return a mean model, null otherwise.
+ * @param svgp SVG-Plot
*/
- @SuppressWarnings("unchecked")
- private static Clustering<MeanModel<? extends NumberVector<?, ?>>> findMeanModel(Clustering<?> c) {
- if(c.getAllClusters().get(0).getModel() instanceof MeanModel<?>) {
- return (Clustering<MeanModel<? extends NumberVector<?, ?>>>) c;
- }
- return null;
- }
+ private void addCSSClasses(SVGPlot svgp) {
+ if (!svgp.getCSSClassManager().contains(CLUSTERMEAN)) {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ ColorLibrary colors = style.getColorSet(StyleLibrary.PLOT);
+ int clusterID = 0;
+
+ for (@SuppressWarnings("unused")
+ Cluster<?> cluster : clustering.getAllClusters()) {
+ CSSClass cls = new CSSClass(this, CLUSTERMEAN + clusterID);
+ cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.PLOT) * 2.);
+
+ final String color;
+ if (clustering.getAllClusters().size() == 1) {
+ color = SVGConstants.CSS_BLACK_VALUE;
+ } else {
+ color = colors.getColor(clusterID);
+ }
- @Override
- public boolean allowThumbnails(VisualizationTask task) {
- // Don't use thumbnails
- return false;
+ cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, color);
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
+
+ svgp.addCSSClassOrLogError(cls);
+ clusterID++;
+ }
+ }
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/index/RTreeParallelVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/index/RTreeParallelVisualization.java
index 499d14a5..7c2c8b9f 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/index/RTreeParallelVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/index/RTreeParallelVisualization.java
@@ -60,13 +60,10 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.index.TreeMBRVi
*
* @author Robert Rödler
*
- * @apiviz.has AbstractRStarTree oneway - - visualizes
- *
- * @param <N> Tree node type
- * @param <E> Tree entry type
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-// TODO: listen for tree changes instead of data changes?
-public class RTreeParallelVisualization<N extends AbstractRStarTreeNode<N, E>, E extends SpatialEntry> extends AbstractParallelVisualization<NumberVector<?, ?>> implements DataStoreListener {
+public class RTreeParallelVisualization extends AbstractVisFactory {
/**
* Generic tag to indicate the type of element. Used in IDs, CSS-Classes etc.
*/
@@ -78,190 +75,187 @@ public class RTreeParallelVisualization<N extends AbstractRStarTreeNode<N, E>, E
public static final String NAME = "R-Tree Index MBRs";
/**
- * Fill parameter.
- */
- protected boolean fill = true;
-
- /**
- * The tree we visualize
+ * Settings
*/
- protected AbstractRStarTree<N, E> tree;
+ protected Parameterizer settings;
/**
* Constructor.
*
- * @param task Visualization task
- * @param fill Fill flag
+ * @param settings Settings
*/
- @SuppressWarnings("unchecked")
- public RTreeParallelVisualization(VisualizationTask task, boolean fill) {
- super(task);
- this.tree = AbstractRStarTree.class.cast(task.getResult());
- this.fill = fill;
- context.addDataStoreListener(this);
- context.addResultListener(this);
- incrementalRedraw();
+ public RTreeParallelVisualization(Parameterizer settings) {
+ super();
+ this.settings = settings;
}
@Override
- public void destroy() {
- context.removeDataStoreListener(this);
- context.removeResultListener(this);
- super.destroy();
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance<RStarTreeNode, SpatialEntry>(task);
}
@Override
- protected void redraw() {
- if(tree != null) {
- addCSSClasses(svgp);
- E root = tree.getRootEntry();
- visualizeRTreeEntry(svgp, layer, proj, tree, root, 0, 0);
- }
- }
-
- /**
- * Adds the required CSS-Classes
- *
- * @param svgp SVG-Plot
- */
- private void addCSSClasses(SVGPlot svgp) {
- ColorLibrary colors = context.getStyleLibrary().getColorSet(StyleLibrary.PLOT);
-
- for(int i = 0; i < tree.getHeight(); i++) {
-
- if(!svgp.getCSSClassManager().contains(INDEX + i)) {
- CSSClass cls = new CSSClass(this, INDEX + i);
-
- // Relative depth of this level. 1.0 = toplevel
- final double relDepth = 1. - (((double) i) / tree.getHeight());
- if(fill) {
- cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, colors.getColor(i));
- cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, relDepth * context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT));
- cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, colors.getColor(i));
- cls.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, 0.2);
- }
- else {
- cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, colors.getColor(i));
- cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, relDepth * context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT));
- cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ ArrayList<AbstractRStarTree<RStarTreeNode, SpatialEntry>> trees = ResultUtil.filterResults(result, AbstractRStarTree.class);
+ for(AbstractRStarTree<RStarTreeNode, SpatialEntry> tree : trees) {
+ if(tree instanceof Result) {
+ Collection<ParallelPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ParallelPlotProjector.class);
+ for(ParallelPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, (Result) tree, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_BACKGROUND + 2;
+ baseResult.getHierarchy().add((Result) tree, task);
+ baseResult.getHierarchy().add(p, task);
}
- cls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
- cls.setStatement(SVGConstants.CSS_STROKE_LINEJOIN_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
- svgp.addCSSClassOrLogError(cls);
}
}
- svgp.updateStyleElement();
}
/**
- * Recursively draw the MBR rectangles.
- *
- * @param svgp SVG Plot
- * @param layer Layer
- * @param proj Projection
- * @param rtree Rtree to visualize
- * @param entry Current entry
- * @param depth Current depth
- */
- private void visualizeRTreeEntry(SVGPlot svgp, Element layer, ProjectionParallel proj, AbstractRStarTree<? extends N, E> rtree, E entry, int depth, int step) {
- final int dim = proj.getVisibleDimensions();
- double[] min = proj.fastProjectDataToRenderSpace(SpatialUtil.getMin(entry));
- double[] max = proj.fastProjectDataToRenderSpace(SpatialUtil.getMax(entry));
- assert (min.length == dim && max.length == dim);
- SVGPath path = new SVGPath();
- for(int i = 0; i < dim; i++) {
- path.drawTo(getVisibleAxisX(i), Math.max(min[i], max[i]));
- }
- for(int i = dim - 1; i >= 0; i--) {
- path.drawTo(getVisibleAxisX(i), Math.min(min[i], max[i]));
- }
- path.close();
-
- Element intervals = path.makeElement(svgp);
-
- SVGUtil.addCSSClass(intervals, INDEX + depth);
- layer.appendChild(intervals);
-
- if(!entry.isLeafEntry()) {
- N node = rtree.getNode(entry);
- for(int i = 0; i < node.getNumEntries(); i++) {
- E child = node.getEntry(i);
- if(!child.isLeafEntry()) {
- visualizeRTreeEntry(svgp, layer, proj, rtree, child, depth + 1, ++step);
- }
- }
- }
- }
-
- /**
- * Factory
+ * Instance for a particular data set and tree
*
* @author Robert Rödler
*
- * @apiviz.stereotype factory
- * @apiviz.uses RTreeParallelVisualization oneway - - «create»
+ * @apiviz.has AbstractRStarTree oneway - - visualizes
+ *
+ * @param <N> Tree node type
+ * @param <E> Tree entry type
*/
- public static class Factory extends AbstractVisFactory {
+ // TODO: listen for tree changes instead of data changes?
+ public class Instance<N extends AbstractRStarTreeNode<N, E>, E extends SpatialEntry> extends AbstractParallelVisualization<NumberVector<?>> implements DataStoreListener {
/**
- * Fill parameter.
+ * The tree we visualize
*/
- protected boolean fill = true;
+ protected AbstractRStarTree<N, E> tree;
/**
* Constructor.
*
- * @param fill
+ * @param task Visualization task
*/
- public Factory(boolean fill) {
- super();
- this.fill = fill;
+ @SuppressWarnings("unchecked")
+ public Instance(VisualizationTask task) {
+ super(task);
+ this.tree = AbstractRStarTree.class.cast(task.getResult());
+ context.addDataStoreListener(this);
+ context.addResultListener(this);
+ incrementalRedraw();
}
@Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new RTreeParallelVisualization<RStarTreeNode, SpatialEntry>(task, fill);
+ public void destroy() {
+ context.removeDataStoreListener(this);
+ context.removeResultListener(this);
+ super.destroy();
}
@Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- ArrayList<AbstractRStarTree<RStarTreeNode, SpatialEntry>> trees = ResultUtil.filterResults(result, AbstractRStarTree.class);
- for(AbstractRStarTree<RStarTreeNode, SpatialEntry> tree : trees) {
- if(tree instanceof Result) {
- Collection<ParallelPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ParallelPlotProjector.class);
- for(ParallelPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, (Result) tree, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_BACKGROUND + 2);
- baseResult.getHierarchy().add((Result) tree, task);
- baseResult.getHierarchy().add(p, task);
+ protected void redraw() {
+ if(tree != null) {
+ addCSSClasses(svgp);
+ E root = tree.getRootEntry();
+ visualizeRTreeEntry(svgp, layer, proj, tree, root, 0, 0);
+ }
+ }
+
+ /**
+ * Adds the required CSS-Classes
+ *
+ * @param svgp SVG-Plot
+ */
+ private void addCSSClasses(SVGPlot svgp) {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ final ColorLibrary colors = style.getColorSet(StyleLibrary.PLOT);
+
+ for(int i = 0; i < tree.getHeight(); i++) {
+ if(!svgp.getCSSClassManager().contains(INDEX + i)) {
+ CSSClass cls = new CSSClass(this, INDEX + i);
+
+ // Relative depth of this level. 1.0 = toplevel
+ final double relDepth = 1. - (((double) i) / tree.getHeight());
+ if(settings.fill) {
+ cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, colors.getColor(i));
+ cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, relDepth * style.getLineWidth(StyleLibrary.PLOT));
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, colors.getColor(i));
+ cls.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, 0.2);
+ }
+ else {
+ cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, colors.getColor(i));
+ cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, relDepth * style.getLineWidth(StyleLibrary.PLOT));
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
}
+ cls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+ cls.setStatement(SVGConstants.CSS_STROKE_LINEJOIN_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+ svgp.addCSSClassOrLogError(cls);
}
}
+ svgp.updateStyleElement();
}
/**
- * Parameterization class.
+ * Recursively draw the MBR rectangles.
*
- * @author Erich Schubert
- *
- * @apiviz.exclude
+ * @param svgp SVG Plot
+ * @param layer Layer
+ * @param proj Projection
+ * @param rtree Rtree to visualize
+ * @param entry Current entry
+ * @param depth Current depth
*/
- public static class Parameterizer extends AbstractParameterizer {
- protected boolean fill = true;
+ private void visualizeRTreeEntry(SVGPlot svgp, Element layer, ProjectionParallel proj, AbstractRStarTree<? extends N, E> rtree, E entry, int depth, int step) {
+ final int dim = proj.getVisibleDimensions();
+ double[] min = proj.fastProjectDataToRenderSpace(SpatialUtil.getMin(entry));
+ double[] max = proj.fastProjectDataToRenderSpace(SpatialUtil.getMax(entry));
+ assert (min.length == dim && max.length == dim);
+ SVGPath path = new SVGPath();
+ for(int i = 0; i < dim; i++) {
+ path.drawTo(getVisibleAxisX(i), Math.max(min[i], max[i]));
+ }
+ for(int i = dim - 1; i >= 0; i--) {
+ path.drawTo(getVisibleAxisX(i), Math.min(min[i], max[i]));
+ }
+ path.close();
+
+ Element intervals = path.makeElement(svgp);
+
+ SVGUtil.addCSSClass(intervals, INDEX + depth);
+ layer.appendChild(intervals);
- @Override
- protected void makeOptions(Parameterization config) {
- super.makeOptions(config);
- Flag fillF = new Flag(TreeMBRVisualization.Factory.FILL_ID);
- fillF.setDefaultValue(true);
- if(config.grab(fillF)) {
- fill = fillF.getValue();
+ if(!entry.isLeafEntry()) {
+ N node = rtree.getNode(entry);
+ for(int i = 0; i < node.getNumEntries(); i++) {
+ E child = node.getEntry(i);
+ if(!child.isLeafEntry()) {
+ visualizeRTreeEntry(svgp, layer, proj, rtree, child, depth + 1, ++step);
+ }
}
}
+ }
+
+ }
+
+ /**
+ * Parameterization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public static class Parameterizer extends AbstractParameterizer {
+ protected boolean fill = true;
- @Override
- protected Factory makeInstance() {
- return new Factory(fill);
+ @Override
+ protected void makeOptions(Parameterization config) {
+ super.makeOptions(config);
+ Flag fillF = new Flag(TreeMBRVisualization.Parameterizer.FILL_ID);
+ fillF.setDefaultValue(Boolean.TRUE);
+ if(config.grab(fillF)) {
+ fill = fillF.isTrue();
}
}
+
+ @Override
+ protected RTreeParallelVisualization makeInstance() {
+ return new RTreeParallelVisualization(this);
+ }
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionAxisRangeVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionAxisRangeVisualization.java
index b894a87b..6e2a9329 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionAxisRangeVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionAxisRangeVisualization.java
@@ -48,134 +48,126 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.parallel.AbstractParallelVi
import de.lmu.ifi.dbs.elki.visualization.visualizers.thumbs.ThumbnailVisualization;
/**
- * Visualizer for generating an SVG-Element representing the selected range for
- * each dimension
+ * Visualizer for generating an SVG-Element representing the selected range.
*
* @author Robert Rödler
*
- * @apiviz.has SelectionResult oneway - - visualizes
- * @apiviz.has RangeSelection oneway - - visualizes
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class SelectionAxisRangeVisualization extends AbstractParallelVisualization<NumberVector<?, ?>> {
+public class SelectionAxisRangeVisualization extends AbstractVisFactory {
/**
* A short name characterizing this Visualizer.
*/
- private static final String NAME = "Selection Axis Range";
-
- /**
- * CSS Class for the range marker
- */
- public static final String MARKER = "selectionAxisRange";
+ public static final String NAME = "Selection Axis Range";
/**
* Constructor.
- *
- * @param task Visualization task
*/
- public SelectionAxisRangeVisualization(VisualizationTask task) {
- super(task);
- addCSSClasses(svgp);
- context.addResultListener(this);
- incrementalRedraw();
+ public SelectionAxisRangeVisualization() {
+ super();
+ thumbmask |= ThumbnailVisualization.ON_SELECTION;
}
@Override
- public void destroy() {
- context.removeResultListener(this);
- super.destroy();
- }
-
- /**
- * Adds the required CSS-Classes
- *
- * @param svgp SVG-Plot
- */
- private void addCSSClasses(SVGPlot svgp) {
- final StyleLibrary style = context.getStyleLibrary();
- // Class for the cube
- if(!svgp.getCSSClassManager().contains(MARKER)) {
- CSSClass cls = new CSSClass(this, MARKER);
- cls.setStatement(SVGConstants.CSS_STROKE_VALUE, style.getColor(StyleLibrary.SELECTION));
- cls.setStatement(SVGConstants.CSS_STROKE_OPACITY_PROPERTY, style.getOpacity(StyleLibrary.SELECTION));
- cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.PLOT));
- cls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
- cls.setStatement(SVGConstants.CSS_STROKE_LINEJOIN_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
-
- cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, style.getColor(StyleLibrary.SELECTION));
- cls.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, style.getOpacity(StyleLibrary.SELECTION));
-
- svgp.addCSSClassOrLogError(cls);
- }
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
}
@Override
- protected void redraw() {
- DBIDSelection selContext = context.getSelection();
- if(selContext == null || !(selContext instanceof RangeSelection)) {
- return;
- }
- DoubleDoublePair[] ranges = ((RangeSelection) selContext).getRanges();
- if(ranges == null) {
- return;
- }
-
- // Project:
- double[] min = new double[ranges.length];
- double[] max = new double[ranges.length];
- for(int d = 0; d < ranges.length; d++) {
- if(ranges[d] != null) {
- min[d] = ranges[d].first;
- max[d] = ranges[d].second;
- }
- }
- min = proj.fastProjectDataToRenderSpace(min);
- max = proj.fastProjectDataToRenderSpace(max);
-
- int dim = proj.getVisibleDimensions();
- for(int d = 0; d < dim; d++) {
- if(ranges[proj.getDimForVisibleAxis(d)] != null) {
- double amin = Math.min(min[d], max[d]);
- double amax = Math.max(min[d], max[d]);
- Element rect = svgp.svgRect(getVisibleAxisX(d) - (0.01 * StyleLibrary.SCALE), amin, 0.02 * StyleLibrary.SCALE, amax - amin);
- SVGUtil.addCSSClass(rect, MARKER);
- layer.appendChild(rect);
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<SelectionResult> selectionResults = ResultUtil.filterResults(result, SelectionResult.class);
+ for(SelectionResult selres : selectionResults) {
+ Collection<ParallelPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ParallelPlotProjector.class);
+ for(ParallelPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, selres, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_DATA - 1;
+ baseResult.getHierarchy().add(selres, task);
+ baseResult.getHierarchy().add(p, task);
}
}
}
/**
- * Factory for visualizers to generate an SVG-Element containing a cube as
- * marker representing the selected range for each dimension
+ * Instance
*
* @author Robert Rödler
*
- * @apiviz.stereotype factory
- * @apiviz.uses SelectionAxisRangeVisualization oneway - - «create»
+ * @apiviz.has SelectionResult oneway - - visualizes
+ * @apiviz.has RangeSelection oneway - - visualizes
*/
- public static class Factory extends AbstractVisFactory {
+ public class Instance extends AbstractParallelVisualization<NumberVector<?>> {
+ /**
+ * CSS Class for the range marker
+ */
+ public static final String MARKER = "selectionAxisRange";
+
/**
* Constructor.
+ *
+ * @param task Visualization task
*/
- public Factory() {
- super();
- thumbmask |= ThumbnailVisualization.ON_SELECTION;
+ public Instance(VisualizationTask task) {
+ super(task);
+ addCSSClasses(svgp);
+ context.addResultListener(this);
+ incrementalRedraw();
}
- @Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new SelectionAxisRangeVisualization(task);
+ /**
+ * Adds the required CSS-Classes
+ *
+ * @param svgp SVG-Plot
+ */
+ private void addCSSClasses(SVGPlot svgp) {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ // Class for the cube
+ if(!svgp.getCSSClassManager().contains(MARKER)) {
+ CSSClass cls = new CSSClass(this, MARKER);
+ cls.setStatement(SVGConstants.CSS_STROKE_VALUE, style.getColor(StyleLibrary.SELECTION));
+ cls.setStatement(SVGConstants.CSS_STROKE_OPACITY_PROPERTY, style.getOpacity(StyleLibrary.SELECTION));
+ cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.PLOT));
+ cls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+ cls.setStatement(SVGConstants.CSS_STROKE_LINEJOIN_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, style.getColor(StyleLibrary.SELECTION));
+ cls.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, style.getOpacity(StyleLibrary.SELECTION));
+
+ svgp.addCSSClassOrLogError(cls);
+ }
}
@Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<SelectionResult> selectionResults = ResultUtil.filterResults(result, SelectionResult.class);
- for(SelectionResult selres : selectionResults) {
- Collection<ParallelPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ParallelPlotProjector.class);
- for(ParallelPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, selres, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA - 1);
- baseResult.getHierarchy().add(selres, task);
- baseResult.getHierarchy().add(p, task);
+ protected void redraw() {
+ DBIDSelection selContext = context.getSelection();
+ if(!(selContext instanceof RangeSelection)) {
+ return;
+ }
+ DoubleDoublePair[] ranges = ((RangeSelection) selContext).getRanges();
+ if(ranges == null) {
+ return;
+ }
+
+ // Project:
+ double[] min = new double[ranges.length];
+ double[] max = new double[ranges.length];
+ for(int d = 0; d < ranges.length; d++) {
+ if(ranges[d] != null) {
+ min[d] = ranges[d].first;
+ max[d] = ranges[d].second;
+ }
+ }
+ min = proj.fastProjectDataToRenderSpace(min);
+ max = proj.fastProjectDataToRenderSpace(max);
+
+ int dim = proj.getVisibleDimensions();
+ for(int d = 0; d < dim; d++) {
+ if(ranges[proj.getDimForVisibleAxis(d)] != null) {
+ double amin = Math.min(min[d], max[d]);
+ double amax = Math.max(min[d], max[d]);
+ Element rect = svgp.svgRect(getVisibleAxisX(d) - (0.01 * StyleLibrary.SCALE), amin, 0.02 * StyleLibrary.SCALE, amax - amin);
+ SVGUtil.addCSSClass(rect, MARKER);
+ layer.appendChild(rect);
}
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionLineVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionLineVisualization.java
index b9a9ac48..d7ccb072 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionLineVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionLineVisualization.java
@@ -54,113 +54,113 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.thumbs.ThumbnailVisualizati
*
* @author Robert Rödler
*
- * @apiviz.has SelectionResult oneway - - visualizes
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class SelectionLineVisualization extends AbstractParallelVisualization<NumberVector<?, ?>> implements DataStoreListener {
+public class SelectionLineVisualization extends AbstractVisFactory {
/**
* A short name characterizing this Visualizer.
*/
- private static final String NAME = "Selection Line";
-
- /**
- * CSS Class for the range marker
- */
- public static final String MARKER = "SelectionLine";
+ public static final String NAME = "Selection Line";
/**
* Constructor.
- *
- * @param task Visualization task
*/
- public SelectionLineVisualization(VisualizationTask task) {
- super(task);
- addCSSClasses(svgp);
- context.addDataStoreListener(this);
- context.addResultListener(this);
- incrementalRedraw();
+ public SelectionLineVisualization() {
+ super();
+ thumbmask |= ThumbnailVisualization.ON_DATA | ThumbnailVisualization.ON_SELECTION;
}
-
+
@Override
- public void destroy() {
- context.removeDataStoreListener(this);
- context.removeResultListener(this);
- super.destroy();
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
}
@Override
- protected void redraw() {
- DBIDSelection selContext = context.getSelection();
- if(selContext != null) {
- DBIDs selection = selContext.getSelectedIds();
-
- for(DBIDIter iter = selection.iter(); iter.valid(); iter.advance()) {
- double[] yPos = proj.fastProjectDataToRenderSpace(relation.get(iter));
-
- SVGPath path = new SVGPath();
- for(int i = 0; i < proj.getVisibleDimensions(); i++) {
- path.drawTo(getVisibleAxisX(i), yPos[i]);
- }
- Element marker = path.makeElement(svgp);
- SVGUtil.addCSSClass(marker, MARKER);
- layer.appendChild(marker);
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<SelectionResult> selectionResults = ResultUtil.filterResults(result, SelectionResult.class);
+ for(SelectionResult selres : selectionResults) {
+ Collection<ParallelPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ParallelPlotProjector.class);
+ for(ParallelPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, selres, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_DATA - 1;
+ baseResult.getHierarchy().add(selres, task);
+ baseResult.getHierarchy().add(p, task);
}
}
}
/**
- * Adds the required CSS-Classes
- *
- * @param svgp SVG-Plot
- */
- private void addCSSClasses(SVGPlot svgp) {
- final StyleLibrary style = context.getStyleLibrary();
- // Class for the cube
- if(!svgp.getCSSClassManager().contains(MARKER)) {
- CSSClass cls = new CSSClass(this, MARKER);
- cls.setStatement(SVGConstants.CSS_STROKE_VALUE, style.getColor(StyleLibrary.SELECTION));
- cls.setStatement(SVGConstants.CSS_STROKE_OPACITY_PROPERTY, style.getOpacity(StyleLibrary.SELECTION));
- cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.PLOT) * 2.);
- cls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
- cls.setStatement(SVGConstants.CSS_STROKE_LINEJOIN_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
- cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
- svgp.addCSSClassOrLogError(cls);
- }
- }
-
- /**
- * Factory for visualizers to generate an SVG-Element containing a cube as
- * marker representing the selected range for each dimension
+ * Instance
*
* @author Robert Rödler
*
- * @apiviz.stereotype factory
+ * @apiviz.has SelectionResult oneway - - visualizes
*/
- public static class Factory extends AbstractVisFactory {
+ public class Instance extends AbstractParallelVisualization<NumberVector<?>> implements DataStoreListener {
+ /**
+ * CSS Class for the range marker
+ */
+ public static final String MARKER = "SelectionLine";
+
/**
* Constructor.
+ *
+ * @param task Visualization task
*/
- public Factory() {
- super();
- thumbmask |= ThumbnailVisualization.ON_DATA | ThumbnailVisualization.ON_SELECTION;
+ public Instance(VisualizationTask task) {
+ super(task);
+ addCSSClasses(svgp);
+ context.addDataStoreListener(this);
+ context.addResultListener(this);
+ incrementalRedraw();
}
@Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new SelectionLineVisualization(task);
+ public void destroy() {
+ context.removeDataStoreListener(this);
+ context.removeResultListener(this);
+ super.destroy();
}
@Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<SelectionResult> selectionResults = ResultUtil.filterResults(result, SelectionResult.class);
- for(SelectionResult selres : selectionResults) {
- Collection<ParallelPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ParallelPlotProjector.class);
- for(ParallelPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, selres, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA -1);
- baseResult.getHierarchy().add(selres, task);
- baseResult.getHierarchy().add(p, task);
+ protected void redraw() {
+ DBIDSelection selContext = context.getSelection();
+ if(selContext != null) {
+ DBIDs selection = selContext.getSelectedIds();
+
+ for(DBIDIter iter = selection.iter(); iter.valid(); iter.advance()) {
+ double[] yPos = proj.fastProjectDataToRenderSpace(relation.get(iter));
+
+ SVGPath path = new SVGPath();
+ for(int i = 0; i < proj.getVisibleDimensions(); i++) {
+ path.drawTo(getVisibleAxisX(i), yPos[i]);
+ }
+ Element marker = path.makeElement(svgp);
+ SVGUtil.addCSSClass(marker, MARKER);
+ layer.appendChild(marker);
}
}
}
+
+ /**
+ * Adds the required CSS-Classes
+ *
+ * @param svgp SVG-Plot
+ */
+ private void addCSSClasses(SVGPlot svgp) {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ // Class for the cube
+ if(!svgp.getCSSClassManager().contains(MARKER)) {
+ CSSClass cls = new CSSClass(this, MARKER);
+ cls.setStatement(SVGConstants.CSS_STROKE_VALUE, style.getColor(StyleLibrary.SELECTION));
+ cls.setStatement(SVGConstants.CSS_STROKE_OPACITY_PROPERTY, style.getOpacity(StyleLibrary.SELECTION));
+ cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.PLOT) * 2.);
+ cls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+ cls.setStatement(SVGConstants.CSS_STROKE_LINEJOIN_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
+ svgp.addCSSClassOrLogError(cls);
+ }
+ }
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionToolAxisRangeVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionToolAxisRangeVisualization.java
index 2f3c6778..4fd00023 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionToolAxisRangeVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionToolAxisRangeVisualization.java
@@ -59,14 +59,14 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.parallel.AbstractParallelVi
*
* @author Robert Rödler
*
- * @apiviz.has SelectionResult oneway - - updates
- * @apiviz.has RangeSelection oneway - - updates
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class SelectionToolAxisRangeVisualization extends AbstractParallelVisualization<NumberVector<?, ?>> implements DragableArea.DragListener {
+public class SelectionToolAxisRangeVisualization extends AbstractVisFactory {
/**
* The logger for this class.
*/
- protected static final Logging logger = Logging.getLogger(SelectionToolAxisRangeVisualization.class);
+ private static final Logging LOG = Logging.getLogger(SelectionToolAxisRangeVisualization.class);
/**
* A short name characterizing this Visualizer.
@@ -74,237 +74,232 @@ public class SelectionToolAxisRangeVisualization extends AbstractParallelVisuali
private static final String NAME = "Axis Range Selection";
/**
- * Generic tag to indicate the type of element. Used in IDs, CSS-Classes etc.
+ * Constructor, adhering to
+ * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
*/
- private static final String CSS_RANGEMARKER = "selectionAxisRangeMarker";
-
- /**
- * Element for selection rectangle
- */
- private Element rtag;
-
- /**
- * Element for the rectangle to add listeners
- */
- private Element etag;
-
- /**
- * Constructor.
- *
- * @param task Task
- */
- public SelectionToolAxisRangeVisualization(VisualizationTask task) {
- super(task);
- incrementalRedraw();
+ public SelectionToolAxisRangeVisualization() {
+ super();
}
@Override
- public void destroy() {
- super.destroy();
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
}
@Override
- protected void redraw() {
- addCSSClasses(svgp);
-
- // rtag: tag for the selected rect
- rtag = svgp.svgElement(SVGConstants.SVG_G_TAG);
- SVGUtil.addCSSClass(rtag, CSS_RANGEMARKER);
- layer.appendChild(rtag);
-
- // etag: sensitive area
- DragableArea drag = new DragableArea(svgp, -.1 * getMarginLeft(), -.1 * getMarginTop(), getSizeX() + getMarginLeft() * .2, getSizeY() + getMarginTop() * .2, this);
- etag = drag.getElement();
- layer.appendChild(etag);
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<SelectionResult> selectionResults = ResultUtil.filterResults(result, SelectionResult.class);
+ for(SelectionResult selres : selectionResults) {
+ Collection<ParallelPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ParallelPlotProjector.class);
+ for(ParallelPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, selres, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_INTERACTIVE;
+ task.tool = true;
+ task.thumbnail = false;
+ task.noexport = true;
+ task.initDefaultVisibility(false);
+ baseResult.getHierarchy().add(selres, task);
+ baseResult.getHierarchy().add(p, task);
+ }
+ }
}
/**
- * Delete the children of the element
+ * Instance
+ *
+ * @author Robert Rödler
*
- * @param container SVG-Element
+ * @apiviz.has SelectionResult oneway - - updates
+ * @apiviz.has RangeSelection oneway - - updates
*/
- private void deleteChildren(Element container) {
- while(container.hasChildNodes()) {
- container.removeChild(container.getLastChild());
+ public class Instance extends AbstractParallelVisualization<NumberVector<?>> implements DragableArea.DragListener {
+ /**
+ * Generic tag to indicate the type of element. Used in IDs, CSS-Classes
+ * etc.
+ */
+ private static final String CSS_RANGEMARKER = "selectionAxisRangeMarker";
+
+ /**
+ * Element for selection rectangle
+ */
+ private Element rtag;
+
+ /**
+ * Element for the rectangle to add listeners
+ */
+ private Element etag;
+
+ /**
+ * Constructor.
+ *
+ * @param task Task
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
+ incrementalRedraw();
}
- }
- /**
- * Set the selected ranges and the mask for the actual dimensions in the
- * context
- *
- * @param x1 min x-value
- * @param x2 max x-value
- * @param y1 min y-value
- * @param y2 max y-value
- */
- private void updateSelectionRectKoordinates(double x1, double x2, double y1, double y2, DoubleDoublePair[] ranges) {
- final int dim = proj.getVisibleDimensions();
- int minaxis = dim + 1;
- int maxaxis = -1;
- {
- int i = 0;
- while(i < dim) {
- double axx = getVisibleAxisX(i);
- if(x1 < axx || x2 < axx) {
- minaxis = i;
- break;
- }
- i++;
+ @Override
+ protected void redraw() {
+ addCSSClasses(svgp);
+
+ // rtag: tag for the selected rect
+ rtag = svgp.svgElement(SVGConstants.SVG_G_TAG);
+ SVGUtil.addCSSClass(rtag, CSS_RANGEMARKER);
+ layer.appendChild(rtag);
+
+ // etag: sensitive area
+ DragableArea drag = new DragableArea(svgp, -.1 * getMarginLeft(), -.1 * getMarginTop(), getSizeX() + getMarginLeft() * .2, getSizeY() + getMarginTop() * .2, this);
+ etag = drag.getElement();
+ layer.appendChild(etag);
+ }
+
+ /**
+ * Delete the children of the element
+ *
+ * @param container SVG-Element
+ */
+ private void deleteChildren(Element container) {
+ while(container.hasChildNodes()) {
+ container.removeChild(container.getLastChild());
}
- while(i <= dim) {
- double axx = getVisibleAxisX(i);
- if(x2 < axx && x1 < axx) {
- maxaxis = i;
- break;
+ }
+
+ /**
+ * Set the selected ranges and the mask for the actual dimensions in the
+ * context
+ *
+ * @param x1 min x-value
+ * @param x2 max x-value
+ * @param y1 min y-value
+ * @param y2 max y-value
+ */
+ private void updateSelectionRectKoordinates(double x1, double x2, double y1, double y2, DoubleDoublePair[] ranges) {
+ final int dim = proj.getVisibleDimensions();
+ int minaxis = dim + 1;
+ int maxaxis = -1;
+ {
+ int i = 0;
+ while(i < dim) {
+ double axx = getVisibleAxisX(i);
+ if(x1 < axx || x2 < axx) {
+ minaxis = i;
+ break;
+ }
+ i++;
+ }
+ while(i <= dim) {
+ double axx = getVisibleAxisX(i);
+ if(x2 < axx && x1 < axx) {
+ maxaxis = i;
+ break;
+ }
+ i++;
}
- i++;
}
- }
- double z1 = Math.max(Math.min(y1, y2), 0);
- double z2 = Math.min(Math.max(y1, y2), getSizeY());
- for(int i = minaxis; i < maxaxis; i++) {
- double v1 = proj.fastProjectRenderToDataSpace(z1, i);
- double v2 = proj.fastProjectRenderToDataSpace(z2, i);
- if(logger.isDebugging()) {
- logger.debug("Axis " + i + " dimension " + proj.getDimForVisibleAxis(i) + " " + v1 + " to " + v2);
+ double z1 = Math.max(Math.min(y1, y2), 0);
+ double z2 = Math.min(Math.max(y1, y2), getSizeY());
+ for(int i = minaxis; i < maxaxis; i++) {
+ double v1 = proj.fastProjectRenderToDataSpace(z1, i);
+ double v2 = proj.fastProjectRenderToDataSpace(z2, i);
+ if(LOG.isDebugging()) {
+ LOG.debug("Axis " + i + " dimension " + proj.getDimForVisibleAxis(i) + " " + v1 + " to " + v2);
+ }
+ ranges[proj.getDimForVisibleAxis(i)] = new DoubleDoublePair(Math.min(v1, v2), Math.max(v1, v2));
}
- ranges[proj.getDimForVisibleAxis(i)] = new DoubleDoublePair(Math.min(v1, v2), Math.max(v1, v2));
}
- }
-
- @Override
- public boolean startDrag(SVGPoint startPoint, Event evt) {
- return true;
- }
- @Override
- public boolean duringDrag(SVGPoint startPoint, SVGPoint dragPoint, Event evt, boolean inside) {
- deleteChildren(rtag);
- double x = Math.min(startPoint.getX(), dragPoint.getX());
- double y = Math.min(startPoint.getY(), dragPoint.getY());
- double width = Math.abs(startPoint.getX() - dragPoint.getX());
- double height = Math.abs(startPoint.getY() - dragPoint.getY());
- rtag.appendChild(svgp.svgRect(x, y, width, height));
- return true;
- }
-
- @Override
- public boolean endDrag(SVGPoint startPoint, SVGPoint dragPoint, Event evt, boolean inside) {
- deleteChildren(rtag);
- if(startPoint.getX() != dragPoint.getX() || startPoint.getY() != dragPoint.getY()) {
- updateSelection(proj, startPoint, dragPoint);
+ @Override
+ public boolean startDrag(SVGPoint startPoint, Event evt) {
+ return true;
}
- return true;
- }
- /**
- * Update the selection in the context.
- *
- * @param proj The projection
- * @param p1 First Point of the selected rectangle
- * @param p2 Second Point of the selected rectangle
- */
- private void updateSelection(Projection proj, SVGPoint p1, SVGPoint p2) {
- DBIDSelection selContext = context.getSelection();
- ModifiableDBIDs selection;
- if(selContext != null) {
- selection = DBIDUtil.newHashSet(selContext.getSelectedIds());
- }
- else {
- selection = DBIDUtil.newHashSet();
+ @Override
+ public boolean duringDrag(SVGPoint startPoint, SVGPoint dragPoint, Event evt, boolean inside) {
+ deleteChildren(rtag);
+ double x = Math.min(startPoint.getX(), dragPoint.getX());
+ double y = Math.min(startPoint.getY(), dragPoint.getY());
+ double width = Math.abs(startPoint.getX() - dragPoint.getX());
+ double height = Math.abs(startPoint.getY() - dragPoint.getY());
+ rtag.appendChild(svgp.svgRect(x, y, width, height));
+ return true;
}
- DoubleDoublePair[] ranges;
- if(p1 == null || p2 == null) {
- logger.warning("no rect selected: p1: " + p1 + " p2: " + p2);
+ @Override
+ public boolean endDrag(SVGPoint startPoint, SVGPoint dragPoint, Event evt, boolean inside) {
+ deleteChildren(rtag);
+ if(startPoint.getX() != dragPoint.getX() || startPoint.getY() != dragPoint.getY()) {
+ updateSelection(proj, startPoint, dragPoint);
+ }
+ return true;
}
- else {
- double x1 = Math.min(p1.getX(), p2.getX());
- double x2 = Math.max(p1.getX(), p2.getX());
- double y1 = Math.max(p1.getY(), p2.getY());
- double y2 = Math.min(p1.getY(), p2.getY());
- int dim = proj.getInputDimensionality();
- if(selContext instanceof RangeSelection) {
- ranges = ((RangeSelection) selContext).getRanges();
+ /**
+ * Update the selection in the context.
+ *
+ * @param proj The projection
+ * @param p1 First Point of the selected rectangle
+ * @param p2 Second Point of the selected rectangle
+ */
+ private void updateSelection(Projection proj, SVGPoint p1, SVGPoint p2) {
+ DBIDSelection selContext = context.getSelection();
+ ModifiableDBIDs selection;
+ if(selContext != null) {
+ selection = DBIDUtil.newHashSet(selContext.getSelectedIds());
}
else {
- ranges = new DoubleDoublePair[dim];
+ selection = DBIDUtil.newHashSet();
+ }
+ DoubleDoublePair[] ranges;
+
+ if(p1 == null || p2 == null) {
+ LOG.warning("no rect selected: p1: " + p1 + " p2: " + p2);
}
- updateSelectionRectKoordinates(x1, x2, y1, y2, ranges);
+ else {
+ double x1 = Math.min(p1.getX(), p2.getX());
+ double x2 = Math.max(p1.getX(), p2.getX());
+ double y1 = Math.max(p1.getY(), p2.getY());
+ double y2 = Math.min(p1.getY(), p2.getY());
+
+ int dim = proj.getInputDimensionality();
+ if(selContext instanceof RangeSelection) {
+ ranges = ((RangeSelection) selContext).getRanges();
+ }
+ else {
+ ranges = new DoubleDoublePair[dim];
+ }
+ updateSelectionRectKoordinates(x1, x2, y1, y2, ranges);
- selection.clear();
+ selection.clear();
- candidates: for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
- NumberVector<?, ?> dbTupel = relation.get(iditer);
- for(int i = 0; i < dim; i++) {
- if(ranges != null && ranges[i] != null) {
- if(dbTupel.doubleValue(i + 1) < ranges[i].first || dbTupel.doubleValue(i + 1) > ranges[i].second) {
- continue candidates;
+ candidates: for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
+ NumberVector<?> dbTupel = relation.get(iditer);
+ for(int i = 0; i < dim; i++) {
+ if(ranges != null && ranges[i] != null) {
+ if(dbTupel.doubleValue(i) < ranges[i].first || dbTupel.doubleValue(i) > ranges[i].second) {
+ continue candidates;
+ }
}
}
+ selection.add(iditer);
}
- selection.add(iditer);
+ context.setSelection(new RangeSelection(selection, ranges));
}
- context.setSelection(new RangeSelection(selection, ranges));
}
- }
- /**
- * Adds the required CSS-Classes
- *
- * @param svgp SVG-Plot
- */
- protected void addCSSClasses(SVGPlot svgp) {
- // Class for the range marking
- if(!svgp.getCSSClassManager().contains(CSS_RANGEMARKER)) {
- final CSSClass rcls = new CSSClass(this, CSS_RANGEMARKER);
- final StyleLibrary style = context.getStyleLibrary();
- rcls.setStatement(SVGConstants.CSS_FILL_PROPERTY, style.getColor(StyleLibrary.SELECTION_ACTIVE));
- rcls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, style.getOpacity(StyleLibrary.SELECTION_ACTIVE));
- svgp.addCSSClassOrLogError(rcls);
- }
- }
-
- /**
- * Factory for tool visualizations for selecting ranges and the inclosed
- * objects
- *
- * @author Robert Rödler
- *
- * @apiviz.stereotype factory
- * @apiviz.uses SelectionToolAxisRangeVisualization oneway - - «create»
- */
- public static class Factory extends AbstractVisFactory {
/**
- * Constructor, adhering to
- * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
+ * Adds the required CSS-Classes
+ *
+ * @param svgp SVG-Plot
*/
- public Factory() {
- super();
- }
-
- @Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new SelectionToolAxisRangeVisualization(task);
- }
-
- @Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<SelectionResult> selectionResults = ResultUtil.filterResults(result, SelectionResult.class);
- for(SelectionResult selres : selectionResults) {
- Collection<ParallelPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ParallelPlotProjector.class);
- for(ParallelPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, selres, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_INTERACTIVE);
- task.put(VisualizationTask.META_TOOL, true);
- task.put(VisualizationTask.META_NOTHUMB, true);
- task.put(VisualizationTask.META_NOEXPORT, true);
- task.put(VisualizationTask.META_VISIBLE_DEFAULT, false);
- baseResult.getHierarchy().add(selres, task);
- baseResult.getHierarchy().add(p, task);
- }
+ protected void addCSSClasses(SVGPlot svgp) {
+ // Class for the range marking
+ if(!svgp.getCSSClassManager().contains(CSS_RANGEMARKER)) {
+ final CSSClass rcls = new CSSClass(this, CSS_RANGEMARKER);
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ rcls.setStatement(SVGConstants.CSS_FILL_PROPERTY, style.getColor(StyleLibrary.SELECTION_ACTIVE));
+ rcls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, style.getOpacity(StyleLibrary.SELECTION_ACTIVE));
+ svgp.addCSSClassOrLogError(rcls);
}
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionToolLineVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionToolLineVisualization.java
index 3e66b4cd..b7296492 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionToolLineVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionToolLineVisualization.java
@@ -58,21 +58,16 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.parallel.AbstractParallelVi
*
* @author Robert Rödler
*
- * @apiviz.has SelectionResult oneway - - updates
- * @apiviz.has DBIDSelection oneway - - updates
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance - - «create»
*/
-public class SelectionToolLineVisualization extends AbstractParallelVisualization<NumberVector<?, ?>> implements DragableArea.DragListener {
+public class SelectionToolLineVisualization extends AbstractVisFactory {
/**
* A short name characterizing this Visualizer.
*/
private static final String NAME = "Object Selection";
/**
- * CSS class of the selection rectangle while selecting.
- */
- private static final String CSS_RANGEMARKER = "selectionRangeMarker";
-
- /**
* Input modes
*
* @apiviz.exclude
@@ -82,249 +77,253 @@ public class SelectionToolLineVisualization extends AbstractParallelVisualizatio
}
/**
- * Element for selection rectangle
- */
- Element rtag;
-
- /**
- * Element for the rectangle to add listeners
- */
- Element etag;
-
- /**
- * Constructor.
- *
- * @param task Task
+ * Constructor, adhering to
+ * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
*/
- public SelectionToolLineVisualization(VisualizationTask task) {
- super(task);
- incrementalRedraw();
+ public SelectionToolLineVisualization() {
+ super();
}
@Override
- protected void redraw() {
- addCSSClasses(svgp);
-
- rtag = svgp.svgElement(SVGConstants.SVG_G_TAG);
- SVGUtil.addCSSClass(rtag, CSS_RANGEMARKER);
- layer.appendChild(rtag);
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
+ }
- // etag: sensitive area
- DragableArea drag = new DragableArea(svgp, -.1 * getMarginLeft(), -.5 * getMarginTop(), getSizeX() + .2 * getMarginLeft(), getMarginTop() * 1.5 + getSizeY(), this);
- etag = drag.getElement();
- layer.appendChild(etag);
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<SelectionResult> selectionResults = ResultUtil.filterResults(result, SelectionResult.class);
+ for(SelectionResult selres : selectionResults) {
+ Collection<ParallelPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ParallelPlotProjector.class);
+ for(ParallelPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, selres, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_INTERACTIVE;
+ task.tool = true;
+ task.thumbnail = false;
+ task.noexport = true;
+ task.initDefaultVisibility(false);
+ baseResult.getHierarchy().add(selres, task);
+ baseResult.getHierarchy().add(p, task);
+ }
+ }
}
/**
- * Delete the children of the element
+ * Instance.
+ *
+ * @author Robert Rödler
*
- * @param container SVG-Element
+ * @apiviz.has SelectionResult oneway - - updates
+ * @apiviz.has DBIDSelection oneway - - updates
*/
- private void deleteChildren(Element container) {
- while(container.hasChildNodes()) {
- container.removeChild(container.getLastChild());
+ public class Instance extends AbstractParallelVisualization<NumberVector<?>> implements DragableArea.DragListener {
+ /**
+ * CSS class of the selection rectangle while selecting.
+ */
+ private static final String CSS_RANGEMARKER = "selectionRangeMarker";
+
+ /**
+ * Element for selection rectangle
+ */
+ Element rtag;
+
+ /**
+ * Element for the rectangle to add listeners
+ */
+ Element etag;
+
+ /**
+ * Constructor.
+ *
+ * @param task Task
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
+ incrementalRedraw();
}
- }
- @Override
- public boolean startDrag(SVGPoint startPoint, Event evt) {
- return true;
- }
+ @Override
+ protected void redraw() {
+ addCSSClasses(svgp);
- @Override
- public boolean duringDrag(SVGPoint startPoint, SVGPoint dragPoint, Event evt, boolean inside) {
- deleteChildren(rtag);
- double x = Math.min(startPoint.getX(), dragPoint.getX());
- double y = Math.min(startPoint.getY(), dragPoint.getY());
- double width = Math.abs(startPoint.getX() - dragPoint.getX());
- double height = Math.abs(startPoint.getY() - dragPoint.getY());
- rtag.appendChild(svgp.svgRect(x, y, width, height));
- return true;
- }
+ rtag = svgp.svgElement(SVGConstants.SVG_G_TAG);
+ SVGUtil.addCSSClass(rtag, CSS_RANGEMARKER);
+ layer.appendChild(rtag);
- @Override
- public boolean endDrag(SVGPoint startPoint, SVGPoint dragPoint, Event evt, boolean inside) {
- Mode mode = getInputMode(evt);
- deleteChildren(rtag);
- if(startPoint.getX() != dragPoint.getX() || startPoint.getY() != dragPoint.getY()) {
- updateSelection(mode, startPoint, dragPoint);
+ // etag: sensitive area
+ DragableArea drag = new DragableArea(svgp, -.1 * getMarginLeft(), -.5 * getMarginTop(), getSizeX() + .2 * getMarginLeft(), getMarginTop() * 1.5 + getSizeY(), this);
+ etag = drag.getElement();
+ layer.appendChild(etag);
}
- return true;
- }
- /**
- * Get the current input mode, on each mouse event.
- *
- * @param evt Mouse event.
- * @return current input mode
- */
- private Mode getInputMode(Event evt) {
- if(evt instanceof DOMMouseEvent) {
- DOMMouseEvent domme = (DOMMouseEvent) evt;
- // TODO: visual indication of mode possible?
- if(domme.getShiftKey()) {
- return Mode.ADD;
- }
- else if(domme.getCtrlKey()) {
- return Mode.INVERT;
- }
- else {
- return Mode.REPLACE;
+ /**
+ * Delete the children of the element
+ *
+ * @param container SVG-Element
+ */
+ private void deleteChildren(Element container) {
+ while(container.hasChildNodes()) {
+ container.removeChild(container.getLastChild());
}
}
- // Default mode is replace.
- return Mode.REPLACE;
- }
- /**
- * Updates the selection in the context.<br>
- *
- * @param mode Input mode
- * @param p1 first point of the selected rectangle
- * @param p2 second point of the selected rectangle
- */
- private void updateSelection(Mode mode, SVGPoint p1, SVGPoint p2) {
- DBIDSelection selContext = context.getSelection();
- // Note: we rely on SET semantics below!
- final HashSetModifiableDBIDs selection;
- if(selContext == null || mode == Mode.REPLACE) {
- selection = DBIDUtil.newHashSet();
+ @Override
+ public boolean startDrag(SVGPoint startPoint, Event evt) {
+ return true;
}
- else {
- selection = DBIDUtil.newHashSet(selContext.getSelectedIds());
+
+ @Override
+ public boolean duringDrag(SVGPoint startPoint, SVGPoint dragPoint, Event evt, boolean inside) {
+ deleteChildren(rtag);
+ double x = Math.min(startPoint.getX(), dragPoint.getX());
+ double y = Math.min(startPoint.getY(), dragPoint.getY());
+ double width = Math.abs(startPoint.getX() - dragPoint.getX());
+ double height = Math.abs(startPoint.getY() - dragPoint.getY());
+ rtag.appendChild(svgp.svgRect(x, y, width, height));
+ return true;
}
- int[] axisrange = getAxisRange(Math.min(p1.getX(), p2.getX()), Math.max(p1.getX(), p2.getX()));
- DBIDs ids = ResultUtil.getSamplingResult(relation).getSample();
- for(DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
- double[] yPos = proj.fastProjectDataToRenderSpace(relation.get(iter));
- if(checkSelected(axisrange, yPos, Math.max(p1.getX(), p2.getX()), Math.min(p1.getX(), p2.getX()), Math.max(p1.getY(), p2.getY()), Math.min(p1.getY(), p2.getY()))) {
- if(mode == Mode.INVERT) {
- if(!selection.contains(iter)) {
- selection.add(iter);
- }
- else {
- selection.remove(iter);
- }
+
+ @Override
+ public boolean endDrag(SVGPoint startPoint, SVGPoint dragPoint, Event evt, boolean inside) {
+ Mode mode = getInputMode(evt);
+ deleteChildren(rtag);
+ if(startPoint.getX() != dragPoint.getX() || startPoint.getY() != dragPoint.getY()) {
+ updateSelection(mode, startPoint, dragPoint);
+ }
+ return true;
+ }
+
+ /**
+ * Get the current input mode, on each mouse event.
+ *
+ * @param evt Mouse event.
+ * @return current input mode
+ */
+ private Mode getInputMode(Event evt) {
+ if(evt instanceof DOMMouseEvent) {
+ DOMMouseEvent domme = (DOMMouseEvent) evt;
+ // TODO: visual indication of mode possible?
+ if(domme.getShiftKey()) {
+ return Mode.ADD;
+ }
+ else if(domme.getCtrlKey()) {
+ return Mode.INVERT;
}
else {
- // In REPLACE and ADD, add objects.
- // The difference was done before by not re-using the selection.
- // Since we are using a set, we can just add in any case.
- selection.add(iter);
+ return Mode.REPLACE;
}
}
+ // Default mode is replace.
+ return Mode.REPLACE;
}
- context.setSelection(new DBIDSelection(selection));
- }
- private int[] getAxisRange(double x1, double x2) {
- final int dim = proj.getVisibleDimensions();
- int minaxis = 0;
- int maxaxis = 0;
- boolean minx = true;
- boolean maxx = false;
- int count = -1;
- for(int i = 0; i < dim; i++) {
- if(minx && getVisibleAxisX(i) > x1) {
- minaxis = count;
- minx = false;
- maxx = true;
+ /**
+ * Updates the selection in the context.<br>
+ *
+ * @param mode Input mode
+ * @param p1 first point of the selected rectangle
+ * @param p2 second point of the selected rectangle
+ */
+ private void updateSelection(Mode mode, SVGPoint p1, SVGPoint p2) {
+ DBIDSelection selContext = context.getSelection();
+ // Note: we rely on SET semantics below!
+ final HashSetModifiableDBIDs selection;
+ if(selContext == null || mode == Mode.REPLACE) {
+ selection = DBIDUtil.newHashSet();
+ }
+ else {
+ selection = DBIDUtil.newHashSet(selContext.getSelectedIds());
}
- if(maxx && (getVisibleAxisX(i) > x2 || i == dim - 1)) {
- maxaxis = count + 1;
- if(i == dim - 1 && getVisibleAxisX(i) <= x2) {
- maxaxis++;
+ int[] axisrange = getAxisRange(Math.min(p1.getX(), p2.getX()), Math.max(p1.getX(), p2.getX()));
+ DBIDs ids = ResultUtil.getSamplingResult(relation).getSample();
+ for(DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
+ double[] yPos = proj.fastProjectDataToRenderSpace(relation.get(iter));
+ if(checkSelected(axisrange, yPos, Math.max(p1.getX(), p2.getX()), Math.min(p1.getX(), p2.getX()), Math.max(p1.getY(), p2.getY()), Math.min(p1.getY(), p2.getY()))) {
+ if(mode == Mode.INVERT) {
+ if(!selection.contains(iter)) {
+ selection.add(iter);
+ }
+ else {
+ selection.remove(iter);
+ }
+ }
+ else {
+ // In REPLACE and ADD, add objects.
+ // The difference was done before by not re-using the selection.
+ // Since we are using a set, we can just add in any case.
+ selection.add(iter);
+ }
}
- break;
}
- count = i;
+ context.setSelection(new DBIDSelection(selection));
}
- return new int[] { minaxis, maxaxis };
- }
- private boolean checkSelected(int[] ar, double[] yPos, double x1, double x2, double y1, double y2) {
- final int dim = proj.getVisibleDimensions();
- if (ar[0] < 0) {
- ar[0] = 0;
- }
- if(ar[1] >= dim) {
- ar[1] = dim - 1;
- }
- for(int i = ar[0] + 1; i <= ar[1] - 1; i++) {
- if(yPos[i] <= y1 && yPos[i] >= y2) {
- return true;
+ private int[] getAxisRange(double x1, double x2) {
+ final int dim = proj.getVisibleDimensions();
+ int minaxis = 0;
+ int maxaxis = 0;
+ boolean minx = true;
+ boolean maxx = false;
+ int count = -1;
+ for(int i = 0; i < dim; i++) {
+ if(minx && getVisibleAxisX(i) > x1) {
+ minaxis = count;
+ minx = false;
+ maxx = true;
+ }
+ if(maxx && (getVisibleAxisX(i) > x2 || i == dim - 1)) {
+ maxaxis = count + 1;
+ if(i == dim - 1 && getVisibleAxisX(i) <= x2) {
+ maxaxis++;
+ }
+ break;
+ }
+ count = i;
}
+ return new int[] { minaxis, maxaxis };
}
- Line2D.Double idline1 = new Line2D.Double(getVisibleAxisX(ar[0]), yPos[ar[0]], getVisibleAxisX(ar[0] + 1), yPos[ar[0] + 1]);
- Line2D.Double idline2 = new Line2D.Double(getVisibleAxisX(ar[1] - 1), yPos[ar[1] - 1], getVisibleAxisX(ar[1]), yPos[ar[1]]);
- Line2D.Double rectline1 = new Line2D.Double(x2, y1, x1, y1);
- Line2D.Double rectline2 = new Line2D.Double(x2, y1, x2, y2);
- Line2D.Double rectline3 = new Line2D.Double(x2, y2, x1, y2);
- if(idline1.intersectsLine(rectline1) || idline1.intersectsLine(rectline2) || idline1.intersectsLine(rectline3)) {
- return true;
- }
- Line2D.Double rectline4 = new Line2D.Double(x1, y1, x1, y2);
- if(idline2.intersectsLine(rectline1) || idline2.intersectsLine(rectline4) || idline2.intersectsLine(rectline3)) {
- return true;
- }
- return false;
- }
- /**
- * Adds the required CSS-Classes
- *
- * @param svgp SVG-Plot
- */
- protected void addCSSClasses(SVGPlot svgp) {
- // Class for the range marking
- if(!svgp.getCSSClassManager().contains(CSS_RANGEMARKER)) {
- final CSSClass rcls = new CSSClass(this, CSS_RANGEMARKER);
- final StyleLibrary style = context.getStyleLibrary();
- rcls.setStatement(SVGConstants.CSS_FILL_PROPERTY, style.getColor(StyleLibrary.SELECTION_ACTIVE));
- rcls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, style.getOpacity(StyleLibrary.SELECTION_ACTIVE));
- svgp.addCSSClassOrLogError(rcls);
+ private boolean checkSelected(int[] ar, double[] yPos, double x1, double x2, double y1, double y2) {
+ final int dim = proj.getVisibleDimensions();
+ if(ar[0] < 0) {
+ ar[0] = 0;
+ }
+ if(ar[1] >= dim) {
+ ar[1] = dim - 1;
+ }
+ for(int i = ar[0] + 1; i <= ar[1] - 1; i++) {
+ if(yPos[i] <= y1 && yPos[i] >= y2) {
+ return true;
+ }
+ }
+ Line2D.Double idline1 = new Line2D.Double(getVisibleAxisX(ar[0]), yPos[ar[0]], getVisibleAxisX(ar[0] + 1), yPos[ar[0] + 1]);
+ Line2D.Double idline2 = new Line2D.Double(getVisibleAxisX(ar[1] - 1), yPos[ar[1] - 1], getVisibleAxisX(ar[1]), yPos[ar[1]]);
+ Line2D.Double rectline1 = new Line2D.Double(x2, y1, x1, y1);
+ Line2D.Double rectline2 = new Line2D.Double(x2, y1, x2, y2);
+ Line2D.Double rectline3 = new Line2D.Double(x2, y2, x1, y2);
+ if(idline1.intersectsLine(rectline1) || idline1.intersectsLine(rectline2) || idline1.intersectsLine(rectline3)) {
+ return true;
+ }
+ Line2D.Double rectline4 = new Line2D.Double(x1, y1, x1, y2);
+ if(idline2.intersectsLine(rectline1) || idline2.intersectsLine(rectline4) || idline2.intersectsLine(rectline3)) {
+ return true;
+ }
+ return false;
}
- }
- /**
- * Factory for tool visualizations for selecting objects
- *
- * @author Robert Rödler
- *
- * @apiviz.stereotype factory
- * @apiviz.uses SelectionToolLineVisualization - - «create»
- *
- */
- public static class Factory extends AbstractVisFactory {
/**
- * Constructor, adhering to
- * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
+ * Adds the required CSS-Classes
+ *
+ * @param svgp SVG-Plot
*/
- public Factory() {
- super();
- }
-
- @Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new SelectionToolLineVisualization(task);
- }
-
- @Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<SelectionResult> selectionResults = ResultUtil.filterResults(result, SelectionResult.class);
- for(SelectionResult selres : selectionResults) {
- Collection<ParallelPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ParallelPlotProjector.class);
- for(ParallelPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, selres, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_INTERACTIVE);
- task.put(VisualizationTask.META_TOOL, true);
- task.put(VisualizationTask.META_NOTHUMB, true);
- task.put(VisualizationTask.META_NOEXPORT, true);
- task.put(VisualizationTask.META_VISIBLE_DEFAULT, false);
- baseResult.getHierarchy().add(selres, task);
- baseResult.getHierarchy().add(p, task);
- }
+ protected void addCSSClasses(SVGPlot svgp) {
+ // Class for the range marking
+ if(!svgp.getCSSClassManager().contains(CSS_RANGEMARKER)) {
+ final CSSClass rcls = new CSSClass(this, CSS_RANGEMARKER);
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ rcls.setStatement(SVGConstants.CSS_FILL_PROPERTY, style.getColor(StyleLibrary.SELECTION_ACTIVE));
+ rcls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, style.getOpacity(StyleLibrary.SELECTION_ACTIVE));
+ svgp.addCSSClassOrLogError(rcls);
}
}
}
-}
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/AbstractScatterplotVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/AbstractScatterplotVisualization.java
index cce9ea5d..8cedcf0b 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/AbstractScatterplotVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/AbstractScatterplotVisualization.java
@@ -56,7 +56,7 @@ public abstract class AbstractScatterplotVisualization extends AbstractVisualiza
/**
* The representation we visualize
*/
- final protected Relation<? extends NumberVector<?, ?>> rel;
+ final protected Relation<? extends NumberVector<?>> rel;
/**
* The DBID sample
@@ -73,7 +73,7 @@ public abstract class AbstractScatterplotVisualization extends AbstractVisualiza
this.proj = task.getProj();
this.rel = task.getRelation();
this.sample = ResultUtil.getSamplingResult(rel);
- final double margin = context.getStyleLibrary().getSize(StyleLibrary.MARGIN);
+ final double margin = context.getStyleResult().getStyleLibrary().getSize(StyleLibrary.MARGIN);
this.layer = setupCanvas(svgp, proj, margin, task.getWidth(), task.getHeight());
}
@@ -91,7 +91,7 @@ public abstract class AbstractScatterplotVisualization extends AbstractVisualiza
final CanvasSize canvas = proj.estimateViewport();
final double sizex = canvas.getDiffX();
final double sizey = canvas.getDiffY();
- String transform = SVGUtil.makeMarginTransform(width, height, sizex, sizey, margin) + " translate(" + SVGUtil.fmt(sizex / 2) + " " + SVGUtil.fmt(sizey / 2) + ")";
+ String transform = SVGUtil.makeMarginTransform(width, height, sizex, sizey, margin) + " translate(" + SVGUtil.fmt(sizex * .5) + " " + SVGUtil.fmt(sizey * .5) + ")";
final Element layer = SVGUtil.svgElement(svgp.getDocument(), SVGConstants.SVG_G_TAG);
SVGUtil.setAtt(layer, SVGConstants.SVG_TRANSFORM_ATTRIBUTE, transform);
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/AbstractTooltipVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/AbstractTooltipVisualization.java
index 8ce6ad67..107af095 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/AbstractTooltipVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/AbstractTooltipVisualization.java
@@ -31,7 +31,8 @@ import org.w3c.dom.events.EventListener;
import org.w3c.dom.events.EventTarget;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreListener;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.logging.LoggingUtil;
import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
@@ -87,18 +88,12 @@ public abstract class AbstractTooltipVisualization extends AbstractScatterplotVi
}
@Override
- public void destroy() {
- super.destroy();
- context.removeDataStoreListener(this);
- }
-
- @Override
public void redraw() {
setupCSS(svgp);
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ double dotsize = style.getLineWidth(StyleLibrary.PLOT);
- double dotsize = context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT);
-
- for(DBID id : sample.getSample()) {
+ for(DBIDIter id = sample.getSample().iter(); id.valid(); id.advance()) {
double[] v = proj.fastProjectDataToRenderSpace(rel.get(id));
Element tooltip = makeTooltip(id, v[0], v[1], dotsize);
SVGUtil.addCSSClass(tooltip, TOOLTIP_HIDDEN);
@@ -118,7 +113,16 @@ public abstract class AbstractTooltipVisualization extends AbstractScatterplotVi
}
}
- abstract protected Element makeTooltip(DBID id, double x, double y, double dotsize);
+ /**
+ * Make a tooltip Element for this id.
+ *
+ * @param id Id to make a tooltip for
+ * @param x X position
+ * @param y Y position
+ * @param dotsize Size of a dot
+ * @return Element
+ */
+ protected abstract Element makeTooltip(DBIDRef id, double x, double y, double dotsize);
/**
* Handle the hover events.
@@ -129,7 +133,7 @@ public abstract class AbstractTooltipVisualization extends AbstractScatterplotVi
if(evt.getTarget() instanceof Element) {
Element e = (Element) evt.getTarget();
Node next = e.getNextSibling();
- if(next != null && next instanceof Element) {
+ if(next instanceof Element) {
toggleTooltip((Element) next, evt.getType());
}
else {
@@ -174,7 +178,7 @@ public abstract class AbstractTooltipVisualization extends AbstractScatterplotVi
*
* @param svgp the SVGPlot to register the Tooltip-CSS-Class.
*/
- abstract protected void setupCSS(SVGPlot svgp);
+ protected abstract void setupCSS(SVGPlot svgp);
@Override
public void resultChanged(Result current) {
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/AxisVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/AxisVisualization.java
index 4173084d..f08c4b2d 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/AxisVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/AxisVisualization.java
@@ -28,10 +28,10 @@ import java.util.Collection;
import org.apache.batik.util.SVGConstants;
import org.w3c.dom.Element;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
import de.lmu.ifi.dbs.elki.visualization.css.CSSClass;
import de.lmu.ifi.dbs.elki.visualization.css.CSSClassManager.CSSNamingConflict;
@@ -47,113 +47,116 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
* Generates a SVG-Element containing axes, including labeling.
*
* @author Remigius Wojdanowski
+ * @author Erich Schubert
*
- * @apiviz.uses SVGSimpleLinearAxis
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class AxisVisualization extends AbstractScatterplotVisualization {
+public class AxisVisualization extends AbstractVisFactory {
+ /**
+ * A short name characterizing this Visualizer.
+ */
+ private static final String NAME = "Axes";
+
/**
* Constructor.
- *
- * @param task VisualizationTask
*/
- public AxisVisualization(VisualizationTask task) {
- super(task);
- incrementalRedraw();
+ public AxisVisualization() {
+ super();
}
@Override
- protected void redraw() {
- int dim = DatabaseUtil.dimensionality(rel);
-
- // origin
- double[] orig = proj.fastProjectScaledToRenderSpace(new double[dim]);
- // diagonal point opposite to origin
- double[] diag = new double[dim];
- for(int d2 = 0; d2 < dim; d2++) {
- diag[d2] = 1;
- }
- diag = proj.fastProjectScaledToRenderSpace(diag);
- // compute angle to diagonal line, used for axis labeling.
- double diaga = Math.atan2(diag[1] - orig[1], diag[0] - orig[0]);
-
- double alfontsize = 1.1 * context.getStyleLibrary().getTextSize(StyleLibrary.AXIS_LABEL);
- CSSClass alcls = new CSSClass(AxisVisualization.class, "unmanaged");
- alcls.setStatement(SVGConstants.CSS_FONT_SIZE_PROPERTY, SVGUtil.fmt(alfontsize));
- alcls.setStatement(SVGConstants.CSS_FILL_PROPERTY, context.getStyleLibrary().getTextColor(StyleLibrary.AXIS_LABEL));
- alcls.setStatement(SVGConstants.CSS_FONT_FAMILY_PROPERTY, context.getStyleLibrary().getFontFamily(StyleLibrary.AXIS_LABEL));
-
- // draw axes
- for(int d = 0; d < dim; d++) {
- double[] v = new double[dim];
- v[d] = 1;
- // projected endpoint of axis
- double[] ax = proj.fastProjectScaledToRenderSpace(v);
- boolean righthand = false;
- double axa = Math.atan2(ax[1] - orig[1], ax[0] - orig[0]);
- if(axa > diaga || (diaga > 0 && axa > diaga + Math.PI)) {
- righthand = true;
- }
- // System.err.println(ax.get(0) + " "+ ax.get(1)+
- // " "+(axa*180/Math.PI)+" "+(diaga*180/Math.PI));
- if(ax[0] != orig[0] || ax[1] != orig[1]) {
- try {
- SVGSimpleLinearAxis.drawAxis(svgp, layer, proj.getScale(d), orig[0], orig[1], ax[0], ax[1], righthand ? SVGSimpleLinearAxis.LabelStyle.RIGHTHAND : SVGSimpleLinearAxis.LabelStyle.LEFTHAND, context.getStyleLibrary());
- // TODO: move axis labeling into drawAxis function.
- double offx = (righthand ? 1 : -1) * 0.02 * Projection.SCALE;
- double offy = (righthand ? 1 : -1) * 0.02 * Projection.SCALE;
- Element label = svgp.svgText(ax[0] + offx, ax[1] + offy, DatabaseUtil.getColumnLabel(rel, d + 1));
- SVGUtil.setAtt(label, SVGConstants.SVG_STYLE_ATTRIBUTE, alcls.inlineCSS());
- SVGUtil.setAtt(label, SVGConstants.SVG_TEXT_ANCHOR_ATTRIBUTE, righthand ? SVGConstants.SVG_START_VALUE : SVGConstants.SVG_END_VALUE);
- layer.appendChild(label);
- }
- catch(CSSNamingConflict e) {
- throw new RuntimeException("Conflict in CSS naming for axes.", e);
- }
- }
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
+ }
+
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(result, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, p, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_BACKGROUND;
+ baseResult.getHierarchy().add(p, task);
}
}
+ @Override
+ public boolean allowThumbnails(VisualizationTask task) {
+ // Don't use thumbnails
+ return false;
+ }
+
/**
- * Factory for axis visualizations
+ * Instance.
*
* @author Erich Schubert
+ * @author Remigius Wojdanowski
+ *
+ * @apiviz.uses SVGSimpleLinearAxis
*
- * @apiviz.stereotype factory
- * @apiviz.uses AxisVisualization oneway - - «create»
*/
- public static class Factory extends AbstractVisFactory {
+ public class Instance extends AbstractScatterplotVisualization {
/**
- * A short name characterizing this Visualizer.
+ * Constructor.
+ *
+ * @param task VisualizationTask
*/
- private static final String NAME = "Axes";
-
- /**
- * Constructor, adhering to
- * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
- */
- public Factory() {
- super();
- }
-
- @Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new AxisVisualization(task);
+ public Instance(VisualizationTask task) {
+ super(task);
+ incrementalRedraw();
}
@Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(result, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, p, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_BACKGROUND);
- baseResult.getHierarchy().add(p, task);
+ protected void redraw() {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ final int dim = RelationUtil.dimensionality(rel);
+
+ // origin
+ double[] orig = proj.fastProjectScaledToRenderSpace(new double[dim]);
+ // diagonal point opposite to origin
+ double[] diag = new double[dim];
+ for(int d2 = 0; d2 < dim; d2++) {
+ diag[d2] = 1;
+ }
+ diag = proj.fastProjectScaledToRenderSpace(diag);
+ // compute angle to diagonal line, used for axis labeling.
+ double diaga = Math.atan2(diag[1] - orig[1], diag[0] - orig[0]);
+
+ double alfontsize = 1.1 * style.getTextSize(StyleLibrary.AXIS_LABEL);
+ CSSClass alcls = new CSSClass(AxisVisualization.class, "unmanaged");
+ alcls.setStatement(SVGConstants.CSS_FONT_SIZE_PROPERTY, SVGUtil.fmt(alfontsize));
+ alcls.setStatement(SVGConstants.CSS_FILL_PROPERTY, style.getTextColor(StyleLibrary.AXIS_LABEL));
+ alcls.setStatement(SVGConstants.CSS_FONT_FAMILY_PROPERTY, style.getFontFamily(StyleLibrary.AXIS_LABEL));
+
+ // draw axes
+ for(int d = 0; d < dim; d++) {
+ double[] v = new double[dim];
+ v[d] = 1;
+ // projected endpoint of axis
+ double[] ax = proj.fastProjectScaledToRenderSpace(v);
+ boolean righthand = false;
+ double axa = Math.atan2(ax[1] - orig[1], ax[0] - orig[0]);
+ if(axa > diaga || (diaga > 0 && axa > diaga + Math.PI)) {
+ righthand = true;
+ }
+ // System.err.println(ax.get(0) + " "+ ax.get(1)+
+ // " "+(axa*180/Math.PI)+" "+(diaga*180/Math.PI));
+ if(ax[0] != orig[0] || ax[1] != orig[1]) {
+ try {
+ SVGSimpleLinearAxis.drawAxis(svgp, layer, proj.getScale(d), orig[0], orig[1], ax[0], ax[1], righthand ? SVGSimpleLinearAxis.LabelStyle.RIGHTHAND : SVGSimpleLinearAxis.LabelStyle.LEFTHAND, style);
+ // TODO: move axis labeling into drawAxis function.
+ double offx = (righthand ? 1 : -1) * 0.02 * Projection.SCALE;
+ double offy = (righthand ? 1 : -1) * 0.02 * Projection.SCALE;
+ Element label = svgp.svgText(ax[0] + offx, ax[1] + offy, RelationUtil.getColumnLabel(rel, d));
+ SVGUtil.setAtt(label, SVGConstants.SVG_STYLE_ATTRIBUTE, alcls.inlineCSS());
+ SVGUtil.setAtt(label, SVGConstants.SVG_TEXT_ANCHOR_ATTRIBUTE, righthand ? SVGConstants.SVG_START_VALUE : SVGConstants.SVG_END_VALUE);
+ layer.appendChild(label);
+ }
+ catch(CSSNamingConflict e) {
+ throw new RuntimeException("Conflict in CSS naming for axes.", e);
+ }
+ }
}
- }
-
- @Override
- public boolean allowThumbnails(VisualizationTask task) {
- // Don't use thumbnails
- return false;
}
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/MarkerVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/MarkerVisualization.java
index c98ca83b..f2140502 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/MarkerVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/MarkerVisualization.java
@@ -54,121 +54,122 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.thumbs.ThumbnailVisualizati
*
* @author Erich Schubert
*
- * @apiviz.uses StyleResult
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class MarkerVisualization extends AbstractScatterplotVisualization implements DataStoreListener {
+public class MarkerVisualization extends AbstractVisFactory {
/**
- * Generic tag to indicate the type of element. Used in IDs, CSS-Classes etc.
+ * A short name characterizing this Visualizer.
*/
- public static final String DOTMARKER = "dot";
+ private static final String NAME = "Markers";
/**
- * The result we visualize
+ * Constructor, adhering to
+ * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
*/
- private StyleResult style;
-
- /**
- * Constructor.
- *
- * @param task Visualization task
- */
- public MarkerVisualization(VisualizationTask task) {
- super(task);
- this.style = task.getResult();
- context.addDataStoreListener(this);
- context.addResultListener(this);
- incrementalRedraw();
+ public MarkerVisualization() {
+ super();
+ thumbmask |= ThumbnailVisualization.ON_DATA | ThumbnailVisualization.ON_STYLE;
}
@Override
- public void destroy() {
- super.destroy();
- context.removeDataStoreListener(this);
- context.removeResultListener(this);
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
}
@Override
- public void redraw() {
- final MarkerLibrary ml = context.getStyleLibrary().markers();
- final double marker_size = context.getStyleLibrary().getSize(StyleLibrary.MARKERPLOT);
- final StylingPolicy spol = style.getStylingPolicy();
-
- if(spol instanceof ClassStylingPolicy) {
- ClassStylingPolicy cspol = (ClassStylingPolicy) spol;
- for(int cnum = cspol.getMinStyle(); cnum < cspol.getMaxStyle(); cnum++) {
- for(DBIDIter iter = cspol.iterateClass(cnum); iter.valid(); iter.advance()) {
- if(!sample.getSample().contains(iter)) {
- continue; // TODO: can we test more efficiently than this?
- }
- try {
- final NumberVector<?, ?> vec = rel.get(iter);
- double[] v = proj.fastProjectDataToRenderSpace(vec);
- ml.useMarker(svgp, layer, v[0], v[1], cnum, marker_size);
- }
- catch(ObjectNotFoundException e) {
- // ignore.
- }
- }
- }
- }
- else {
- final String FILL = SVGConstants.CSS_FILL_PROPERTY + ":";
- // Color-based styling. Fall back to dots
- for(DBIDIter iter = sample.getSample().iter(); iter.valid(); iter.advance()) {
- try {
- double[] v = proj.fastProjectDataToRenderSpace(rel.get(iter));
- Element dot = svgp.svgCircle(v[0], v[1], marker_size);
- SVGUtil.addCSSClass(dot, DOTMARKER);
- int col = spol.getColorForDBID(iter);
- SVGUtil.setAtt(dot, SVGConstants.SVG_STYLE_ATTRIBUTE, FILL + SVGUtil.colorToString(col));
- layer.appendChild(dot);
- }
- catch(ObjectNotFoundException e) {
- // ignore.
- }
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ // Find a style result to visualize:
+ Collection<StyleResult> styleres = ResultUtil.filterResults(result, StyleResult.class);
+ for(StyleResult c : styleres) {
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, c, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_DATA;
+ baseResult.getHierarchy().add(c, task);
+ baseResult.getHierarchy().add(p, task);
}
}
}
/**
- * Visualization factory
+ * Instance.
*
* @author Erich Schubert
*
- * @apiviz.stereotype factory
- * @apiviz.uses MarkerVisualization oneway - - «create»
+ * @apiviz.uses StyleResult
*/
- public static class Factory extends AbstractVisFactory {
+ public class Instance extends AbstractScatterplotVisualization implements DataStoreListener {
/**
- * A short name characterizing this Visualizer.
+ * Generic tag to indicate the type of element. Used in IDs, CSS-Classes
+ * etc.
*/
- private static final String NAME = "Markers";
+ public static final String DOTMARKER = "dot";
/**
- * Constructor, adhering to
- * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
+ * The result we visualize
*/
- public Factory() {
- super();
- thumbmask |= ThumbnailVisualization.ON_DATA | ThumbnailVisualization.ON_STYLE;
+ private StyleResult style;
+
+ /**
+ * Constructor.
+ *
+ * @param task Visualization task
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
+ this.style = task.getResult();
+ context.addDataStoreListener(this);
+ context.addResultListener(this);
+ incrementalRedraw();
}
@Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new MarkerVisualization(task);
+ public void destroy() {
+ super.destroy();
+ context.removeDataStoreListener(this);
+ context.removeResultListener(this);
}
@Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- // Find a style result to visualize:
- Collection<StyleResult> styleres = ResultUtil.filterResults(result, StyleResult.class);
- for(StyleResult c : styleres) {
- Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, c, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA);
- baseResult.getHierarchy().add(c, task);
- baseResult.getHierarchy().add(p, task);
+ public void redraw() {
+ final MarkerLibrary ml = style.getStyleLibrary().markers();
+ final double marker_size = style.getStyleLibrary().getSize(StyleLibrary.MARKERPLOT);
+ final StylingPolicy spol = style.getStylingPolicy();
+
+ if(spol instanceof ClassStylingPolicy) {
+ ClassStylingPolicy cspol = (ClassStylingPolicy) spol;
+ for(int cnum = cspol.getMinStyle(); cnum < cspol.getMaxStyle(); cnum++) {
+ for(DBIDIter iter = cspol.iterateClass(cnum); iter.valid(); iter.advance()) {
+ if(!sample.getSample().contains(iter)) {
+ continue; // TODO: can we test more efficiently than this?
+ }
+ try {
+ final NumberVector<?> vec = rel.get(iter);
+ double[] v = proj.fastProjectDataToRenderSpace(vec);
+ ml.useMarker(svgp, layer, v[0], v[1], cnum, marker_size);
+ }
+ catch(ObjectNotFoundException e) {
+ // ignore.
+ }
+ }
+ }
+ }
+ else {
+ final String FILL = SVGConstants.CSS_FILL_PROPERTY + ":";
+ // Color-based styling. Fall back to dots
+ for(DBIDIter iter = sample.getSample().iter(); iter.valid(); iter.advance()) {
+ try {
+ double[] v = proj.fastProjectDataToRenderSpace(rel.get(iter));
+ Element dot = svgp.svgCircle(v[0], v[1], marker_size);
+ SVGUtil.addCSSClass(dot, DOTMARKER);
+ int col = spol.getColorForDBID(iter);
+ SVGUtil.setAtt(dot, SVGConstants.SVG_STYLE_ATTRIBUTE, FILL + SVGUtil.colorToString(col));
+ layer.appendChild(dot);
+ }
+ catch(ObjectNotFoundException e) {
+ // ignore.
+ }
}
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/PolygonVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/PolygonVisualization.java
index 5b7c13b4..0fc99bbc 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/PolygonVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/PolygonVisualization.java
@@ -34,15 +34,14 @@ import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreListener;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector;
import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.utilities.exceptions.ObjectNotFoundException;
import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
import de.lmu.ifi.dbs.elki.visualization.css.CSSClass;
-import de.lmu.ifi.dbs.elki.visualization.projections.Projection2D;
import de.lmu.ifi.dbs.elki.visualization.projector.ScatterPlotProjector;
import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGPath;
@@ -55,126 +54,123 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
*
* @author Erich Schubert
*
- * @apiviz.has PolygonsObject - - visualizes
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class PolygonVisualization extends AbstractScatterplotVisualization implements DataStoreListener {
+public class PolygonVisualization extends AbstractVisFactory {
/**
* A short name characterizing this Visualizer.
*/
private static final String NAME = "Polygons";
/**
- * Generic tag to indicate the type of element. Used in IDs, CSS-Classes etc.
+ * Constructor
*/
- public static final String POLYS = "polys";
-
- /**
- * The current projection
- */
- final protected Projection2D proj;
-
- /**
- * The representation we visualize
- */
- final protected Relation<PolygonsObject> rep;
-
- /**
- * Constructor.
- *
- * @param task Task to visualize
- */
- public PolygonVisualization(VisualizationTask task) {
- super(task);
- this.proj = task.getProj();
- this.rep = task.getResult(); // Note: relation was used for projection
- context.addDataStoreListener(this);
- incrementalRedraw();
+ public PolygonVisualization() {
+ super();
}
@Override
- public void destroy() {
- super.destroy();
- context.removeDataStoreListener(this);
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
}
@Override
- public void redraw() {
- CSSClass css = new CSSClass(svgp, POLYS);
- // TODO: separate fill and line colors?
- css.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, context.getStyleLibrary().getLineWidth(StyleLibrary.POLYGONS));
- css.setStatement(SVGConstants.CSS_STROKE_PROPERTY, context.getStyleLibrary().getColor(StyleLibrary.POLYGONS));
- css.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
- svgp.addCSSClassOrLogError(css);
- svgp.updateStyleElement();
-
- // draw data
- for(DBIDIter iditer = rep.iterDBIDs(); iditer.valid(); iditer.advance()) {
- try {
- PolygonsObject poly = rep.get(iditer);
- if(poly == null) {
- continue;
- }
- SVGPath path = new SVGPath();
- for(Polygon ppoly : poly.getPolygons()) {
- Vector first = ppoly.get(0);
- double[] f = proj.fastProjectDataToRenderSpace(first.getArrayRef());
- path.moveTo(f[0], f[1]);
- for(Vector v : ppoly) {
- if(v == first) {
- continue;
- }
- double[] p = proj.fastProjectDataToRenderSpace(v.getArrayRef());
- path.drawTo(p[0], p[1]);
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<Relation<?>> results = ResultUtil.filterResults(result, Relation.class);
+ for(Relation<?> rel : results) {
+ if(TypeUtil.POLYGON_TYPE.isAssignableFromType(rel.getDataTypeInformation())) {
+ // Assume that a 2d projector is using the same coordinates as the
+ // polygons.
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
+ if(RelationUtil.dimensionality(p.getRelation()) == 2) {
+ final VisualizationTask task = new VisualizationTask(NAME, rel, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_DATA - 10;
+ baseResult.getHierarchy().add(rel, task);
+ baseResult.getHierarchy().add(p, task);
}
- // close path.
- path.drawTo(f[0], f[1]);
}
- Element e = path.makeElement(svgp);
- SVGUtil.addCSSClass(e, POLYS);
- layer.appendChild(e);
- }
- catch(ObjectNotFoundException e) {
- // ignore.
}
}
}
/**
- * The visualization factory
+ * Instance
*
* @author Erich Schubert
*
- * @apiviz.stereotype factory
- * @apiviz.uses PolygonVisualization oneway - - «create»
+ * @apiviz.has PolygonsObject - - visualizes
*/
- public static class Factory extends AbstractVisFactory {
+ public class Instance extends AbstractScatterplotVisualization implements DataStoreListener {
+ /**
+ * Generic tag to indicate the type of element. Used in IDs, CSS-Classes
+ * etc.
+ */
+ public static final String POLYS = "polys";
+
/**
- * Constructor
+ * The representation we visualize
*/
- public Factory() {
- super();
+ final protected Relation<PolygonsObject> rep;
+
+ /**
+ * Constructor.
+ *
+ * @param task Task to visualize
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
+ this.rep = task.getResult(); // Note: relation was used for projection
+ context.addDataStoreListener(this);
+ incrementalRedraw();
}
@Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new PolygonVisualization(task);
+ public void destroy() {
+ super.destroy();
+ context.removeDataStoreListener(this);
}
@Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<Relation<?>> results = ResultUtil.filterResults(result, Relation.class);
- for(Relation<?> rel : results) {
- if(TypeUtil.POLYGON_TYPE.isAssignableFromType(rel.getDataTypeInformation())) {
- // Assume that a 2d projector is using the same coordinates as the polygons.
- Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : ps) {
- if(DatabaseUtil.dimensionality(p.getRelation()) == 2) {
- final VisualizationTask task = new VisualizationTask(NAME, rel, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA - 10);
- baseResult.getHierarchy().add(rel, task);
- baseResult.getHierarchy().add(p, task);
+ public void redraw() {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ CSSClass css = new CSSClass(svgp, POLYS);
+ // TODO: separate fill and line colors?
+ css.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.POLYGONS));
+ css.setStatement(SVGConstants.CSS_STROKE_PROPERTY, style.getColor(StyleLibrary.POLYGONS));
+ css.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
+ svgp.addCSSClassOrLogError(css);
+ svgp.updateStyleElement();
+
+ // draw data
+ for(DBIDIter iditer = rep.iterDBIDs(); iditer.valid(); iditer.advance()) {
+ try {
+ PolygonsObject poly = rep.get(iditer);
+ if(poly == null) {
+ continue;
+ }
+ SVGPath path = new SVGPath();
+ for(Polygon ppoly : poly.getPolygons()) {
+ Vector first = ppoly.get(0);
+ double[] f = proj.fastProjectDataToRenderSpace(first.getArrayRef());
+ path.moveTo(f[0], f[1]);
+ for(Vector v : ppoly) {
+ if(v == first) {
+ continue;
+ }
+ double[] p = proj.fastProjectDataToRenderSpace(v.getArrayRef());
+ path.drawTo(p[0], p[1]);
}
+ // close path.
+ path.drawTo(f[0], f[1]);
}
+ Element e = path.makeElement(svgp);
+ SVGUtil.addCSSClass(e, POLYS);
+ layer.appendChild(e);
+ }
+ catch(ObjectNotFoundException e) {
+ // ignore.
}
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/ReferencePointsVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/ReferencePointsVisualization.java
index 056b788b..94c1c8d2 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/ReferencePointsVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/ReferencePointsVisualization.java
@@ -46,98 +46,103 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
/**
* The actual visualization instance, for a single projection
*
+ * @author Remigius Wojdanowski
* @author Erich Schubert
*
- * @apiviz.has ReferencePointsResult oneway - - visualizes
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-// TODO: add a result listener for the reference points.
-public class ReferencePointsVisualization extends AbstractScatterplotVisualization {
- /**
- * Generic tag to indicate the type of element. Used in IDs, CSS-Classes etc.
- */
- public static final String REFPOINT = "refpoint";
-
+public class ReferencePointsVisualization extends AbstractVisFactory {
/**
* A short name characterizing this Visualizer.
*/
private static final String NAME = "Reference Points";
/**
- * Serves reference points.
+ * Constructor, adhering to
+ * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
*/
- protected ReferencePointsResult<? extends NumberVector<?, ?>> result;
-
- /**
- * Constructor.
- *
- * @param task Visualization task
- */
- public ReferencePointsVisualization(VisualizationTask task) {
- super(task);
- this.result = task.getResult();
- incrementalRedraw();
+ public ReferencePointsVisualization() {
+ super();
}
@Override
- public void redraw() {
- setupCSS(svgp);
- Iterator<? extends NumberVector<?, ?>> iter = result.iterator();
-
- final double dotsize = context.getStyleLibrary().getSize(StyleLibrary.REFERENCE_POINTS);
- while(iter.hasNext()) {
- NumberVector<?, ?> v = iter.next();
- double[] projected = proj.fastProjectDataToRenderSpace(v);
- Element dot = svgp.svgCircle(projected[0], projected[1], dotsize);
- SVGUtil.addCSSClass(dot, REFPOINT);
- layer.appendChild(dot);
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<ReferencePointsResult<?>> rps = ResultUtil.filterResults(result, ReferencePointsResult.class);
+ for(ReferencePointsResult<?> rp : rps) {
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, rp, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_DATA;
+ baseResult.getHierarchy().add(rp, task);
+ baseResult.getHierarchy().add(p, task);
+ }
}
}
- /**
- * Registers the Reference-Point-CSS-Class at a SVGPlot.
- *
- * @param svgp the SVGPlot to register the -CSS-Class.
- */
- private void setupCSS(SVGPlot svgp) {
- CSSClass refpoint = new CSSClass(svgp, REFPOINT);
- refpoint.setStatement(SVGConstants.CSS_FILL_PROPERTY, context.getStyleLibrary().getColor(StyleLibrary.REFERENCE_POINTS));
- svgp.addCSSClassOrLogError(refpoint);
+ @Override
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
}
/**
- * Generates a SVG-Element visualizing reference points.
+ * Instance.
*
* @author Remigius Wojdanowski
+ * @author Erich Schubert
*
- * @apiviz.stereotype factory
- * @apiviz.uses ReferencePointsVisualization oneway - - «create»
+ * @apiviz.has ReferencePointsResult oneway - - visualizes
*/
- public static class Factory extends AbstractVisFactory {
+ // TODO: add a result listener for the reference points.
+ public class Instance extends AbstractScatterplotVisualization {
+ /**
+ * Generic tag to indicate the type of element. Used in IDs, CSS-Classes
+ * etc.
+ */
+ public static final String REFPOINT = "refpoint";
+
+ /**
+ * Serves reference points.
+ */
+ protected ReferencePointsResult<? extends NumberVector<?>> result;
+
/**
- * Constructor, adhering to
- * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
+ * Constructor.
+ *
+ * @param task Visualization task
*/
- public Factory() {
- super();
+ public Instance(VisualizationTask task) {
+ super(task);
+ this.result = task.getResult();
+ incrementalRedraw();
}
@Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<ReferencePointsResult<?>> rps = ResultUtil.filterResults(result, ReferencePointsResult.class);
- for(ReferencePointsResult<?> rp : rps) {
- Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, rp, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA);
- baseResult.getHierarchy().add(rp, task);
- baseResult.getHierarchy().add(p, task);
- }
+ public void redraw() {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ setupCSS(svgp);
+ Iterator<? extends NumberVector<?>> iter = result.iterator();
+
+ final double dotsize = style.getSize(StyleLibrary.REFERENCE_POINTS);
+ while(iter.hasNext()) {
+ NumberVector<?> v = iter.next();
+ double[] projected = proj.fastProjectDataToRenderSpace(v);
+ Element dot = svgp.svgCircle(projected[0], projected[1], dotsize);
+ SVGUtil.addCSSClass(dot, REFPOINT);
+ layer.appendChild(dot);
}
}
- @Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new ReferencePointsVisualization(task);
+ /**
+ * Registers the Reference-Point-CSS-Class at a SVGPlot.
+ *
+ * @param svgp the SVGPlot to register the -CSS-Class.
+ */
+ private void setupCSS(SVGPlot svgp) {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ CSSClass refpoint = new CSSClass(svgp, REFPOINT);
+ refpoint.setStatement(SVGConstants.CSS_FILL_PROPERTY, style.getColor(StyleLibrary.REFERENCE_POINTS));
+ svgp.addCSSClassOrLogError(refpoint);
}
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/ToolBox2DVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/ToolBox2DVisualization.java
index d0c05cc7..352e79de 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/ToolBox2DVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/ToolBox2DVisualization.java
@@ -54,9 +54,10 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.VisualizerUtil;
*
* @author Heidi Kolb
*
- * @apiviz.has VisualizationTask oneway - - visualizes
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class ToolBox2DVisualization extends AbstractScatterplotVisualization {
+public class ToolBox2DVisualization extends AbstractVisFactory {
/**
* A short name characterizing this Visualizer.
*/
@@ -65,241 +66,241 @@ public class ToolBox2DVisualization extends AbstractScatterplotVisualization {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(Factory.class);
+ private static final Logging LOG = Logging.getLogger(ToolBox2DVisualization.class);
/**
- * CSS class for a tool button
+ * Constructor
*/
- public static final String CSS_TOOL_BUTTON = "toolButton";
-
- /**
- * CSS class for the tool button caption
- */
- public static final String CSS_TOOL_CAPTION = "toolCaption";
-
- /**
- * CSS class for a tool button
- */
- public static final String CSS_TOOL_BUTTON_SELECTED = "toolButtonSelected";
-
- /**
- * The container
- */
- private Element container;
-
- /**
- * Constructor.
- *
- * @param task Task
- */
- public ToolBox2DVisualization(VisualizationTask task) {
- super(task);
- // TODO: which result do we best attach to?
- context.addResultListener(this);
- incrementalRedraw();
+ public ToolBox2DVisualization() {
+ super();
}
@Override
- protected void redraw() {
- addCSSClasses(svgp);
- container = svgp.svgElement(SVGConstants.SVG_G_TAG);
- buildToolBox();
- layer.appendChild(container);
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
}
- /**
- * Deletes the children of the container
- *
- * @param container Element to delete children
- */
- private void deleteChildren(Element container) {
- while(container.hasChildNodes()) {
- container.removeChild(container.getLastChild());
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(result, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, p, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_INTERACTIVE;
+ task.thumbnail = false;
+ task.noexport = true;
+ task.noembed = true;
+ baseResult.getHierarchy().add(p, task);
}
}
/**
- * Build the toolbox
+ * Instance.
+ *
+ * @author Heidi Kolb
+ *
+ * @apiviz.has VisualizationTask oneway - - visualizes
*/
- private void buildToolBox() {
- double scale = StyleLibrary.SCALE;
- deleteChildren(container);
-
- ArrayList<VisualizationTask> vis = new ArrayList<VisualizationTask>();
- Collection<VisualizationTask> visualizers = ResultUtil.filterResults(task.getResult(), VisualizationTask.class);
- for(VisualizationTask task : visualizers) {
- if(VisualizerUtil.isTool(task) && !vis.contains(task)) {
- vis.add(task);
- }
- }
+ public class Instance extends AbstractScatterplotVisualization {
+ /**
+ * CSS class for a tool button
+ */
+ public static final String CSS_TOOL_BUTTON = "toolButton";
- // calculate the position of the first tool
- CanvasSize viewport = proj.estimateViewport();
- double x = viewport.getMinX() - 0.17 * scale;
- double width = 0.07 * scale;
- double height = 0.06 * scale;
- double miny = viewport.getMinY();
- double maxy = viewport.getMaxY();
- double y = (miny + maxy) / 2 - (vis.size() * height * 1.4) / 2;
- if(y < miny) {
- logger.warning("Too many Tools");
- }
+ /**
+ * CSS class for the tool button caption
+ */
+ public static final String CSS_TOOL_CAPTION = "toolCaption";
- // add tools
- Element[] toolTags = new Element[vis.size()];
- for(int i = 0; i < vis.size(); i++) {
- VisualizationTask v = vis.get(i);
- toolTags[i] = svgp.svgRect(x, y, width, height);
- String name = v.getLongName();
- // Split
- List<String> lines = FormatUtil.splitAtLastBlank(name, 8);
- // Generate label objects.
- for(int l = 0; l < lines.size(); l++) {
- Element selectRangeText = svgp.svgText(x + 0.01 * scale, y + (0.02 + 0.05 * l / lines.size()) * scale, lines.get(l));
- SVGUtil.setAtt(selectRangeText, SVGConstants.SVG_CLASS_ATTRIBUTE, CSS_TOOL_CAPTION);
- container.appendChild(selectRangeText);
- }
+ /**
+ * CSS class for a tool button
+ */
+ public static final String CSS_TOOL_BUTTON_SELECTED = "toolButtonSelected";
- if(VisualizerUtil.isVisible(v)) {
- SVGUtil.addCSSClass(toolTags[i], CSS_TOOL_BUTTON_SELECTED);
- }
- else {
- SVGUtil.addCSSClass(toolTags[i], CSS_TOOL_BUTTON);
- }
- addEventListener(toolTags[i], v);
+ /**
+ * The container
+ */
+ private Element container;
- container.appendChild(toolTags[i]);
- y = y + 0.1 * scale;
+ /**
+ * Constructor.
+ *
+ * @param task Task
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
+ // TODO: which result do we best attach to?
+ context.addResultListener(this);
+ incrementalRedraw();
}
- }
- /**
- * Adds the required CSS-Classes
- *
- * @param svgp SVG-Plot
- */
- private void addCSSClasses(SVGPlot svgp) {
- // Class for the not selected tool
- if(!svgp.getCSSClassManager().contains(CSS_TOOL_BUTTON)) {
- final CSSClass modeCls = new CSSClass(this, CSS_TOOL_BUTTON);
- modeCls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, 0.4);
- modeCls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_GREEN_VALUE);
- modeCls.setStatement(SVGConstants.CSS_CURSOR_PROPERTY, SVGConstants.CSS_POINTER_VALUE);
-
- svgp.addCSSClassOrLogError(modeCls);
- }
- // Class for the selected tool
- if(!svgp.getCSSClassManager().contains(CSS_TOOL_BUTTON_SELECTED)) {
- final CSSClass modeCls = new CSSClass(this, CSS_TOOL_BUTTON_SELECTED);
- modeCls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, 0.4);
- modeCls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_BLUE_VALUE);
- modeCls.setStatement(SVGConstants.CSS_STROKE_OPACITY_PROPERTY, 0.4);
- modeCls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, SVGConstants.CSS_BLUE_VALUE);
- modeCls.setStatement(SVGConstants.CSS_CURSOR_PROPERTY, SVGConstants.CSS_POINTER_VALUE);
-
- svgp.addCSSClassOrLogError(modeCls);
+ @Override
+ protected void redraw() {
+ addCSSClasses(svgp);
+ container = svgp.svgElement(SVGConstants.SVG_G_TAG);
+ buildToolBox();
+ layer.appendChild(container);
}
- // Class for the text of the tools
- if(!svgp.getCSSClassManager().contains(CSS_TOOL_CAPTION)) {
- final CSSClass label = new CSSClass(svgp, CSS_TOOL_CAPTION);
- label.setStatement(SVGConstants.CSS_FILL_PROPERTY, context.getStyleLibrary().getTextColor(StyleLibrary.AXIS_LABEL));
- label.setStatement(SVGConstants.CSS_FONT_FAMILY_PROPERTY, context.getStyleLibrary().getFontFamily(StyleLibrary.AXIS_LABEL));
- label.setStatement(SVGConstants.CSS_FONT_SIZE_PROPERTY, context.getStyleLibrary().getTextSize(StyleLibrary.AXIS_LABEL) * .8);
-
- svgp.addCSSClassOrLogError(label);
+
+ /**
+ * Deletes the children of the container
+ *
+ * @param container Element to delete children
+ */
+ private void deleteChildren(Element container) {
+ while(container.hasChildNodes()) {
+ container.removeChild(container.getLastChild());
+ }
}
- }
- /**
- * Add an event listener to the Element
- *
- * @param tag Element to add the listener
- * @param tool Tool represented by the Element
- */
- private void addEventListener(final Element tag, final VisualizationTask tool) {
- EventTarget targ = (EventTarget) tag;
- targ.addEventListener(SVGConstants.SVG_EVENT_CLICK, new EventListener() {
- @Override
- public void handleEvent(Event evt) {
- handleMouseClick(tool);
+ /**
+ * Build the toolbox
+ */
+ private void buildToolBox() {
+ double scale = StyleLibrary.SCALE;
+ deleteChildren(container);
+
+ ArrayList<VisualizationTask> vis = new ArrayList<VisualizationTask>();
+ Collection<VisualizationTask> visualizers = ResultUtil.filterResults(task.getResult(), VisualizationTask.class);
+ for(VisualizationTask task : visualizers) {
+ if(task.tool && !vis.contains(task)) {
+ vis.add(task);
+ }
}
- }, false);
- }
- /**
- * Handle the mouseClick - change the selected tool in the context
- *
- * @param tool Selected Tool
- */
- protected void handleMouseClick(VisualizationTask tool) {
- // TODO: Move this to the selected tool instead?
- if(VisualizerUtil.isVisible(tool)) {
- context.setSelection(null);
- }
- VisualizerUtil.setVisible(context, tool, true);
- }
+ // calculate the position of the first tool
+ CanvasSize viewport = proj.estimateViewport();
+ double x = viewport.getMinX() - 0.17 * scale;
+ double width = 0.07 * scale;
+ double height = 0.06 * scale;
+ double miny = viewport.getMinY();
+ double maxy = viewport.getMaxY();
+ double y = (miny + maxy - vis.size() * height * 1.4) * .5;
+ if(y < miny) {
+ LOG.warning("Too many Tools");
+ }
- @Override
- public void resultAdded(Result child, Result parent) {
- if(child instanceof VisualizationTask) {
- VisualizationTask task = (VisualizationTask) child;
- if(VisualizerUtil.isTool(task)) {
- synchronizedRedraw();
+ // add tools
+ Element[] toolTags = new Element[vis.size()];
+ for(int i = 0; i < vis.size(); i++) {
+ VisualizationTask v = vis.get(i);
+ toolTags[i] = svgp.svgRect(x, y, width, height);
+ String name = v.getLongName();
+ // Split
+ List<String> lines = FormatUtil.splitAtLastBlank(name, 8);
+ // Generate label objects.
+ for(int l = 0; l < lines.size(); l++) {
+ Element selectRangeText = svgp.svgText(x + 0.01 * scale, y + (0.02 + 0.05 * l / lines.size()) * scale, lines.get(l));
+ SVGUtil.setAtt(selectRangeText, SVGConstants.SVG_CLASS_ATTRIBUTE, CSS_TOOL_CAPTION);
+ container.appendChild(selectRangeText);
+ }
+
+ if(v.visible) {
+ SVGUtil.addCSSClass(toolTags[i], CSS_TOOL_BUTTON_SELECTED);
+ }
+ else {
+ SVGUtil.addCSSClass(toolTags[i], CSS_TOOL_BUTTON);
+ }
+ addEventListener(toolTags[i], v);
+
+ container.appendChild(toolTags[i]);
+ y = y + 0.1 * scale;
}
}
- }
- @Override
- public void resultRemoved(Result child, Result parent) {
- if(child instanceof VisualizationTask) {
- VisualizationTask task = (VisualizationTask) child;
- if(VisualizerUtil.isTool(task)) {
- synchronizedRedraw();
+ /**
+ * Adds the required CSS-Classes
+ *
+ * @param svgp SVG-Plot
+ */
+ private void addCSSClasses(SVGPlot svgp) {
+ // Class for the not selected tool
+ if(!svgp.getCSSClassManager().contains(CSS_TOOL_BUTTON)) {
+ final CSSClass modeCls = new CSSClass(this, CSS_TOOL_BUTTON);
+ modeCls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, 0.4);
+ modeCls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_GREEN_VALUE);
+ modeCls.setStatement(SVGConstants.CSS_CURSOR_PROPERTY, SVGConstants.CSS_POINTER_VALUE);
+
+ svgp.addCSSClassOrLogError(modeCls);
+ }
+ // Class for the selected tool
+ if(!svgp.getCSSClassManager().contains(CSS_TOOL_BUTTON_SELECTED)) {
+ final CSSClass modeCls = new CSSClass(this, CSS_TOOL_BUTTON_SELECTED);
+ modeCls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, 0.4);
+ modeCls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_BLUE_VALUE);
+ modeCls.setStatement(SVGConstants.CSS_STROKE_OPACITY_PROPERTY, 0.4);
+ modeCls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, SVGConstants.CSS_BLUE_VALUE);
+ modeCls.setStatement(SVGConstants.CSS_CURSOR_PROPERTY, SVGConstants.CSS_POINTER_VALUE);
+
+ svgp.addCSSClassOrLogError(modeCls);
+ }
+ // Class for the text of the tools
+ if(!svgp.getCSSClassManager().contains(CSS_TOOL_CAPTION)) {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ final CSSClass label = new CSSClass(svgp, CSS_TOOL_CAPTION);
+ label.setStatement(SVGConstants.CSS_FILL_PROPERTY, style.getTextColor(StyleLibrary.AXIS_LABEL));
+ label.setStatement(SVGConstants.CSS_FONT_FAMILY_PROPERTY, style.getFontFamily(StyleLibrary.AXIS_LABEL));
+ label.setStatement(SVGConstants.CSS_FONT_SIZE_PROPERTY, style.getTextSize(StyleLibrary.AXIS_LABEL) * .8);
+
+ svgp.addCSSClassOrLogError(label);
}
}
- }
- @Override
- public void resultChanged(Result current) {
- if(current instanceof VisualizationTask) {
- VisualizationTask task = (VisualizationTask) current;
- if(VisualizerUtil.isTool(task)) {
- synchronizedRedraw();
- }
+ /**
+ * Add an event listener to the Element
+ *
+ * @param tag Element to add the listener
+ * @param tool Tool represented by the Element
+ */
+ private void addEventListener(final Element tag, final VisualizationTask tool) {
+ EventTarget targ = (EventTarget) tag;
+ targ.addEventListener(SVGConstants.SVG_EVENT_CLICK, new EventListener() {
+ @Override
+ public void handleEvent(Event evt) {
+ handleMouseClick(tool);
+ }
+ }, false);
}
- }
- /**
- * Factory for visualizers for a toolbox
- *
- * @author Heidi Kolb
- *
- * @apiviz.stereotype factory
- * @apiviz.uses ToolBox2DVisualization oneway - - «create»
- */
- public static class Factory extends AbstractVisFactory {
/**
- * Constructor
+ * Handle the mouseClick - change the selected tool in the context
+ *
+ * @param tool Selected Tool
*/
- public Factory() {
- super();
+ protected void handleMouseClick(VisualizationTask tool) {
+ // TODO: Move this to the selected tool instead?
+ if(tool.visible) {
+ context.setSelection(null);
+ }
+ VisualizerUtil.setVisible(context, tool, true);
}
@Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new ToolBox2DVisualization(task);
+ public void resultAdded(Result child, Result parent) {
+ if(child instanceof VisualizationTask) {
+ VisualizationTask task = (VisualizationTask) child;
+ if(task.tool) {
+ synchronizedRedraw();
+ }
+ }
+ }
+
+ @Override
+ public void resultRemoved(Result child, Result parent) {
+ if(child instanceof VisualizationTask) {
+ VisualizationTask task = (VisualizationTask) child;
+ if(task.tool) {
+ synchronizedRedraw();
+ }
+ }
}
@Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(result, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, p, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_INTERACTIVE);
- task.put(VisualizationTask.META_NOTHUMB, true);
- task.put(VisualizationTask.META_NOEXPORT, true);
- task.put(VisualizationTask.META_NOEMBED, true);
- baseResult.getHierarchy().add(p, task);
+ public void resultChanged(Result current) {
+ if(current instanceof VisualizationTask) {
+ VisualizationTask task = (VisualizationTask) current;
+ if(task.tool) {
+ synchronizedRedraw();
+ }
}
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/TooltipScoreVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/TooltipScoreVisualization.java
index 139fa1ed..f3f35002 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/TooltipScoreVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/TooltipScoreVisualization.java
@@ -31,7 +31,7 @@ import org.apache.batik.util.SVGConstants;
import org.w3c.dom.Element;
import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
import de.lmu.ifi.dbs.elki.result.Result;
@@ -56,8 +56,12 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
* as the cursor lingers on the marker.
*
* @author Remigius Wojdanowski
+ * @author Erich Schubert
+ *
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class TooltipScoreVisualization extends AbstractTooltipVisualization {
+public class TooltipScoreVisualization extends AbstractVisFactory {
/**
* A short name characterizing this Visualizer.
*/
@@ -69,185 +73,182 @@ public class TooltipScoreVisualization extends AbstractTooltipVisualization {
public static final String NAME_GEN = " Tooltips";
/**
- * Number format.
+ * Settings
*/
- NumberFormat nf;
+ protected Parameterizer settings;
/**
- * Number value to visualize
- */
- private Relation<? extends Number> result;
-
- /**
- * Font size to use.
- */
- private double fontsize;
-
- /**
- * Constructor
+ * Constructor.
*
- * @param task Task
- * @param nf Number Format
+ * @param settings Settings
*/
- public TooltipScoreVisualization(VisualizationTask task, NumberFormat nf) {
- super(task);
- this.result = task.getResult();
- this.nf = nf;
- this.fontsize = 3 * context.getStyleLibrary().getTextSize(StyleLibrary.PLOT);
- synchronizedRedraw();
+ public TooltipScoreVisualization(Parameterizer settings) {
+ super();
+ this.settings = settings;
}
@Override
- protected Element makeTooltip(DBID id, double x, double y, double dotsize) {
- return svgp.svgText(x + dotsize, y + fontsize * 0.07, nf.format(result.get(id).doubleValue()));
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
}
- /**
- * Registers the Tooltip-CSS-Class at a SVGPlot.
- *
- * @param svgp the SVGPlot to register the Tooltip-CSS-Class.
- */
@Override
- protected void setupCSS(SVGPlot svgp) {
- final StyleLibrary style = context.getStyleLibrary();
- final double fontsize = style.getTextSize(StyleLibrary.PLOT);
- final String fontfamily = style.getFontFamily(StyleLibrary.PLOT);
-
- CSSClass tooltiphidden = new CSSClass(svgp, TOOLTIP_HIDDEN);
- tooltiphidden.setStatement(SVGConstants.CSS_FONT_SIZE_PROPERTY, fontsize);
- tooltiphidden.setStatement(SVGConstants.CSS_FONT_FAMILY_PROPERTY, fontfamily);
- tooltiphidden.setStatement(SVGConstants.CSS_DISPLAY_PROPERTY, SVGConstants.CSS_NONE_VALUE);
- svgp.addCSSClassOrLogError(tooltiphidden);
-
- CSSClass tooltipvisible = new CSSClass(svgp, TOOLTIP_VISIBLE);
- tooltipvisible.setStatement(SVGConstants.CSS_FONT_SIZE_PROPERTY, fontsize);
- tooltipvisible.setStatement(SVGConstants.CSS_FONT_FAMILY_PROPERTY, fontfamily);
- svgp.addCSSClassOrLogError(tooltipvisible);
-
- CSSClass tooltipsticky = new CSSClass(svgp, TOOLTIP_STICKY);
- tooltipsticky.setStatement(SVGConstants.CSS_FONT_SIZE_PROPERTY, fontsize);
- tooltipsticky.setStatement(SVGConstants.CSS_FONT_FAMILY_PROPERTY, fontfamily);
- svgp.addCSSClassOrLogError(tooltipsticky);
-
- // invisible but sensitive area for the tooltip activator
- CSSClass tooltiparea = new CSSClass(svgp, TOOLTIP_AREA);
- tooltiparea.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_RED_VALUE);
- tooltiparea.setStatement(SVGConstants.CSS_STROKE_PROPERTY, SVGConstants.CSS_NONE_VALUE);
- tooltiparea.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, "0");
- tooltiparea.setStatement(SVGConstants.CSS_CURSOR_PROPERTY, SVGConstants.CSS_POINTER_VALUE);
- svgp.addCSSClassOrLogError(tooltiparea);
-
- svgp.updateStyleElement();
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ // TODO: we can also visualize other scores!
+ Collection<OutlierResult> ors = ResultUtil.filterResults(result, OutlierResult.class);
+ for(OutlierResult o : ors) {
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, o.getScores(), p.getRelation(), this);
+ task.tool = true;
+ task.initDefaultVisibility(false);
+ baseResult.getHierarchy().add(o.getScores(), task);
+ baseResult.getHierarchy().add(p, task);
+ }
+ }
+ Collection<Relation<?>> rrs = ResultUtil.filterResults(result, Relation.class);
+ for(Relation<?> r : rrs) {
+ if(!TypeUtil.DOUBLE.isAssignableFromType(r.getDataTypeInformation()) && !TypeUtil.INTEGER.isAssignableFromType(r.getDataTypeInformation())) {
+ continue;
+ }
+ // Skip if we already considered it above
+ boolean add = true;
+ for(Result p : baseResult.getHierarchy().getChildren(r)) {
+ if(p instanceof VisualizationTask && ((VisualizationTask) p).getFactory() instanceof TooltipScoreVisualization) {
+ add = false;
+ break;
+ }
+ }
+ if(add) {
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(r.getLongName() + NAME_GEN, r, p.getRelation(), this);
+ task.tool = true;
+ task.initDefaultVisibility(false);
+ baseResult.getHierarchy().add(r, task);
+ baseResult.getHierarchy().add(p, task);
+ }
+ }
+ }
}
/**
- * Factory for tooltip visualizers
+ * Instance
*
+ * @author Remigius Wojdanowski
* @author Erich Schubert
- *
- * @apiviz.stereotype factory
- * @apiviz.uses TooltipScoreVisualization oneway - - «create»
*/
- public static class Factory extends AbstractVisFactory {
+ public class Instance extends AbstractTooltipVisualization {
/**
- * Parameter for the gamma-correction.
- *
- * <p>
- * Key: {@code -tooltip.digits}
- * </p>
- *
- * <p>
- * Default value: 4
- * </p>
+ * Number value to visualize
*/
- public static final OptionID DIGITS_ID = OptionID.getOrCreateOptionID("tooltip.digits", "Number of digits to show (e.g. when visualizing outlier scores)");
+ private Relation<? extends Number> result;
/**
- * Number formatter used for visualization
+ * Font size to use.
*/
- NumberFormat nf = null;
+ private double fontsize;
/**
- * Constructor.
+ * Constructor
*
- * @param digits number of digits
+ * @param task Task
*/
- public Factory(int digits) {
- super();
- nf = NumberFormat.getInstance(Locale.ROOT);
- nf.setGroupingUsed(false);
- nf.setMaximumFractionDigits(digits);
+ public Instance(VisualizationTask task) {
+ super(task);
+ this.result = task.getResult();
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ this.fontsize = 3 * style.getTextSize(StyleLibrary.PLOT);
+ synchronizedRedraw();
}
@Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new TooltipScoreVisualization(task, nf);
+ protected Element makeTooltip(DBIDRef id, double x, double y, double dotsize) {
+ return svgp.svgText(x + dotsize, y + fontsize * 0.07, settings.nf.format(result.get(id).doubleValue()));
}
+ /**
+ * Registers the Tooltip-CSS-Class at a SVGPlot.
+ *
+ * @param svgp the SVGPlot to register the Tooltip-CSS-Class.
+ */
@Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- // TODO: we can also visualize other scores!
- Collection<OutlierResult> ors = ResultUtil.filterResults(result, OutlierResult.class);
- for(OutlierResult o : ors) {
- Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, o.getScores(), p.getRelation(), this);
- task.put(VisualizationTask.META_TOOL, true);
- task.put(VisualizationTask.META_VISIBLE_DEFAULT, false);
- baseResult.getHierarchy().add(o.getScores(), task);
- baseResult.getHierarchy().add(p, task);
- }
- }
- Collection<Relation<?>> rrs = ResultUtil.filterResults(result, Relation.class);
- for(Relation<?> r : rrs) {
- if(!TypeUtil.DOUBLE.isAssignableFromType(r.getDataTypeInformation()) && !TypeUtil.INTEGER.isAssignableFromType(r.getDataTypeInformation())) {
- continue;
- }
- // Skip if we already considered it above
- boolean add = true;
- for(Result p : baseResult.getHierarchy().getChildren(r)) {
- if(p instanceof VisualizationTask && ((VisualizationTask) p).getFactory() instanceof Factory) {
- add = false;
- break;
- }
- }
- if(add) {
- Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(r.getLongName() + NAME_GEN, r, p.getRelation(), this);
- task.put(VisualizationTask.META_TOOL, true);
- task.put(VisualizationTask.META_VISIBLE_DEFAULT, false);
- baseResult.getHierarchy().add(r, task);
- baseResult.getHierarchy().add(p, task);
- }
- }
- }
+ protected void setupCSS(SVGPlot svgp) {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ final double fontsize = style.getTextSize(StyleLibrary.PLOT);
+ final String fontfamily = style.getFontFamily(StyleLibrary.PLOT);
+
+ CSSClass tooltiphidden = new CSSClass(svgp, TOOLTIP_HIDDEN);
+ tooltiphidden.setStatement(SVGConstants.CSS_FONT_SIZE_PROPERTY, fontsize);
+ tooltiphidden.setStatement(SVGConstants.CSS_FONT_FAMILY_PROPERTY, fontfamily);
+ tooltiphidden.setStatement(SVGConstants.CSS_DISPLAY_PROPERTY, SVGConstants.CSS_NONE_VALUE);
+ svgp.addCSSClassOrLogError(tooltiphidden);
+
+ CSSClass tooltipvisible = new CSSClass(svgp, TOOLTIP_VISIBLE);
+ tooltipvisible.setStatement(SVGConstants.CSS_FONT_SIZE_PROPERTY, fontsize);
+ tooltipvisible.setStatement(SVGConstants.CSS_FONT_FAMILY_PROPERTY, fontfamily);
+ svgp.addCSSClassOrLogError(tooltipvisible);
+
+ CSSClass tooltipsticky = new CSSClass(svgp, TOOLTIP_STICKY);
+ tooltipsticky.setStatement(SVGConstants.CSS_FONT_SIZE_PROPERTY, fontsize);
+ tooltipsticky.setStatement(SVGConstants.CSS_FONT_FAMILY_PROPERTY, fontfamily);
+ svgp.addCSSClassOrLogError(tooltipsticky);
+
+ // invisible but sensitive area for the tooltip activator
+ CSSClass tooltiparea = new CSSClass(svgp, TOOLTIP_AREA);
+ tooltiparea.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_RED_VALUE);
+ tooltiparea.setStatement(SVGConstants.CSS_STROKE_PROPERTY, SVGConstants.CSS_NONE_VALUE);
+ tooltiparea.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, "0");
+ tooltiparea.setStatement(SVGConstants.CSS_CURSOR_PROPERTY, SVGConstants.CSS_POINTER_VALUE);
+ svgp.addCSSClassOrLogError(tooltiparea);
+
+ svgp.updateStyleElement();
}
+ }
+
+ /**
+ * Parameterization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public static class Parameterizer extends AbstractParameterizer {
+ /**
+ * Number formatter used for visualization
+ */
+ NumberFormat nf = null;
/**
- * Parameterization class.
+ * Parameter for the gamma-correction.
*
- * @author Erich Schubert
+ * <p>
+ * Key: {@code -tooltip.digits}
+ * </p>
*
- * @apiviz.exclude
+ * <p>
+ * Default value: 4
+ * </p>
*/
- public static class Parameterizer extends AbstractParameterizer {
- protected int digits = 4;
+ public static final OptionID DIGITS_ID = new OptionID("tooltip.digits", "Number of digits to show (e.g. when visualizing outlier scores)");
- @Override
- protected void makeOptions(Parameterization config) {
- super.makeOptions(config);
- IntParameter DIGITS_PARAM = new IntParameter(DIGITS_ID, new GreaterEqualConstraint(0), 4);
-
- if(config.grab(DIGITS_PARAM)) {
- digits = DIGITS_PARAM.getValue();
- }
+ @Override
+ protected void makeOptions(Parameterization config) {
+ super.makeOptions(config);
+ IntParameter digitsP = new IntParameter(DIGITS_ID, 4);
+ digitsP.addConstraint(new GreaterEqualConstraint(0));
+
+ if(config.grab(digitsP)) {
+ int digits = digitsP.intValue();
+ nf = NumberFormat.getInstance(Locale.ROOT);
+ nf.setGroupingUsed(false);
+ nf.setMaximumFractionDigits(digits);
}
+ }
- @Override
- protected Factory makeInstance() {
- return new Factory(digits);
- }
+ @Override
+ protected TooltipScoreVisualization makeInstance() {
+ return new TooltipScoreVisualization(this);
}
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/TooltipStringVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/TooltipStringVisualization.java
index d015793c..979ce905 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/TooltipStringVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/TooltipStringVisualization.java
@@ -32,6 +32,7 @@ import de.lmu.ifi.dbs.elki.data.ClassLabel;
import de.lmu.ifi.dbs.elki.data.ExternalID;
import de.lmu.ifi.dbs.elki.data.LabelList;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
import de.lmu.ifi.dbs.elki.result.Result;
@@ -52,9 +53,10 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
* @author Remigius Wojdanowski
* @author Erich Schubert
*
- * @apiviz.has Relation oneway - - visualizes
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class TooltipStringVisualization extends AbstractTooltipVisualization {
+public class TooltipStringVisualization extends AbstractVisFactory {
/**
* A short name characterizing this Visualizer.
*/
@@ -76,146 +78,147 @@ public class TooltipStringVisualization extends AbstractTooltipVisualization {
public static final String NAME_EID = "External ID Tooltips";
/**
- * Number value to visualize
+ * Constructor, adhering to
+ * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
*/
- private Relation<?> result;
-
- /**
- * Font size to use.
- */
- private double fontsize;
-
- /**
- * Constructor.
- *
- * @param task Task
- */
- public TooltipStringVisualization(VisualizationTask task) {
- super(task);
- this.result = task.getResult();
- this.fontsize = 3 * context.getStyleLibrary().getTextSize(StyleLibrary.PLOT);
- synchronizedRedraw();
+ public TooltipStringVisualization() {
+ super();
}
@Override
- protected Element makeTooltip(DBID id, double x, double y, double dotsize) {
- final Object data = result.get(id);
- String label;
- if(data == null) {
- label = "null";
- }
- else {
- label = data.toString();
- }
- if(label == "" || label == null) {
- label = "null";
- }
- return svgp.svgText(x + dotsize, y + fontsize * 0.07, label);
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
}
- /**
- * Registers the Tooltip-CSS-Class at a SVGPlot.
- *
- * @param svgp the SVGPlot to register the Tooltip-CSS-Class.
- */
@Override
- protected void setupCSS(SVGPlot svgp) {
- final StyleLibrary style = context.getStyleLibrary();
- final double fontsize = style.getTextSize(StyleLibrary.PLOT);
- final String fontfamily = style.getFontFamily(StyleLibrary.PLOT);
-
- CSSClass tooltiphidden = new CSSClass(svgp, TOOLTIP_HIDDEN);
- tooltiphidden.setStatement(SVGConstants.CSS_FONT_SIZE_PROPERTY, fontsize);
- tooltiphidden.setStatement(SVGConstants.CSS_FONT_FAMILY_PROPERTY, fontfamily);
- tooltiphidden.setStatement(SVGConstants.CSS_DISPLAY_PROPERTY, SVGConstants.CSS_NONE_VALUE);
- svgp.addCSSClassOrLogError(tooltiphidden);
-
- CSSClass tooltipvisible = new CSSClass(svgp, TOOLTIP_VISIBLE);
- tooltipvisible.setStatement(SVGConstants.CSS_FONT_SIZE_PROPERTY, fontsize);
- tooltipvisible.setStatement(SVGConstants.CSS_FONT_FAMILY_PROPERTY, fontfamily);
- svgp.addCSSClassOrLogError(tooltipvisible);
-
- CSSClass tooltipsticky = new CSSClass(svgp, TOOLTIP_STICKY);
- tooltipsticky.setStatement(SVGConstants.CSS_FONT_SIZE_PROPERTY, fontsize);
- tooltipsticky.setStatement(SVGConstants.CSS_FONT_FAMILY_PROPERTY, fontfamily);
- svgp.addCSSClassOrLogError(tooltipsticky);
-
- // invisible but sensitive area for the tooltip activator
- CSSClass tooltiparea = new CSSClass(svgp, TOOLTIP_AREA);
- tooltiparea.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_RED_VALUE);
- tooltiparea.setStatement(SVGConstants.CSS_STROKE_PROPERTY, SVGConstants.CSS_NONE_VALUE);
- tooltiparea.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, "0");
- tooltiparea.setStatement(SVGConstants.CSS_CURSOR_PROPERTY, SVGConstants.CSS_POINTER_VALUE);
- svgp.addCSSClassOrLogError(tooltiparea);
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<Relation<?>> reps = ResultUtil.filterResults(result, Relation.class);
+ for(Relation<?> rep : reps) {
+ if(DBID.class.isAssignableFrom(rep.getDataTypeInformation().getRestrictionClass())) {
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME_ID, rep, p.getRelation(), this);
+ task.tool = true;
+ task.initDefaultVisibility(false);
+ baseResult.getHierarchy().add(rep, task);
+ baseResult.getHierarchy().add(p, task);
+ }
+ }
+ if(ClassLabel.class.isAssignableFrom(rep.getDataTypeInformation().getRestrictionClass())) {
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME_CLASS, rep, p.getRelation(), this);
+ task.tool = true;
+ task.initDefaultVisibility(false);
+ baseResult.getHierarchy().add(rep, task);
+ baseResult.getHierarchy().add(p, task);
+ }
+ }
+ if(LabelList.class.isAssignableFrom(rep.getDataTypeInformation().getRestrictionClass())) {
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME_LABEL, rep, p.getRelation(), this);
+ task.tool = true;
+ task.initDefaultVisibility(false);
+ baseResult.getHierarchy().add(rep, task);
+ baseResult.getHierarchy().add(p, task);
+ }
+ }
+ if(ExternalID.class.isAssignableFrom(rep.getDataTypeInformation().getRestrictionClass())) {
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME_EID, rep, p.getRelation(), this);
+ task.tool = true;
+ task.initDefaultVisibility(false);
+ baseResult.getHierarchy().add(rep, task);
+ baseResult.getHierarchy().add(p, task);
+ }
+ }
+ }
}
/**
- * Factory
+ * Instance
*
+ * @author Remigius Wojdanowski
* @author Erich Schubert
*
- * @apiviz.stereotype factory
- * @apiviz.uses TooltipStringVisualization oneway - - «create»
+ * @apiviz.has Relation oneway - - visualizes
*/
- public static class Factory extends AbstractVisFactory {
+ public class Instance extends AbstractTooltipVisualization {
+ /**
+ * Number value to visualize
+ */
+ private Relation<?> result;
+
/**
- * Constructor, adhering to
- * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
+ * Font size to use.
*/
- public Factory() {
- super();
+ private double fontsize;
+
+ /**
+ * Constructor.
+ *
+ * @param task Task
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
+ this.result = task.getResult();
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ this.fontsize = 3 * style.getTextSize(StyleLibrary.PLOT);
+ synchronizedRedraw();
}
@Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new TooltipStringVisualization(task);
+ protected Element makeTooltip(DBIDRef id, double x, double y, double dotsize) {
+ final Object data = result.get(id);
+ String label;
+ if(data == null) {
+ label = "null";
+ }
+ else {
+ label = data.toString();
+ }
+ if(label == "" || label == null) {
+ label = "null";
+ }
+ return svgp.svgText(x + dotsize, y + fontsize * 0.07, label);
}
+ /**
+ * Registers the Tooltip-CSS-Class at a SVGPlot.
+ *
+ * @param svgp the SVGPlot to register the Tooltip-CSS-Class.
+ */
@Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<Relation<?>> reps = ResultUtil.filterResults(result, Relation.class);
- for(Relation<?> rep : reps) {
- if(DBID.class.isAssignableFrom(rep.getDataTypeInformation().getRestrictionClass())) {
- Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME_ID, rep, p.getRelation(), this);
- task.put(VisualizationTask.META_TOOL, true);
- task.put(VisualizationTask.META_VISIBLE_DEFAULT, false);
- baseResult.getHierarchy().add(rep, task);
- baseResult.getHierarchy().add(p, task);
- }
- }
- if(ClassLabel.class.isAssignableFrom(rep.getDataTypeInformation().getRestrictionClass())) {
- Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME_CLASS, rep, p.getRelation(), this);
- task.put(VisualizationTask.META_TOOL, true);
- task.put(VisualizationTask.META_VISIBLE_DEFAULT, false);
- baseResult.getHierarchy().add(rep, task);
- baseResult.getHierarchy().add(p, task);
- }
- }
- if(LabelList.class.isAssignableFrom(rep.getDataTypeInformation().getRestrictionClass())) {
- Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME_LABEL, rep, p.getRelation(), this);
- task.put(VisualizationTask.META_TOOL, true);
- task.put(VisualizationTask.META_VISIBLE_DEFAULT, false);
- baseResult.getHierarchy().add(rep, task);
- baseResult.getHierarchy().add(p, task);
- }
- }
- if(ExternalID.class.isAssignableFrom(rep.getDataTypeInformation().getRestrictionClass())) {
- Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME_EID, rep, p.getRelation(), this);
- task.put(VisualizationTask.META_TOOL, true);
- task.put(VisualizationTask.META_VISIBLE_DEFAULT, false);
- baseResult.getHierarchy().add(rep, task);
- baseResult.getHierarchy().add(p, task);
- }
- }
- }
+ protected void setupCSS(SVGPlot svgp) {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ final double fontsize = style.getTextSize(StyleLibrary.PLOT);
+ final String fontfamily = style.getFontFamily(StyleLibrary.PLOT);
+
+ CSSClass tooltiphidden = new CSSClass(svgp, TOOLTIP_HIDDEN);
+ tooltiphidden.setStatement(SVGConstants.CSS_FONT_SIZE_PROPERTY, fontsize);
+ tooltiphidden.setStatement(SVGConstants.CSS_FONT_FAMILY_PROPERTY, fontfamily);
+ tooltiphidden.setStatement(SVGConstants.CSS_DISPLAY_PROPERTY, SVGConstants.CSS_NONE_VALUE);
+ svgp.addCSSClassOrLogError(tooltiphidden);
+
+ CSSClass tooltipvisible = new CSSClass(svgp, TOOLTIP_VISIBLE);
+ tooltipvisible.setStatement(SVGConstants.CSS_FONT_SIZE_PROPERTY, fontsize);
+ tooltipvisible.setStatement(SVGConstants.CSS_FONT_FAMILY_PROPERTY, fontfamily);
+ svgp.addCSSClassOrLogError(tooltipvisible);
+
+ CSSClass tooltipsticky = new CSSClass(svgp, TOOLTIP_STICKY);
+ tooltipsticky.setStatement(SVGConstants.CSS_FONT_SIZE_PROPERTY, fontsize);
+ tooltipsticky.setStatement(SVGConstants.CSS_FONT_FAMILY_PROPERTY, fontfamily);
+ svgp.addCSSClassOrLogError(tooltipsticky);
+
+ // invisible but sensitive area for the tooltip activator
+ CSSClass tooltiparea = new CSSClass(svgp, TOOLTIP_AREA);
+ tooltiparea.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_RED_VALUE);
+ tooltiparea.setStatement(SVGConstants.CSS_STROKE_PROPERTY, SVGConstants.CSS_NONE_VALUE);
+ tooltiparea.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, "0");
+ tooltiparea.setStatement(SVGConstants.CSS_CURSOR_PROPERTY, SVGConstants.CSS_POINTER_VALUE);
+ svgp.addCSSClassOrLogError(tooltiparea);
}
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/ClusterHullVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/ClusterHullVisualization.java
index e1817b1e..ebaf9eb9 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/ClusterHullVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/ClusterHullVisualization.java
@@ -69,207 +69,201 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.AbstractScatter
* @author Robert Rödler
* @author Erich Schubert
*
- * @apiviz.has Clustering oneway - - visualizes
- * @apiviz.uses GrahamScanConvexHull2D
- * @apiviz.uses AlphaShape
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class ClusterHullVisualization extends AbstractScatterplotVisualization {
+public class ClusterHullVisualization extends AbstractVisFactory {
/**
* A short name characterizing this Visualizer.
*/
private static final String NAME = "Cluster Hull Visualization";
/**
- * Generic tags to indicate the type of element. Used in IDs, CSS-Classes etc.
+ * Settings
*/
- public static final String CLUSTERHULL = "cluster-hull";
+ Parameterizer settings;
/**
- * The result we work on
- */
- Clustering<Model> clustering;
-
- /**
- * Alpha value
- */
- double alpha = Double.POSITIVE_INFINITY;
-
- /**
- * Constructor
+ * Constructor.
*
- * @param task VisualizationTask
- * @param alpha Alpha value
+ * @param settings Settings
*/
- public ClusterHullVisualization(VisualizationTask task, double alpha) {
- super(task);
- this.clustering = task.getResult();
- this.alpha = alpha;
- incrementalRedraw();
+ public ClusterHullVisualization(Parameterizer settings) {
+ super();
+ this.settings = settings;
}
@Override
- protected void redraw() {
- // Viewport size, for "relative size" computations
- final CanvasSize viewp = proj.estimateViewport();
- double projarea = viewp.getDiffX() * viewp.getDiffY();
-
- double opacity = 0.25;
-
- Iterator<Cluster<Model>> ci = clustering.getAllClusters().iterator();
-
- for(int cnum = 0; cnum < clustering.getAllClusters().size(); cnum++) {
- Cluster<?> clus = ci.next();
- final DBIDs ids = clus.getIDs();
-
- if(alpha >= Double.POSITIVE_INFINITY) {
- GrahamScanConvexHull2D hull = new GrahamScanConvexHull2D();
-
- for(DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
- double[] projP = proj.fastProjectDataToRenderSpace(rel.get(iter));
- hull.add(new Vector(projP));
- }
- Polygon chres = hull.getHull();
-
- // Plot the convex hull:
- if(chres != null && chres.size() > 1) {
- SVGPath path = new SVGPath(chres);
- // Approximate area (using bounding box)
- double hullarea = SpatialUtil.volume(chres);
- final double relativeArea = (projarea - hullarea) / projarea;
- final double relativeSize = (double) ids.size() / rel.size();
- opacity = Math.sqrt(relativeSize * relativeArea);
-
- Element hulls = path.makeElement(svgp);
- addCSSClasses(svgp, cnum, opacity);
- SVGUtil.addCSSClass(hulls, CLUSTERHULL + cnum);
- layer.appendChild(hulls);
- }
- }
- else {
- ArrayList<Vector> ps = new ArrayList<Vector>(ids.size());
- for(DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
- double[] projP = proj.fastProjectDataToRenderSpace(rel.get(iter));
- ps.add(new Vector(projP));
- }
- List<Polygon> polys = (new AlphaShape(ps, alpha * Projection.SCALE)).compute();
- for(Polygon p : polys) {
- SVGPath path = new SVGPath(p);
- Element hulls = path.makeElement(svgp);
- addCSSClasses(svgp, cnum, 0.5);
- SVGUtil.addCSSClass(hulls, CLUSTERHULL + cnum);
- layer.appendChild(hulls);
- }
- }
- }
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
}
- /**
- * Adds the required CSS-Classes
- *
- * @param svgp SVG-Plot
- */
- private void addCSSClasses(SVGPlot svgp, int clusterID, double opac) {
- ColorLibrary colors = context.getStyleLibrary().getColorSet(StyleLibrary.PLOT);
-
- CSSClass cls = new CSSClass(this, CLUSTERHULL + clusterID);
- cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT));
-
- final String color;
- if(clustering.getAllClusters().size() == 1) {
- color = "black";
- }
- else {
- color = colors.getColor(clusterID);
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ // Find clusterings we can visualize:
+ Collection<Clustering<?>> clusterings = ResultUtil.filterResults(result, Clustering.class);
+ for(Clustering<?> c : clusterings) {
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, c, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_DATA - 1;
+ task.initDefaultVisibility(false);
+ baseResult.getHierarchy().add(c, task);
+ baseResult.getHierarchy().add(p, task);
+ }
}
- cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, color);
- cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, color);
- cls.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, opac);
-
- svgp.addCSSClassOrLogError(cls);
}
/**
- * Factory for visualizers to generate an SVG-Element containing the convex
- * hull or alpha shape of a cluster.
+ * Instance.
*
* @author Robert Rödler
* @author Erich Schubert
*
- * @apiviz.stereotype factory
- * @apiviz.uses ClusterHullVisualization oneway - - «create»
+ * @apiviz.has Clustering oneway - - visualizes
+ * @apiviz.uses GrahamScanConvexHull2D
+ * @apiviz.uses AlphaShape
*/
- public static class Factory extends AbstractVisFactory {
+ public class Instance extends AbstractScatterplotVisualization {
/**
- * Alpha value
+ * Generic tags to indicate the type of element. Used in IDs, CSS-Classes
+ * etc.
*/
- double alpha = Double.POSITIVE_INFINITY;
+ public static final String CLUSTERHULL = "cluster-hull";
/**
- * Constructor.
- *
- * @param alpha Alpha value
+ * The result we work on
*/
- public Factory(double alpha) {
- super();
- this.alpha = alpha;
- }
+ Clustering<Model> clustering;
- @Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new ClusterHullVisualization(task, alpha);
+ /**
+ * Constructor
+ *
+ * @param task VisualizationTask
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
+ this.clustering = task.getResult();
+ incrementalRedraw();
}
@Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- // Find clusterings we can visualize:
- Collection<Clustering<?>> clusterings = ResultUtil.filterResults(result, Clustering.class);
- for(Clustering<?> c : clusterings) {
- Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, c, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA - 1);
- task.put(VisualizationTask.META_VISIBLE_DEFAULT, false);
- baseResult.getHierarchy().add(c, task);
- baseResult.getHierarchy().add(p, task);
+ protected void redraw() {
+ // Viewport size, for "relative size" computations
+ final CanvasSize viewp = proj.estimateViewport();
+ double projarea = viewp.getDiffX() * viewp.getDiffY();
+
+ double opacity = 0.25;
+
+ Iterator<Cluster<Model>> ci = clustering.getAllClusters().iterator();
+
+ for(int cnum = 0; cnum < clustering.getAllClusters().size(); cnum++) {
+ Cluster<?> clus = ci.next();
+ final DBIDs ids = clus.getIDs();
+
+ if(settings.alpha >= Double.POSITIVE_INFINITY) {
+ GrahamScanConvexHull2D hull = new GrahamScanConvexHull2D();
+
+ for(DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
+ double[] projP = proj.fastProjectDataToRenderSpace(rel.get(iter));
+ hull.add(new Vector(projP));
+ }
+ Polygon chres = hull.getHull();
+
+ // Plot the convex hull:
+ if(chres != null && chres.size() > 1) {
+ SVGPath path = new SVGPath(chres);
+ // Approximate area (using bounding box)
+ double hullarea = SpatialUtil.volume(chres);
+ final double relativeArea = (projarea - hullarea) / projarea;
+ final double relativeSize = (double) ids.size() / rel.size();
+ opacity = Math.sqrt(relativeSize * relativeArea);
+
+ Element hulls = path.makeElement(svgp);
+ addCSSClasses(svgp, cnum, opacity);
+ SVGUtil.addCSSClass(hulls, CLUSTERHULL + cnum);
+ layer.appendChild(hulls);
+ }
+ }
+ else {
+ ArrayList<Vector> ps = new ArrayList<Vector>(ids.size());
+ for(DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
+ double[] projP = proj.fastProjectDataToRenderSpace(rel.get(iter));
+ ps.add(new Vector(projP));
+ }
+ List<Polygon> polys = (new AlphaShape(ps, settings.alpha * Projection.SCALE)).compute();
+ for(Polygon p : polys) {
+ SVGPath path = new SVGPath(p);
+ Element hulls = path.makeElement(svgp);
+ addCSSClasses(svgp, cnum, 0.5);
+ SVGUtil.addCSSClass(hulls, CLUSTERHULL + cnum);
+ layer.appendChild(hulls);
+ }
}
}
}
/**
- * Parameterization class.
- *
- * @author Erich Schubert
+ * Adds the required CSS-Classes
*
- * @apiviz.exclude
+ * @param svgp SVG-Plot
*/
- public static class Parameterizer extends AbstractParameterizer {
- /**
- * Alpha-Value for alpha-shapes
- *
- * <p>
- * Key: {@code -hull.alpha}
- * </p>
- */
- public static final OptionID ALPHA_ID = OptionID.getOrCreateOptionID("hull.alpha", "Alpha value for hull drawing (in projected space!).");
-
- /**
- * Alpha value
- */
- double alpha = Double.POSITIVE_INFINITY;
-
- @Override
- protected void makeOptions(Parameterization config) {
- super.makeOptions(config);
- DoubleParameter alphaP = new DoubleParameter(ALPHA_ID, Double.POSITIVE_INFINITY);
- if(config.grab(alphaP)) {
- alpha = alphaP.getValue();
- }
+ private void addCSSClasses(SVGPlot svgp, int clusterID, double opac) {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ ColorLibrary colors = style.getColorSet(StyleLibrary.PLOT);
+
+ CSSClass cls = new CSSClass(this, CLUSTERHULL + clusterID);
+ cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.PLOT));
+
+ final String color;
+ if(clustering.getAllClusters().size() == 1) {
+ color = "black";
+ }
+ else {
+ color = colors.getColor(clusterID);
}
+ cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, color);
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, color);
+ cls.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, opac);
+
+ svgp.addCSSClassOrLogError(cls);
+ }
+ }
+
+ /**
+ * Parameterization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public static class Parameterizer extends AbstractParameterizer {
+ /**
+ * Alpha-Value for alpha-shapes
+ *
+ * <p>
+ * Key: {@code -hull.alpha}
+ * </p>
+ */
+ public static final OptionID ALPHA_ID = new OptionID("hull.alpha", "Alpha value for hull drawing (in projected space!).");
+
+ /**
+ * Alpha value
+ */
+ double alpha = Double.POSITIVE_INFINITY;
- @Override
- protected Factory makeInstance() {
- return new Factory(alpha);
+ @Override
+ protected void makeOptions(Parameterization config) {
+ super.makeOptions(config);
+ DoubleParameter alphaP = new DoubleParameter(ALPHA_ID, Double.POSITIVE_INFINITY);
+ if(config.grab(alphaP)) {
+ alpha = alphaP.doubleValue();
}
}
+
+ @Override
+ protected ClusterHullVisualization makeInstance() {
+ return new ClusterHullVisualization(this);
+ }
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/ClusterMeanVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/ClusterMeanVisualization.java
index 4f14f4ef..c07e5dca 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/ClusterMeanVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/ClusterMeanVisualization.java
@@ -35,7 +35,7 @@ import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.data.model.MeanModel;
import de.lmu.ifi.dbs.elki.data.model.MedoidModel;
import de.lmu.ifi.dbs.elki.data.model.Model;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
@@ -61,238 +61,235 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.AbstractScatter
*
* @author Heidi Kolb
*
- * @apiviz.has MeanModel oneway - - visualizes
- * @apiviz.has MedoidModel oneway - - visualizes
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class ClusterMeanVisualization extends AbstractScatterplotVisualization {
+public class ClusterMeanVisualization extends AbstractVisFactory {
/**
* A short name characterizing this Visualizer.
*/
private static final String NAME = "Cluster Means";
/**
- * CSS class name for center of the means
+ * Settings
*/
- private final static String CSS_MEAN_CENTER = "mean-center";
-
- /**
- * CSS class name for center of the means
- */
- private final static String CSS_MEAN = "mean-marker";
-
- /**
- * CSS class name for center of the means
- */
- private final static String CSS_MEAN_STAR = "mean-star";
-
- /**
- * Clustering to visualize.
- */
- Clustering<Model> clustering;
-
- /**
- * Draw stars
- */
- boolean stars;
+ protected Parameterizer settings;
/**
* Constructor.
*
- * @param task Visualization task
- * @param stars Draw stars
+ * @param settings Settings
*/
- public ClusterMeanVisualization(VisualizationTask task, boolean stars) {
- super(task);
- this.clustering = task.getResult();
- this.stars = stars;
- incrementalRedraw();
+ public ClusterMeanVisualization(Parameterizer settings) {
+ super();
+ this.settings = settings;
}
@Override
- protected void redraw() {
- addCSSClasses(svgp);
-
- MarkerLibrary ml = context.getStyleLibrary().markers();
- double marker_size = context.getStyleLibrary().getSize(StyleLibrary.MARKERPLOT);
-
- Iterator<Cluster<Model>> ci = clustering.getAllClusters().iterator();
- for(int cnum = 0; ci.hasNext(); cnum++) {
- Cluster<Model> clus = ci.next();
- Model model = clus.getModel();
- double[] mean;
- if(model instanceof MeanModel) {
- @SuppressWarnings("unchecked")
- MeanModel<? extends NumberVector<?, ?>> mmodel = (MeanModel<? extends NumberVector<?, ?>>) model;
- mean = proj.fastProjectDataToRenderSpace(mmodel.getMean());
- }
- else if(model instanceof MedoidModel) {
- MedoidModel mmodel = (MedoidModel) model;
- mean = proj.fastProjectDataToRenderSpace(rel.get(mmodel.getMedoid()));
- }
- else {
- continue;
- }
-
- // add a greater Marker for the mean
- Element meanMarker = ml.useMarker(svgp, layer, mean[0], mean[1], cnum, marker_size * 3);
- SVGUtil.setAtt(meanMarker, SVGConstants.SVG_CLASS_ATTRIBUTE, CSS_MEAN);
-
- // Add a fine cross to mark the exact location of the mean.
- Element meanMarkerCenter = svgp.svgLine(mean[0] - .7, mean[1], mean[0] + .7, mean[1]);
- SVGUtil.setAtt(meanMarkerCenter, SVGConstants.SVG_CLASS_ATTRIBUTE, CSS_MEAN_CENTER);
- Element meanMarkerCenter2 = svgp.svgLine(mean[0], mean[1] - .7, mean[0], mean[1] + .7);
- SVGUtil.setAtt(meanMarkerCenter2, SVGConstants.SVG_CLASS_ATTRIBUTE, CSS_MEAN_CENTER);
-
- layer.appendChild(meanMarkerCenter);
- layer.appendChild(meanMarkerCenter2);
-
- if(stars) {
- SVGPath star = new SVGPath();
- for(DBID id : clus.getIDs()) {
- double[] obj = proj.fastProjectDataToRenderSpace(rel.get(id));
- star.moveTo(mean);
- star.drawTo(obj);
- }
- Element stare = star.makeElement(svgp);
- SVGUtil.setCSSClass(stare, CSS_MEAN_STAR + "_" + cnum);
- layer.appendChild(stare);
- }
- }
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
}
- /**
- * Adds the required CSS-Classes
- *
- * @param svgp SVG-Plot
- */
- private void addCSSClasses(SVGPlot svgp) {
- if(!svgp.getCSSClassManager().contains(CSS_MEAN_CENTER)) {
- CSSClass center = new CSSClass(this, CSS_MEAN_CENTER);
- center.setStatement(SVGConstants.CSS_STROKE_PROPERTY, context.getStyleLibrary().getTextColor(StyleLibrary.DEFAULT));
- center.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, context.getStyleLibrary().getLineWidth(StyleLibrary.AXIS_TICK) / 2);
- svgp.addCSSClassOrLogError(center);
- }
- if(!svgp.getCSSClassManager().contains(CSS_MEAN)) {
- CSSClass center = new CSSClass(this, CSS_MEAN);
- center.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, "0.7");
- svgp.addCSSClassOrLogError(center);
- }
- if(stars) {
- ColorLibrary colors = context.getStyleLibrary().getColorSet(StyleLibrary.PLOT);
-
- Iterator<Cluster<Model>> ci = clustering.getAllClusters().iterator();
- for(int cnum = 0; ci.hasNext(); cnum++) {
- ci.next();
- if(!svgp.getCSSClassManager().contains(CSS_MEAN_STAR + "_" + cnum)) {
- CSSClass center = new CSSClass(this, CSS_MEAN_STAR + "_" + cnum);
- center.setStatement(SVGConstants.CSS_STROKE_PROPERTY, colors.getColor(cnum));
- center.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT));
- center.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, "0.7");
- svgp.addCSSClassOrLogError(center);
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ // Find clusterings we can visualize:
+ Collection<Clustering<?>> clusterings = ResultUtil.filterResults(result, Clustering.class);
+ for(Clustering<?> c : clusterings) {
+ if(c.getAllClusters().size() > 0) {
+ // Does the cluster have a model with cluster means?
+ if(testMeanModel(c)) {
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, c, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_DATA + 1;
+ baseResult.getHierarchy().add(c, task);
+ baseResult.getHierarchy().add(p, task);
+ }
}
}
}
}
/**
- * Factory for visualizers to generate an SVG-Element containing a marker for
- * the mean in a KMeans-Clustering
+ * Instance.
*
* @author Heidi Kolb
*
- * @apiviz.stereotype factory
- * @apiviz.uses ClusterMeanVisualization oneway - - «create»
+ * @apiviz.has MeanModel oneway - - visualizes
+ * @apiviz.has MedoidModel oneway - - visualizes
*/
- public static class Factory extends AbstractVisFactory {
+ public class Instance extends AbstractScatterplotVisualization {
/**
- * Option ID for visualization of cluster means.
- *
- * <pre>
- * -cluster.stars
- * </pre>
+ * CSS class name for center of the means
+ */
+ private static final String CSS_MEAN_CENTER = "mean-center";
+
+ /**
+ * CSS class name for center of the means
+ */
+ private static final String CSS_MEAN = "mean-marker";
+
+ /**
+ * CSS class name for center of the means
*/
- public static final OptionID STARS_ID = OptionID.getOrCreateOptionID("cluster.stars", "Visualize mean-based clusters using stars.");
+ private static final String CSS_MEAN_STAR = "mean-star";
/**
- * Draw stars
+ * Clustering to visualize.
*/
- private boolean stars;
+ Clustering<Model> clustering;
/**
* Constructor.
*
- * @param stars Draw stars
+ * @param task Visualization task
*/
- public Factory(boolean stars) {
- super();
- this.stars = stars;
+ public Instance(VisualizationTask task) {
+ super(task);
+ this.clustering = task.getResult();
+ incrementalRedraw();
}
@Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new ClusterMeanVisualization(task, stars);
- }
+ protected void redraw() {
+ addCSSClasses(svgp);
- @Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- // Find clusterings we can visualize:
- Collection<Clustering<?>> clusterings = ResultUtil.filterResults(result, Clustering.class);
- for(Clustering<?> c : clusterings) {
- if(c.getAllClusters().size() > 0) {
- // Does the cluster have a model with cluster means?
- if(testMeanModel(c)) {
- Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, c, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA + 1);
- baseResult.getHierarchy().add(c, task);
- baseResult.getHierarchy().add(p, task);
- }
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ MarkerLibrary ml = style.markers();
+ double marker_size = style.getSize(StyleLibrary.MARKERPLOT);
+
+ Iterator<Cluster<Model>> ci = clustering.getAllClusters().iterator();
+ for(int cnum = 0; ci.hasNext(); cnum++) {
+ Cluster<Model> clus = ci.next();
+ Model model = clus.getModel();
+ double[] mean;
+ if(model instanceof MeanModel) {
+ @SuppressWarnings("unchecked")
+ MeanModel<? extends NumberVector<?>> mmodel = (MeanModel<? extends NumberVector<?>>) model;
+ mean = proj.fastProjectDataToRenderSpace(mmodel.getMean());
+ }
+ else if(model instanceof MedoidModel) {
+ MedoidModel mmodel = (MedoidModel) model;
+ mean = proj.fastProjectDataToRenderSpace(rel.get(mmodel.getMedoid()));
+ }
+ else {
+ continue;
+ }
+
+ // add a greater Marker for the mean
+ Element meanMarker = ml.useMarker(svgp, layer, mean[0], mean[1], cnum, marker_size * 3);
+ SVGUtil.setAtt(meanMarker, SVGConstants.SVG_CLASS_ATTRIBUTE, CSS_MEAN);
+
+ // Add a fine cross to mark the exact location of the mean.
+ Element meanMarkerCenter = svgp.svgLine(mean[0] - .7, mean[1], mean[0] + .7, mean[1]);
+ SVGUtil.setAtt(meanMarkerCenter, SVGConstants.SVG_CLASS_ATTRIBUTE, CSS_MEAN_CENTER);
+ Element meanMarkerCenter2 = svgp.svgLine(mean[0], mean[1] - .7, mean[0], mean[1] + .7);
+ SVGUtil.setAtt(meanMarkerCenter2, SVGConstants.SVG_CLASS_ATTRIBUTE, CSS_MEAN_CENTER);
+
+ layer.appendChild(meanMarkerCenter);
+ layer.appendChild(meanMarkerCenter2);
+
+ if(settings.stars) {
+ SVGPath star = new SVGPath();
+ for(DBIDIter id = clus.getIDs().iter(); id.valid(); id.advance()) {
+ double[] obj = proj.fastProjectDataToRenderSpace(rel.get(id));
+ star.moveTo(mean);
+ star.drawTo(obj);
}
+ Element stare = star.makeElement(svgp);
+ SVGUtil.setCSSClass(stare, CSS_MEAN_STAR + "_" + cnum);
+ layer.appendChild(stare);
}
}
}
/**
- * Test if the given clustering has a mean model.
+ * Adds the required CSS-Classes
*
- * @param c Clustering to inspect
- * @return true when the clustering has a mean or medoid model.
+ * @param svgp SVG-Plot
*/
- private static boolean testMeanModel(Clustering<?> c) {
- Model firstmodel = c.getAllClusters().get(0).getModel();
- if(firstmodel instanceof MeanModel<?>) {
- return true;
+ private void addCSSClasses(SVGPlot svgp) {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ if(!svgp.getCSSClassManager().contains(CSS_MEAN_CENTER)) {
+ CSSClass center = new CSSClass(this, CSS_MEAN_CENTER);
+ center.setStatement(SVGConstants.CSS_STROKE_PROPERTY, style.getTextColor(StyleLibrary.DEFAULT));
+ center.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.AXIS_TICK) * .5);
+ svgp.addCSSClassOrLogError(center);
}
- if(firstmodel instanceof MedoidModel) {
- return true;
+ if(!svgp.getCSSClassManager().contains(CSS_MEAN)) {
+ CSSClass center = new CSSClass(this, CSS_MEAN);
+ center.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, "0.7");
+ svgp.addCSSClassOrLogError(center);
+ }
+ if(settings.stars) {
+ ColorLibrary colors = style.getColorSet(StyleLibrary.PLOT);
+
+ Iterator<Cluster<Model>> ci = clustering.getAllClusters().iterator();
+ for(int cnum = 0; ci.hasNext(); cnum++) {
+ ci.next();
+ if(!svgp.getCSSClassManager().contains(CSS_MEAN_STAR + "_" + cnum)) {
+ CSSClass center = new CSSClass(this, CSS_MEAN_STAR + "_" + cnum);
+ center.setStatement(SVGConstants.CSS_STROKE_PROPERTY, colors.getColor(cnum));
+ center.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.PLOT));
+ center.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, "0.7");
+ svgp.addCSSClassOrLogError(center);
+ }
+ }
}
- return false;
}
+ }
+ /**
+ * Test if the given clustering has a mean model.
+ *
+ * @param c Clustering to inspect
+ * @return true when the clustering has a mean or medoid model.
+ */
+ private static boolean testMeanModel(Clustering<?> c) {
+ Model firstmodel = c.getAllClusters().get(0).getModel();
+ if(firstmodel instanceof MeanModel<?>) {
+ return true;
+ }
+ if(firstmodel instanceof MedoidModel) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Parameterization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public static class Parameterizer extends AbstractParameterizer {
/**
- * Parameterization class.
- *
- * @author Erich Schubert
+ * Option ID for visualization of cluster means.
*
- * @apiviz.exclude
+ * <pre>
+ * -cluster.stars
+ * </pre>
*/
- public static class Parameterizer extends AbstractParameterizer {
- protected boolean stars = false;
-
- @Override
- protected void makeOptions(Parameterization config) {
- super.makeOptions(config);
- Flag starsF = new Flag(STARS_ID);
- if(config.grab(starsF)) {
- stars = starsF.getValue();
- }
- }
+ public static final OptionID STARS_ID = new OptionID("cluster.stars", "Visualize mean-based clusters using stars.");
+
+ /**
+ * Whether to draw cluster stars
+ */
+ protected boolean stars = false;
- @Override
- protected Factory makeInstance() {
- return new Factory(stars);
+ @Override
+ protected void makeOptions(Parameterization config) {
+ super.makeOptions(config);
+ Flag starsF = new Flag(STARS_ID);
+ if(config.grab(starsF)) {
+ stars = starsF.isTrue();
}
}
+
+ @Override
+ protected ClusterMeanVisualization makeInstance() {
+ return new ClusterMeanVisualization(this);
+ }
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/ClusterOrderVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/ClusterOrderVisualization.java
index 0d43875c..bd173e80 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/ClusterOrderVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/ClusterOrderVisualization.java
@@ -45,103 +45,105 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
import de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.AbstractScatterplotVisualization;
/**
- * Cluster order visualizer.
+ * Cluster order visualizer: connect objects via the spanning tree the cluster
+ * order represents.
*
* @author Erich Schubert
*
- * @apiviz.has ClusterOrderResult oneway - - visualizes
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-// TODO: listen for CLUSTER ORDER changes.
-public class ClusterOrderVisualization extends AbstractScatterplotVisualization implements DataStoreListener {
+public class ClusterOrderVisualization extends AbstractVisFactory {
/**
* A short name characterizing this Visualizer.
*/
private static final String NAME = "Predecessor Graph";
/**
- * CSS class name
+ * Constructor, adhering to
+ * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
*/
- private static final String CSSNAME = "predecessor";
-
- /**
- * The result we visualize
- */
- protected ClusterOrderResult<?> result;
-
- public ClusterOrderVisualization(VisualizationTask task) {
- super(task);
- result = task.getResult();
- context.addDataStoreListener(this);
- incrementalRedraw();
+ public ClusterOrderVisualization() {
+ super();
}
@Override
- public void destroy() {
- super.destroy();
- context.removeDataStoreListener(this);
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
}
@Override
- public void redraw() {
- CSSClass cls = new CSSClass(this, CSSNAME);
- context.getStyleLibrary().lines().formatCSSClass(cls, 0, context.getStyleLibrary().getLineWidth(StyleLibrary.CLUSTERORDER));
-
- svgp.addCSSClassOrLogError(cls);
-
- for(ClusterOrderEntry<?> ce : result) {
- DBID thisId = ce.getID();
- DBID prevId = ce.getPredecessorID();
- if(thisId == null || prevId == null) {
- continue;
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<ClusterOrderResult<DoubleDistance>> cos = ResultUtil.filterResults(result, ClusterOrderResult.class);
+ for(ClusterOrderResult<DoubleDistance> co : cos) {
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, co, p.getRelation(), this);
+ task.initDefaultVisibility(false);
+ task.level = VisualizationTask.LEVEL_DATA - 1;
+ baseResult.getHierarchy().add(co, task);
+ baseResult.getHierarchy().add(p, task);
}
- double[] thisVec = proj.fastProjectDataToRenderSpace(rel.get(thisId));
- double[] prevVec = proj.fastProjectDataToRenderSpace(rel.get(prevId));
-
- // FIXME: DO NOT COMMIT
- thisVec[0] = thisVec[0] * 0.95 + prevVec[0] * 0.05;
- thisVec[1] = thisVec[1] * 0.95 + prevVec[1] * 0.05;
-
- Element arrow = svgp.svgLine(prevVec[0], prevVec[1], thisVec[0], thisVec[1]);
- SVGUtil.setCSSClass(arrow, cls.getName());
-
- layer.appendChild(arrow);
}
}
/**
- * Visualize an OPTICS cluster order by drawing connection lines.
+ * Instance
*
* @author Erich Schubert
*
- * @apiviz.stereotype factory
- * @apiviz.uses ClusterOrderVisualization oneway - - «create»
+ * @apiviz.has ClusterOrderResult oneway - - visualizes
*/
- public static class Factory extends AbstractVisFactory {
+ // TODO: listen for CLUSTER ORDER changes.
+ public class Instance extends AbstractScatterplotVisualization implements DataStoreListener {
/**
- * Constructor, adhering to
- * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
+ * CSS class name
*/
- public Factory() {
- super();
+ private static final String CSSNAME = "predecessor";
+
+ /**
+ * The result we visualize
+ */
+ protected ClusterOrderResult<?> result;
+
+ public Instance(VisualizationTask task) {
+ super(task);
+ result = task.getResult();
+ context.addDataStoreListener(this);
+ incrementalRedraw();
}
@Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new ClusterOrderVisualization(task);
+ public void destroy() {
+ super.destroy();
+ context.removeDataStoreListener(this);
}
@Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<ClusterOrderResult<DoubleDistance>> cos = ResultUtil.filterResults(result, ClusterOrderResult.class);
- for(ClusterOrderResult<DoubleDistance> co : cos) {
- Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, co, p.getRelation(), this);
- task.put(VisualizationTask.META_VISIBLE_DEFAULT, false);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA - 1);
- baseResult.getHierarchy().add(co, task);
- baseResult.getHierarchy().add(p, task);
+ public void redraw() {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ CSSClass cls = new CSSClass(this, CSSNAME);
+ style.lines().formatCSSClass(cls, 0, style.getLineWidth(StyleLibrary.CLUSTERORDER));
+
+ svgp.addCSSClassOrLogError(cls);
+
+ for(ClusterOrderEntry<?> ce : result) {
+ DBID thisId = ce.getID();
+ DBID prevId = ce.getPredecessorID();
+ if(thisId == null || prevId == null) {
+ continue;
}
+ double[] thisVec = proj.fastProjectDataToRenderSpace(rel.get(thisId));
+ double[] prevVec = proj.fastProjectDataToRenderSpace(rel.get(prevId));
+
+ // FIXME: DO NOT COMMIT
+ thisVec[0] = thisVec[0] * 0.95 + prevVec[0] * 0.05;
+ thisVec[1] = thisVec[1] * 0.95 + prevVec[1] * 0.05;
+
+ Element arrow = svgp.svgLine(prevVec[0], prevVec[1], thisVec[0], thisVec[1]);
+ SVGUtil.setCSSClass(arrow, cls.getName());
+
+ layer.appendChild(arrow);
}
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/EMClusterVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/EMClusterVisualization.java
index 6070361e..2806812a 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/EMClusterVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/EMClusterVisualization.java
@@ -32,6 +32,7 @@ import org.w3c.dom.Element;
import de.lmu.ifi.dbs.elki.data.Cluster;
import de.lmu.ifi.dbs.elki.data.Clustering;
+import de.lmu.ifi.dbs.elki.data.DoubleVector;
import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.data.model.EMModel;
import de.lmu.ifi.dbs.elki.data.model.MeanModel;
@@ -69,114 +70,199 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.AbstractScatter
*
* @author Robert Rödler
*
- * @apiviz.has EMModel oneway - - visualizes
- * @apiviz.uses GrahamScanConvexHull2D
- *
- * @param <NV> Type of the NumberVector being visualized.
+ * @apiviz.stereotype factory
+ * @apiviz.uses EMClusterVisualization oneway - - «create»
*/
-// TODO: nicer stacking of n-fold hulls
-// TODO: can we find a proper sphere for 3+ dimensions?
-public class EMClusterVisualization<NV extends NumberVector<NV, ?>> extends AbstractScatterplotVisualization {
+public class EMClusterVisualization extends AbstractVisFactory {
/**
* A short name characterizing this Visualizer.
*/
private static final String NAME = "EM Cluster Visualization";
/**
- * Generic tags to indicate the type of element. Used in IDs, CSS-Classes etc.
+ * Constants for quantiles of standard deviation
*/
- public static final String EMBORDER = "EMClusterBorder";
+ final static double[] sigma = new double[] { 0.41, 0.223, 0.047 };
/**
- * The result we work on
+ * Constructor
*/
- Clustering<EMModel<NV>> clustering;
+ public EMClusterVisualization() {
+ super();
+ }
- private static final double KAPPA = SVGHyperSphere.EUCLIDEAN_KAPPA;
+ @Override
+ public Instance<DoubleVector> makeVisualization(VisualizationTask task) {
+ return new Instance<DoubleVector>(task);
+ }
+
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ // Find clusterings we can visualize:
+ Collection<Clustering<?>> clusterings = ResultUtil.filterResults(result, Clustering.class);
+ for(Clustering<?> c : clusterings) {
+ if(c.getAllClusters().size() > 0) {
+ // Does the cluster have a model with cluster means?
+ Clustering<MeanModel<DoubleVector>> mcls = findMeanModel(c);
+ if(mcls != null) {
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, c, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_DATA + 3;
+ baseResult.getHierarchy().add(c, task);
+ baseResult.getHierarchy().add(p, task);
+ }
+ }
+ }
+ }
+ }
/**
- * StyleParameter:
+ * Test if the given clustering has a mean model.
+ *
+ * @param <NV> Vector type
+ * @param c Clustering to inspect
+ * @return the clustering cast to return a mean model, null otherwise.
*/
- private int times = 3;
+ @SuppressWarnings("unchecked")
+ private static <NV extends NumberVector<?>> Clustering<MeanModel<NV>> findMeanModel(Clustering<?> c) {
+ final Model firstModel = c.getAllClusters().get(0).getModel();
+ if(c.getAllClusters().get(0).getModel() instanceof MeanModel<?> && firstModel instanceof EMModel<?>) {
+ return (Clustering<MeanModel<NV>>) c;
+ }
+ return null;
+ }
- private int opacStyle = 1;
+ /**
+ * Instance.
+ *
+ * @author Robert Rödler
+ *
+ * @apiviz.has EMModel oneway - - visualizes
+ * @apiviz.uses GrahamScanConvexHull2D
+ *
+ * @param <NV> Type of the NumberVector being visualized.
+ */
+ // TODO: nicer stacking of n-fold hulls
+ // TODO: can we find a proper sphere for 3+ dimensions?
+ public class Instance<NV extends NumberVector<?>> extends AbstractScatterplotVisualization {
+ /**
+ * Generic tags to indicate the type of element. Used in IDs, CSS-Classes
+ * etc.
+ */
+ public static final String EMBORDER = "EMClusterBorder";
- private int softBorder = 1;
+ /**
+ * The result we work on
+ */
+ Clustering<EMModel<NV>> clustering;
- private int drawStyle = 0;
+ private static final double KAPPA = SVGHyperSphere.EUCLIDEAN_KAPPA;
- final static double[] sigma = new double[] { 0.41, 0.223, 0.047 };
+ /**
+ * StyleParameter:
+ */
+ private int times = 3;
- /**
- * Constructor
- *
- * @param task VisualizationTask
- */
- public EMClusterVisualization(VisualizationTask task) {
- super(task);
- this.clustering = task.getResult();
- incrementalRedraw();
- }
+ private int opacStyle = 1;
- @Override
- protected void redraw() {
- // set styles
- addCSSClasses(svgp);
-
- // PCARunner
- PCARunner<NV> pcarun = ClassGenericsUtil.parameterizeOrAbort(PCARunner.class, new EmptyParameterization());
-
- Iterator<Cluster<EMModel<NV>>> ci = clustering.getAllClusters().iterator();
- for(int cnum = 0; cnum < clustering.getAllClusters().size(); cnum++) {
- Cluster<EMModel<NV>> clus = ci.next();
- DBIDs ids = clus.getIDs();
-
- if(ids.size() > 0) {
- Matrix covmat = clus.getModel().getCovarianceMatrix();
- NV centroid = clus.getModel().getMean();
- Vector cent = new Vector(proj.fastProjectDataToRenderSpace(centroid));
-
- // Compute the eigenvectors
- SortedEigenPairs eps = pcarun.processCovarMatrix(covmat).getEigenPairs();
-
- Vector[] pc = new Vector[eps.size()];
- for(int i = 0; i < eps.size(); i++) {
- EigenPair ep = eps.getEigenPair(i);
- Vector sev = ep.getEigenvector().times(Math.sqrt(ep.getEigenvalue()));
- pc[i] = new Vector(proj.fastProjectRelativeDataToRenderSpace(sev.getArrayRef()));
- }
- if(drawStyle != 0 || eps.size() == 2) {
- drawSphere2D(cnum, cent, pc);
+ private int softBorder = 1;
+
+ private int drawStyle = 0;
+
+ /**
+ * Constructor
+ *
+ * @param task VisualizationTask
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
+ this.clustering = task.getResult();
+ incrementalRedraw();
+ }
+
+ @Override
+ protected void redraw() {
+ // set styles
+ addCSSClasses(svgp);
+
+ // PCARunner
+ PCARunner<NV> pcarun = ClassGenericsUtil.parameterizeOrAbort(PCARunner.class, new EmptyParameterization());
+
+ Iterator<Cluster<EMModel<NV>>> ci = clustering.getAllClusters().iterator();
+ for(int cnum = 0; cnum < clustering.getAllClusters().size(); cnum++) {
+ Cluster<EMModel<NV>> clus = ci.next();
+ DBIDs ids = clus.getIDs();
+
+ if(ids.size() > 0) {
+ Matrix covmat = clus.getModel().getCovarianceMatrix();
+ NV centroid = clus.getModel().getMean();
+ Vector cent = new Vector(proj.fastProjectDataToRenderSpace(centroid));
+
+ // Compute the eigenvectors
+ SortedEigenPairs eps = pcarun.processCovarMatrix(covmat).getEigenPairs();
+
+ Vector[] pc = new Vector[eps.size()];
+ for(int i = 0; i < eps.size(); i++) {
+ EigenPair ep = eps.getEigenPair(i);
+ Vector sev = ep.getEigenvector().times(Math.sqrt(ep.getEigenvalue()));
+ pc[i] = new Vector(proj.fastProjectRelativeDataToRenderSpace(sev.getArrayRef()));
+ }
+ if(drawStyle != 0 || eps.size() == 2) {
+ drawSphere2D(cnum, cent, pc);
+ }
+ else {
+ Polygon chres = makeHullComplex(pc);
+ drawHullLines(cnum, cent, chres);
+ }
}
- else {
- Polygon chres = makeHullComplex(pc);
- drawHullLines(cnum, cent, chres);
+ }
+ }
+
+ protected void drawSphere2D(int cnum, Vector cent, Vector[] pc) {
+ for(int dim1 = 0; dim1 < pc.length - 1; dim1++) {
+ for(int dim2 = dim1 + 1; dim2 < pc.length; dim2++) {
+ for(int i = 1; i <= times; i++) {
+ SVGPath path = new SVGPath();
+
+ Vector direction1 = pc[dim1].times(KAPPA * i);
+ Vector direction2 = pc[dim2].times(KAPPA * i);
+
+ Vector p1 = cent.plusTimes(pc[dim1], i);
+ Vector p2 = cent.plusTimes(pc[dim2], i);
+ Vector p3 = cent.minusTimes(pc[dim1], i);
+ Vector p4 = cent.minusTimes(pc[dim2], i);
+
+ path.moveTo(p1);
+ path.cubicTo(p1.plus(direction2), p2.plus(direction1), p2);
+ path.cubicTo(p2.minus(direction1), p3.plus(direction2), p3);
+ path.cubicTo(p3.minus(direction2), p4.minus(direction1), p4);
+ path.cubicTo(p4.plus(direction1), p1.minus(direction2), p1);
+ path.close();
+
+ Element ellipse = path.makeElement(svgp);
+ SVGUtil.addCSSClass(ellipse, EMBORDER + cnum);
+ if(opacStyle == 1) {
+ CSSClass cls = new CSSClass(null, "temp");
+ double s = (i >= 1 && i <= sigma.length) ? sigma[i - 1] : 0.0;
+ cls.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, s);
+ SVGUtil.setAtt(ellipse, SVGConstants.SVG_STYLE_ATTRIBUTE, cls.inlineCSS());
+ }
+ layer.appendChild(ellipse);
+ }
}
}
}
- }
- protected void drawSphere2D(int cnum, Vector cent, Vector[] pc) {
- for(int dim1 = 0; dim1 < pc.length - 1; dim1++) {
- for(int dim2 = dim1 + 1; dim2 < pc.length; dim2++) {
+ protected void drawHullLines(int cnum, Vector cent, Polygon chres) {
+ if(chres.size() > 1) {
for(int i = 1; i <= times; i++) {
SVGPath path = new SVGPath();
-
- Vector direction1 = pc[dim1].times(KAPPA * i);
- Vector direction2 = pc[dim2].times(KAPPA * i);
-
- Vector p1 = cent.plusTimes(pc[dim1], i);
- Vector p2 = cent.plusTimes(pc[dim2], i);
- Vector p3 = cent.minusTimes(pc[dim1], i);
- Vector p4 = cent.minusTimes(pc[dim2], i);
-
- path.moveTo(p1);
- path.cubicTo(p1.plus(direction2), p2.plus(direction1), p2);
- path.cubicTo(p2.minus(direction1), p3.plus(direction2), p3);
- path.cubicTo(p3.minus(direction2), p4.minus(direction1), p4);
- path.cubicTo(p4.plus(direction1), p1.minus(direction2), p1);
+ for(int p = 0; p < chres.size(); p++) {
+ Vector cur = cent.plusTimes(chres.get(p), i);
+ path.drawTo(cur);
+ }
path.close();
-
Element ellipse = path.makeElement(svgp);
SVGUtil.addCSSClass(ellipse, EMBORDER + cnum);
if(opacStyle == 1) {
@@ -189,18 +275,99 @@ public class EMClusterVisualization<NV extends NumberVector<NV, ?>> extends Abst
}
}
}
- }
- protected void drawHullLines(int cnum, Vector cent, Polygon chres) {
- if(chres.size() > 1) {
+ protected Polygon makeHull(Vector[] pc) {
+ GrahamScanConvexHull2D hull = new GrahamScanConvexHull2D();
+
+ Vector diag = new Vector(0, 0);
+ for(int j = 0; j < pc.length; j++) {
+ hull.add(pc[j]);
+ hull.add(pc[j].times(-1));
+ for(int k = j + 1; k < pc.length; k++) {
+ Vector q = pc[k];
+ Vector ppq = pc[j].plus(q).timesEquals(MathUtil.SQRTHALF);
+ Vector pmq = pc[j].minus(q).timesEquals(MathUtil.SQRTHALF);
+ hull.add(ppq);
+ hull.add(ppq.times(-1));
+ hull.add(pmq);
+ hull.add(pmq.times(-1));
+ }
+ diag.plusEquals(pc[j]);
+ }
+ diag.timesEquals(1.0 / Math.sqrt(pc.length));
+ hull.add(diag);
+ hull.add(diag.times(-1));
+
+ Polygon chres = hull.getHull();
+ return chres;
+ }
+
+ protected Polygon makeHullComplex(Vector[] pc) {
+ GrahamScanConvexHull2D hull = new GrahamScanConvexHull2D();
+
+ Vector diag = new Vector(0, 0);
+ for(int j = 0; j < pc.length; j++) {
+ hull.add(pc[j]);
+ hull.add(pc[j].times(-1));
+ for(int k = j + 1; k < pc.length; k++) {
+ Vector q = pc[k];
+ Vector ppq = pc[j].plus(q).timesEquals(MathUtil.SQRTHALF);
+ Vector pmq = pc[j].minus(q).timesEquals(MathUtil.SQRTHALF);
+ hull.add(ppq);
+ hull.add(ppq.times(-1));
+ hull.add(pmq);
+ hull.add(pmq.times(-1));
+ for(int l = k + 1; l < pc.length; l++) {
+ Vector r = pc[k];
+ Vector ppqpr = ppq.plus(r).timesEquals(Math.sqrt(1 / 3.));
+ Vector pmqpr = pmq.plus(r).timesEquals(Math.sqrt(1 / 3.));
+ Vector ppqmr = ppq.minus(r).timesEquals(Math.sqrt(1 / 3.));
+ Vector pmqmr = pmq.minus(r).timesEquals(Math.sqrt(1 / 3.));
+ hull.add(ppqpr);
+ hull.add(ppqpr.times(-1));
+ hull.add(pmqpr);
+ hull.add(pmqpr.times(-1));
+ hull.add(ppqmr);
+ hull.add(ppqmr.times(-1));
+ hull.add(pmqmr);
+ hull.add(pmqmr.times(-1));
+ }
+ }
+ diag.plusEquals(pc[j]);
+ }
+ diag.timesEquals(1.0 / Math.sqrt(pc.length));
+ hull.add(diag);
+ hull.add(diag.times(-1));
+ Polygon chres = hull.getHull();
+ return chres;
+ }
+
+ protected void drawHullArc(int cnum, Vector cent, Polygon chres) {
for(int i = 1; i <= times; i++) {
SVGPath path = new SVGPath();
+
+ ArrayList<Vector> delta = new ArrayList<Vector>(chres.size());
+ for(int p = 0; p < chres.size(); p++) {
+ Vector prev = chres.get((p - 1 + chres.size()) % chres.size());
+ Vector curr = chres.get(p);
+ Vector next = chres.get((p + 1) % chres.size());
+ Vector d1 = next.minus(curr).normalize();
+ Vector d2 = curr.minus(prev).normalize();
+ delta.add(d1.plus(d2));
+ // delta.add(next.minus(prev));
+ }
+
for(int p = 0; p < chres.size(); p++) {
- Vector cur = cent.plusTimes(chres.get(p), i);
- path.drawTo(cur);
+ Vector cur = cent.plus(chres.get(p));
+ Vector nex = cent.plus(chres.get((p + 1) % chres.size()));
+ Vector dcur = delta.get(p);
+ Vector dnex = delta.get((p + 1) % chres.size());
+ drawArc(path, cent, cur, nex, dcur, dnex, i);
}
path.close();
+
Element ellipse = path.makeElement(svgp);
+
SVGUtil.addCSSClass(ellipse, EMBORDER + cnum);
if(opacStyle == 1) {
CSSClass cls = new CSSClass(null, "temp");
@@ -211,262 +378,98 @@ public class EMClusterVisualization<NV extends NumberVector<NV, ?>> extends Abst
layer.appendChild(ellipse);
}
}
- }
-
- protected Polygon makeHull(Vector[] pc) {
- GrahamScanConvexHull2D hull = new GrahamScanConvexHull2D();
-
- Vector diag = new Vector(0, 0);
- for(int j = 0; j < pc.length; j++) {
- hull.add(pc[j]);
- hull.add(pc[j].times(-1));
- for(int k = j + 1; k < pc.length; k++) {
- Vector q = pc[k];
- Vector ppq = pc[j].plus(q).timesEquals(MathUtil.SQRTHALF);
- Vector pmq = pc[j].minus(q).timesEquals(MathUtil.SQRTHALF);
- hull.add(ppq);
- hull.add(ppq.times(-1));
- hull.add(pmq);
- hull.add(pmq.times(-1));
- }
- diag.plusEquals(pc[j]);
- }
- diag.timesEquals(1.0 / Math.sqrt(pc.length));
- hull.add(diag);
- hull.add(diag.times(-1));
-
- Polygon chres = hull.getHull();
- return chres;
- }
-
- protected Polygon makeHullComplex(Vector[] pc) {
- GrahamScanConvexHull2D hull = new GrahamScanConvexHull2D();
-
- Vector diag = new Vector(0, 0);
- for(int j = 0; j < pc.length; j++) {
- hull.add(pc[j]);
- hull.add(pc[j].times(-1));
- for(int k = j + 1; k < pc.length; k++) {
- Vector q = pc[k];
- Vector ppq = pc[j].plus(q).timesEquals(MathUtil.SQRTHALF);
- Vector pmq = pc[j].minus(q).timesEquals(MathUtil.SQRTHALF);
- hull.add(ppq);
- hull.add(ppq.times(-1));
- hull.add(pmq);
- hull.add(pmq.times(-1));
- for(int l = k + 1; l < pc.length; l++) {
- Vector r = pc[k];
- Vector ppqpr = ppq.plus(r).timesEquals(Math.sqrt(1 / 3.));
- Vector pmqpr = pmq.plus(r).timesEquals(Math.sqrt(1 / 3.));
- Vector ppqmr = ppq.minus(r).timesEquals(Math.sqrt(1 / 3.));
- Vector pmqmr = pmq.minus(r).timesEquals(Math.sqrt(1 / 3.));
- hull.add(ppqpr);
- hull.add(ppqpr.times(-1));
- hull.add(pmqpr);
- hull.add(pmqpr.times(-1));
- hull.add(ppqmr);
- hull.add(ppqmr.times(-1));
- hull.add(pmqmr);
- hull.add(pmqmr.times(-1));
- }
- }
- diag.plusEquals(pc[j]);
- }
- diag.timesEquals(1.0 / Math.sqrt(pc.length));
- hull.add(diag);
- hull.add(diag.times(-1));
- Polygon chres = hull.getHull();
- return chres;
- }
- protected void drawHullArc(int cnum, Vector cent, Polygon chres) {
- for(int i = 1; i <= times; i++) {
- SVGPath path = new SVGPath();
-
- ArrayList<Vector> delta = new ArrayList<Vector>(chres.size());
- for(int p = 0; p < chres.size(); p++) {
- Vector prev = chres.get((p - 1 + chres.size()) % chres.size());
- Vector curr = chres.get(p);
- Vector next = chres.get((p + 1) % chres.size());
- Vector d1 = next.minus(curr).normalize();
- Vector d2 = curr.minus(prev).normalize();
- delta.add(d1.plus(d2));
- // delta.add(next.minus(prev));
- }
-
- for(int p = 0; p < chres.size(); p++) {
- Vector cur = cent.plus(chres.get(p));
- Vector nex = cent.plus(chres.get((p + 1) % chres.size()));
- Vector dcur = delta.get(p);
- Vector dnex = delta.get((p + 1) % chres.size());
- drawArc(path, cent, cur, nex, dcur, dnex, i);
+ /**
+ * Draw an arc to simulate the hyper ellipse.
+ *
+ * @param path Path to draw to
+ * @param cent Center
+ * @param pre Previous point
+ * @param nex Next point
+ * @param scale Scaling factor
+ */
+ private void drawArc(SVGPath path, Vector cent, Vector pre, Vector nex, Vector oPrev, Vector oNext, double scale) {
+ // Delta vectors
+ final Vector rPrev = pre.minus(cent);
+ final Vector rNext = nex.minus(cent);
+ final Vector rPrNe = pre.minus(nex);
+ // Scaled fix points
+ final Vector sPrev = cent.plusTimes(rPrev, scale);
+ final Vector sNext = cent.plusTimes(rNext, scale);
+ // Orthogonal vectors to the relative vectors
+ // final Vector oPrev = new Vector(rPrev.get(1), -rPrev.get(0));
+ // final Vector oNext = new Vector(-rNext.get(1), rNext.get(0));
+
+ // Compute the intersection of rPrev+tp*oPrev and rNext+tn*oNext
+ // rPrNe == rPrev - rNext
+ final double zp = rPrNe.get(0) * oNext.get(1) - rPrNe.get(1) * oNext.get(0);
+ final double zn = rPrNe.get(0) * oPrev.get(1) - rPrNe.get(1) * oPrev.get(0);
+ final double n = oPrev.get(1) * oNext.get(0) - oPrev.get(0) * oNext.get(1);
+ if(n == 0) {
+ LoggingUtil.warning("Parallel?!?");
+ path.drawTo(sNext.get(0), sNext.get(1));
+ return;
}
- path.close();
+ final double tp = Math.abs(zp / n);
+ final double tn = Math.abs(zn / n);
+ // LoggingUtil.warning("tp: "+tp+" tn: "+tn);
- Element ellipse = path.makeElement(svgp);
+ // Guide points
+ final Vector gPrev = sPrev.plusTimes(oPrev, KAPPA * scale * tp);
+ final Vector gNext = sNext.minusTimes(oNext, KAPPA * scale * tn);
- SVGUtil.addCSSClass(ellipse, EMBORDER + cnum);
- if(opacStyle == 1) {
- CSSClass cls = new CSSClass(null, "temp");
- double s = (i >= 1 && i <= sigma.length) ? sigma[i - 1] : 0.0;
- cls.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, s);
- SVGUtil.setAtt(ellipse, SVGConstants.SVG_STYLE_ATTRIBUTE, cls.inlineCSS());
+ if(!path.isStarted()) {
+ path.moveTo(sPrev);
}
- layer.appendChild(ellipse);
+ // path.drawTo(sPrev);
+ // path.drawTo(gPrev);
+ // path.drawTo(gNext);
+ // path.drawTo(sNext));
+ // path.moveTo(sPrev);
+ // if(tp < 0 || tn < 0) {
+ // path.drawTo(sNext);
+ // }
+ // else {
+ path.cubicTo(gPrev, gNext, sNext);
+ // }
}
- }
- /**
- * Draw an arc to simulate the hyper ellipse.
- *
- * @param path Path to draw to
- * @param cent Center
- * @param pre Previous point
- * @param nex Next point
- * @param scale Scaling factor
- */
- private void drawArc(SVGPath path, Vector cent, Vector pre, Vector nex, Vector oPrev, Vector oNext, double scale) {
- // Delta vectors
- final Vector rPrev = pre.minus(cent);
- final Vector rNext = nex.minus(cent);
- final Vector rPrNe = pre.minus(nex);
- // Scaled fix points
- final Vector sPrev = cent.plusTimes(rPrev, scale);
- final Vector sNext = cent.plusTimes(rNext, scale);
- // Orthogonal vectors to the relative vectors
- // final Vector oPrev = new Vector(rPrev.get(1), -rPrev.get(0));
- // final Vector oNext = new Vector(-rNext.get(1), rNext.get(0));
-
- // Compute the intersection of rPrev+tp*oPrev and rNext+tn*oNext
- // rPrNe == rPrev - rNext
- final double zp = rPrNe.get(0) * oNext.get(1) - rPrNe.get(1) * oNext.get(0);
- final double zn = rPrNe.get(0) * oPrev.get(1) - rPrNe.get(1) * oPrev.get(0);
- final double n = oPrev.get(1) * oNext.get(0) - oPrev.get(0) * oNext.get(1);
- if(n == 0) {
- LoggingUtil.warning("Parallel?!?");
- path.drawTo(sNext.get(0), sNext.get(1));
- return;
- }
- final double tp = Math.abs(zp / n);
- final double tn = Math.abs(zn / n);
- // LoggingUtil.warning("tp: "+tp+" tn: "+tn);
-
- // Guide points
- final Vector gPrev = sPrev.plusTimes(oPrev, KAPPA * scale * tp);
- final Vector gNext = sNext.minusTimes(oNext, KAPPA * scale * tn);
-
- if(!path.isStarted()) {
- path.moveTo(sPrev);
- }
- // path.drawTo(sPrev);
- // path.drawTo(gPrev);
- // path.drawTo(gNext);
- // path.drawTo(sNext));
- // path.moveTo(sPrev);
- // if(tp < 0 || tn < 0) {
- // path.drawTo(sNext);
- // }
- // else {
- path.cubicTo(gPrev, gNext, sNext);
- // }
- }
-
- /**
- * Adds the required CSS-Classes
- *
- * @param svgp SVG-Plot
- */
- private void addCSSClasses(SVGPlot svgp) {
- if(!svgp.getCSSClassManager().contains(EMBORDER)) {
- ColorLibrary colors = context.getStyleLibrary().getColorSet(StyleLibrary.PLOT);
- String color;
- int clusterID = 0;
-
- for(@SuppressWarnings("unused")
- Cluster<?> cluster : clustering.getAllClusters()) {
- CSSClass cls = new CSSClass(this, EMBORDER + clusterID);
- cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT) / 2);
-
- if(clustering.getAllClusters().size() == 1) {
- color = "black";
- }
- else {
- color = colors.getColor(clusterID);
- }
- if(softBorder == 0) {
- cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, color);
- }
- cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, color);
- cls.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, 0.15);
-
- svgp.addCSSClassOrLogError(cls);
- if(opacStyle == 0) {
- break;
- }
- clusterID++;
- }
- }
- }
-
- /**
- * Visualizer for generating SVG-Elements containing ellipses for first,
- * second and third standard deviation
- *
- * @author Robert Rödler
- *
- * @apiviz.stereotype factory
- * @apiviz.uses EMClusterVisualization oneway - - «create»
- *
- * @param <NV> Type of the NumberVector being visualized.
- */
- public static class Factory<NV extends NumberVector<NV, ?>> extends AbstractVisFactory {
/**
- * Constructor
+ * Adds the required CSS-Classes
+ *
+ * @param svgp SVG-Plot
*/
- public Factory() {
- super();
- }
-
- @Override
- public EMClusterVisualization<NV> makeVisualization(VisualizationTask task) {
- return new EMClusterVisualization<NV>(task);
- }
+ private void addCSSClasses(SVGPlot svgp) {
+ if(!svgp.getCSSClassManager().contains(EMBORDER)) {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ ColorLibrary colors = style.getColorSet(StyleLibrary.PLOT);
+ String color;
+ int clusterID = 0;
+
+ for(@SuppressWarnings("unused")
+ Cluster<?> cluster : clustering.getAllClusters()) {
+ CSSClass cls = new CSSClass(this, EMBORDER + clusterID);
+ cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.PLOT) * .5);
+
+ if(clustering.getAllClusters().size() == 1) {
+ color = "black";
+ }
+ else {
+ color = colors.getColor(clusterID);
+ }
+ if(softBorder == 0) {
+ cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, color);
+ }
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, color);
+ cls.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, 0.15);
- @Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- // Find clusterings we can visualize:
- Collection<Clustering<?>> clusterings = ResultUtil.filterResults(result, Clustering.class);
- for(Clustering<?> c : clusterings) {
- if(c.getAllClusters().size() > 0) {
- // Does the cluster have a model with cluster means?
- Clustering<MeanModel<NV>> mcls = findMeanModel(c);
- if(mcls != null) {
- Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, c, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA + 3);
- baseResult.getHierarchy().add(c, task);
- baseResult.getHierarchy().add(p, task);
- }
+ svgp.addCSSClassOrLogError(cls);
+ if(opacStyle == 0) {
+ break;
}
+ clusterID++;
}
}
}
-
- /**
- * Test if the given clustering has a mean model.
- *
- * @param <NV> Vector type
- * @param c Clustering to inspect
- * @return the clustering cast to return a mean model, null otherwise.
- */
- @SuppressWarnings("unchecked")
- private static <NV extends NumberVector<NV, ?>> Clustering<MeanModel<NV>> findMeanModel(Clustering<?> c) {
- final Model firstModel = c.getAllClusters().get(0).getModel();
- if(c.getAllClusters().get(0).getModel() instanceof MeanModel<?> && firstModel instanceof EMModel<?>) {
- return (Clustering<MeanModel<NV>>) c;
- }
- return null;
- }
}
-}
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/VoronoiVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/VoronoiVisualization.java
index d6ce810c..7ae28f42 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/VoronoiVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/VoronoiVisualization.java
@@ -33,16 +33,17 @@ import org.w3c.dom.Element;
import de.lmu.ifi.dbs.elki.data.Cluster;
import de.lmu.ifi.dbs.elki.data.Clustering;
import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.data.model.KMeansModel;
import de.lmu.ifi.dbs.elki.data.model.MeanModel;
import de.lmu.ifi.dbs.elki.data.model.MedoidModel;
import de.lmu.ifi.dbs.elki.data.model.Model;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.math.geometry.SweepHullDelaunay2D;
import de.lmu.ifi.dbs.elki.math.geometry.SweepHullDelaunay2D.Triangle;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector;
import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
@@ -65,13 +66,10 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.AbstractScatter
* See also: {@link de.lmu.ifi.dbs.elki.algorithm.clustering.kmeans.KMeansLloyd
* KMeans clustering}
*
- * @author Robert Rödler
- * @author Erich Schubert
- *
- * @apiviz.has MeanModel oneway - - visualizes
- * @apiviz.has MedoidModel oneway - - visualizes
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class VoronoiVisualization extends AbstractScatterplotVisualization {
+public class VoronoiVisualization extends AbstractVisFactory {
/**
* A short name characterizing this Visualizer.
*/
@@ -90,238 +88,240 @@ public class VoronoiVisualization extends AbstractScatterplotVisualization {
* @apiviz.exclude
*/
public static enum Mode {
- VORONOI, DELAUNAY, V_AND_D
+ /**
+ * Draw Voronoi cells.
+ */
+ VORONOI,
+ /**
+ * Draw Delaunay triangulation.
+ */
+ DELAUNAY,
+ /**
+ * Draw both Delaunay and Voronoi.
+ */
+ V_AND_D
}
/**
- * The result we work on
+ * Settings.
*/
- Clustering<Model> clustering;
+ private Parameterizer settings;
/**
- * The Voronoi diagram
- */
- Element voronoi;
-
- /**
- * Active drawing mode.
- */
- private Mode mode;
-
- /**
- * Constructor
+ * Constructor.
*
- * @param task VisualizationTask
- * @param mode Drawing mode
+ * @param settings Drawing mode
*/
- public VoronoiVisualization(VisualizationTask task, Mode mode) {
- super(task);
- this.clustering = task.getResult();
- this.mode = mode;
- incrementalRedraw();
+ public VoronoiVisualization(Parameterizer settings) {
+ super();
+ this.settings = settings;
}
@Override
- protected void redraw() {
- addCSSClasses(svgp);
- final List<Cluster<Model>> clusters = clustering.getAllClusters();
-
- if(clusters.size() < 2) {
- return;
- }
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
+ }
- // Collect cluster means
- if(clusters.size() == 2) {
- ArrayList<double[]> means = new ArrayList<double[]>(clusters.size());
- {
- for(Cluster<Model> clus : clusters) {
- Model model = clus.getModel();
- double[] mean;
- if(model instanceof MeanModel) {
- @SuppressWarnings("unchecked")
- MeanModel<? extends NumberVector<?, ?>> mmodel = (MeanModel<? extends NumberVector<?, ?>>) model;
- mean = proj.fastProjectDataToRenderSpace(mmodel.getMean());
- }
- else if(model instanceof MedoidModel) {
- MedoidModel mmodel = (MedoidModel) model;
- mean = proj.fastProjectDataToRenderSpace(rel.get(mmodel.getMedoid()));
- }
- else {
- continue;
- }
- means.add(mean);
- }
- }
- if(mode == Mode.VORONOI || mode == Mode.V_AND_D) {
- Element path = VoronoiDraw.drawFakeVoronoi(proj, means).makeElement(svgp);
- SVGUtil.addCSSClass(path, KMEANSBORDER);
- layer.appendChild(path);
- }
- if(mode == Mode.DELAUNAY || mode == Mode.V_AND_D) {
- Element path = new SVGPath(means.get(0)).drawTo(means.get(1)).makeElement(svgp);
- SVGUtil.addCSSClass(path, KMEANSBORDER);
- layer.appendChild(path);
- }
- }
- else {
- ArrayList<Vector> vmeans = new ArrayList<Vector>(clusters.size());
- ArrayList<double[]> means = new ArrayList<double[]>(clusters.size());
- {
- for(Cluster<Model> clus : clusters) {
- Model model = clus.getModel();
- Vector mean;
- if(model instanceof MeanModel) {
- @SuppressWarnings("unchecked")
- MeanModel<? extends NumberVector<?, ?>> mmodel = (MeanModel<? extends NumberVector<?, ?>>) model;
- mean = mmodel.getMean().getColumnVector();
- }
- else if(model instanceof MedoidModel) {
- MedoidModel mmodel = (MedoidModel) model;
- mean = rel.get(mmodel.getMedoid()).getColumnVector();
- }
- else {
- continue;
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ // Find clusterings we can visualize:
+ Collection<Clustering<?>> clusterings = ResultUtil.filterResults(result, Clustering.class);
+ for (Clustering<?> c : clusterings) {
+ if (c.getAllClusters().size() > 0) {
+ // Does the cluster have a model with cluster means?
+ if (testMeanModel(c)) {
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
+ for (ScatterPlotProjector<?> p : ps) {
+ if (RelationUtil.dimensionality(p.getRelation()) == 2) {
+ final VisualizationTask task = new VisualizationTask(NAME, c, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_DATA + 3;
+ baseResult.getHierarchy().add(p, task);
+ baseResult.getHierarchy().add(c, task);
+ }
}
- vmeans.add(mean);
- means.add(mean.getArrayRef());
}
}
- // Compute Delaunay Triangulation
- ArrayList<Triangle> delaunay = new SweepHullDelaunay2D(vmeans).getDelaunay();
- if(mode == Mode.VORONOI || mode == Mode.V_AND_D) {
- Element path = VoronoiDraw.drawVoronoi(proj, delaunay, means).makeElement(svgp);
- SVGUtil.addCSSClass(path, KMEANSBORDER);
- layer.appendChild(path);
- }
- if(mode == Mode.DELAUNAY || mode == Mode.V_AND_D) {
- Element path = VoronoiDraw.drawDelaunay(proj, delaunay, means).makeElement(svgp);
- SVGUtil.addCSSClass(path, KMEANSBORDER);
- layer.appendChild(path);
- }
}
}
/**
- * Adds the required CSS-Classes
+ * Test if the given clustering has a mean model.
*
- * @param svgp SVG-Plot
+ * @param c Clustering to inspect
+ * @return true when the clustering has a mean or medoid model.
*/
- private void addCSSClasses(SVGPlot svgp) {
- // Class for the distance markers
- if(!svgp.getCSSClassManager().contains(KMEANSBORDER)) {
- CSSClass cls = new CSSClass(this, KMEANSBORDER);
- cls = new CSSClass(this, KMEANSBORDER);
- cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, SVGConstants.CSS_BLACK_VALUE);
- cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT) * .5);
- cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
- cls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
- cls.setStatement(SVGConstants.CSS_STROKE_LINEJOIN_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
- svgp.addCSSClassOrLogError(cls);
+ private static boolean testMeanModel(Clustering<?> c) {
+ Model firstmodel = c.getAllClusters().get(0).getModel();
+ if (firstmodel instanceof KMeansModel<?>) {
+ return true;
+ }
+ if (firstmodel instanceof MedoidModel) {
+ return true;
}
+ return false;
}
/**
- * Factory for visualizers to generate an SVG-Element containing the lines
- * between kMeans clusters
+ * Instance.
*
* @author Robert Rödler
* @author Erich Schubert
*
- * @apiviz.stereotype factory
- * @apiviz.uses VoronoiVisualization oneway - - «create»
+ * @apiviz.has MeanModel oneway - - visualizes
+ * @apiviz.has MedoidModel oneway - - visualizes
*/
- public static class Factory extends AbstractVisFactory {
+ public class Instance extends AbstractScatterplotVisualization {
/**
- * Mode for drawing: Voronoi, Delaunay, both
- *
- * <p>
- * Key: {@code -voronoi.mode}
- * </p>
+ * The result we work on.
*/
- public static final OptionID MODE_ID = OptionID.getOrCreateOptionID("voronoi.mode", "Mode for drawing the voronoi cells (and/or delaunay triangulation)");
+ Clustering<Model> clustering;
/**
- * Drawing mode
+ * The Voronoi diagram.
*/
- private Mode mode;
+ Element voronoi;
/**
- * Constructor
+ * Constructor.
*
- * @param mode Drawing mode
+ * @param task VisualizationTask
*/
- public Factory(Mode mode) {
- super();
- this.mode = mode;
+ public Instance(VisualizationTask task) {
+ super(task);
+ this.clustering = task.getResult();
+ incrementalRedraw();
}
@Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new VoronoiVisualization(task, mode);
- }
+ protected void redraw() {
+ addCSSClasses(svgp);
+ final List<Cluster<Model>> clusters = clustering.getAllClusters();
- @Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- // Find clusterings we can visualize:
- Collection<Clustering<?>> clusterings = ResultUtil.filterResults(result, Clustering.class);
- for(Clustering<?> c : clusterings) {
- if(c.getAllClusters().size() > 0) {
- // Does the cluster have a model with cluster means?
- if(testMeanModel(c)) {
- Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : ps) {
- if(DatabaseUtil.dimensionality(p.getRelation()) == 2) {
- final VisualizationTask task = new VisualizationTask(NAME, c, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA + 3);
- baseResult.getHierarchy().add(p, task);
- baseResult.getHierarchy().add(c, task);
- }
+ if (clusters.size() < 2) {
+ return;
+ }
+
+ // Collect cluster means
+ if (clusters.size() == 2) {
+ ArrayList<double[]> means = new ArrayList<double[]>(clusters.size());
+ {
+ for (Cluster<Model> clus : clusters) {
+ Model model = clus.getModel();
+ double[] mean;
+ if (model instanceof MeanModel) {
+ @SuppressWarnings("unchecked")
+ MeanModel<? extends NumberVector<?>> mmodel = (MeanModel<? extends NumberVector<?>>) model;
+ mean = proj.fastProjectDataToRenderSpace(mmodel.getMean());
+ } else if (model instanceof MedoidModel) {
+ MedoidModel mmodel = (MedoidModel) model;
+ mean = proj.fastProjectDataToRenderSpace(rel.get(mmodel.getMedoid()));
+ } else {
+ continue;
}
+ means.add(mean);
}
}
+ if (settings.mode == Mode.VORONOI || settings.mode == Mode.V_AND_D) {
+ Element path = VoronoiDraw.drawFakeVoronoi(proj, means).makeElement(svgp);
+ SVGUtil.addCSSClass(path, KMEANSBORDER);
+ layer.appendChild(path);
+ }
+ if (settings.mode == Mode.DELAUNAY || settings.mode == Mode.V_AND_D) {
+ Element path = new SVGPath(means.get(0)).drawTo(means.get(1)).makeElement(svgp);
+ SVGUtil.addCSSClass(path, KMEANSBORDER);
+ layer.appendChild(path);
+ }
+ } else {
+ ArrayList<Vector> vmeans = new ArrayList<Vector>(clusters.size());
+ ArrayList<double[]> means = new ArrayList<double[]>(clusters.size());
+ {
+ for (Cluster<Model> clus : clusters) {
+ Model model = clus.getModel();
+ Vector mean;
+ if (model instanceof MeanModel) {
+ @SuppressWarnings("unchecked")
+ MeanModel<? extends NumberVector<?>> mmodel = (MeanModel<? extends NumberVector<?>>) model;
+ mean = mmodel.getMean().getColumnVector();
+ } else if (model instanceof MedoidModel) {
+ MedoidModel mmodel = (MedoidModel) model;
+ mean = rel.get(mmodel.getMedoid()).getColumnVector();
+ } else {
+ continue;
+ }
+ vmeans.add(mean);
+ means.add(mean.getArrayRef());
+ }
+ }
+ // Compute Delaunay Triangulation
+ ArrayList<Triangle> delaunay = new SweepHullDelaunay2D(vmeans).getDelaunay();
+ if (settings.mode == Mode.VORONOI || settings.mode == Mode.V_AND_D) {
+ Element path = VoronoiDraw.drawVoronoi(proj, delaunay, means).makeElement(svgp);
+ SVGUtil.addCSSClass(path, KMEANSBORDER);
+ layer.appendChild(path);
+ }
+ if (settings.mode == Mode.DELAUNAY || settings.mode == Mode.V_AND_D) {
+ Element path = VoronoiDraw.drawDelaunay(proj, delaunay, means).makeElement(svgp);
+ SVGUtil.addCSSClass(path, KMEANSBORDER);
+ layer.appendChild(path);
+ }
}
}
/**
- * Test if the given clustering has a mean model.
+ * Adds the required CSS-Classes.
*
- * @param c Clustering to inspect
- * @return true when the clustering has a mean or medoid model.
+ * @param svgp SVG-Plot
*/
- private static boolean testMeanModel(Clustering<?> c) {
- Model firstmodel = c.getAllClusters().get(0).getModel();
- if(firstmodel instanceof MeanModel<?>) {
- return true;
+ private void addCSSClasses(SVGPlot svgp) {
+ // Class for the distance markers
+ if (!svgp.getCSSClassManager().contains(KMEANSBORDER)) {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ CSSClass cls = new CSSClass(this, KMEANSBORDER);
+ cls = new CSSClass(this, KMEANSBORDER);
+ cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, SVGConstants.CSS_BLACK_VALUE);
+ cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.PLOT) * .5);
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
+ cls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+ cls.setStatement(SVGConstants.CSS_STROKE_LINEJOIN_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+ svgp.addCSSClassOrLogError(cls);
}
- if(firstmodel instanceof MedoidModel) {
- return true;
- }
- return false;
}
+ }
+ /**
+ * Parameterization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public static class Parameterizer extends AbstractParameterizer {
/**
- * Parameterization class.
+ * Mode for drawing: Voronoi, Delaunay, both.
*
- * @author Erich Schubert
- *
- * @apiviz.exclude
+ * <p>
+ * Key: {@code -voronoi.mode}
+ * </p>
*/
- public static class Parameterizer extends AbstractParameterizer {
- protected Mode mode;
+ public static final OptionID MODE_ID = new OptionID("voronoi.mode", "Mode for drawing the voronoi cells (and/or delaunay triangulation)");
- @Override
- protected void makeOptions(Parameterization config) {
- super.makeOptions(config);
- EnumParameter<Mode> modeP = new EnumParameter<Mode>(MODE_ID, Mode.class, Mode.VORONOI);
- if(config.grab(modeP)) {
- mode = modeP.getValue();
- }
- }
+ /**
+ * Drawing mode.
+ */
+ protected Mode mode;
- @Override
- protected Factory makeInstance() {
- return new Factory(mode);
+ @Override
+ protected void makeOptions(Parameterization config) {
+ super.makeOptions(config);
+ EnumParameter<Mode> modeP = new EnumParameter<Mode>(MODE_ID, Mode.class, Mode.VORONOI);
+ if (config.grab(modeP)) {
+ mode = modeP.getValue();
}
}
+
+ @Override
+ protected VoronoiVisualization makeInstance() {
+ return new VoronoiVisualization(this);
+ }
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/density/DensityEstimationOverlay.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/density/DensityEstimationOverlay.java
index c07bc571..96658910 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/density/DensityEstimationOverlay.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/density/DensityEstimationOverlay.java
@@ -52,184 +52,184 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.AbstractScatter
* <em>in the projection, not the actual data!</em>
*
* @author Erich Schubert
+ *
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-// TODO: make parameterizable, in particular color map, kernel bandwidth and
-// kernel function
-public class DensityEstimationOverlay extends AbstractScatterplotVisualization {
+public class DensityEstimationOverlay extends AbstractVisFactory {
/**
* A short name characterizing this Visualizer.
*/
private static final String NAME = "Density estimation overlay";
/**
- * Density map resolution
+ * Constructor, adhering to
+ * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
*/
- private int resolution = 500;
+ public DensityEstimationOverlay() {
+ super();
+ }
- /**
- * The actual image
- */
- private BufferedImage img = null;
+ @Override
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
+ }
+
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(result, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, p.getRelation(), p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_DATA + 1;
+ task.initDefaultVisibility(false);
+ baseResult.getHierarchy().add(p, task);
+ }
+ }
/**
- * Constructor.
+ * Instance for a particular data set.
*
- * @param task Task
+ * @author Erich Schubert
*/
- public DensityEstimationOverlay(VisualizationTask task) {
- super(task);
- incrementalRedraw();
- }
+ // TODO: make parameterizable, in particular color map, kernel bandwidth and
+ // kernel function
+ public class Instance extends AbstractScatterplotVisualization {
+ /**
+ * Density map resolution
+ */
+ private int resolution = 500;
- @Override
- protected void redraw() {
- if(img == null) {
- renderImage();
+ /**
+ * The actual image
+ */
+ private BufferedImage img = null;
+
+ /**
+ * Constructor.
+ *
+ * @param task Task
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
+ incrementalRedraw();
}
- CanvasSize canvas = proj.estimateViewport();
- String imguri = ThumbnailRegistryEntry.INTERNAL_PREFIX + ThumbnailRegistryEntry.registerImage(img);
- Element itag = svgp.svgElement(SVGConstants.SVG_IMAGE_TAG);
- SVGUtil.setAtt(itag, SVGConstants.SVG_IMAGE_RENDERING_ATTRIBUTE, SVGConstants.SVG_OPTIMIZE_SPEED_VALUE);
- SVGUtil.setAtt(itag, SVGConstants.SVG_X_ATTRIBUTE, canvas.minx);
- SVGUtil.setAtt(itag, SVGConstants.SVG_Y_ATTRIBUTE, canvas.miny);
- SVGUtil.setAtt(itag, SVGConstants.SVG_WIDTH_ATTRIBUTE, canvas.maxx - canvas.minx);
- SVGUtil.setAtt(itag, SVGConstants.SVG_HEIGHT_ATTRIBUTE, canvas.maxy - canvas.miny);
- SVGUtil.setAtt(itag, SVGConstants.SVG_STYLE_ATTRIBUTE, SVGConstants.CSS_OPACITY_PROPERTY + ": .5");
- itag.setAttributeNS(SVGConstants.XLINK_NAMESPACE_URI, SVGConstants.XLINK_HREF_QNAME, imguri);
-
- layer.appendChild(itag);
- }
+ @Override
+ protected void redraw() {
+ if(img == null) {
+ renderImage();
+ }
- @Reference(authors = "D. W. Scott", title = "Multivariate density estimation", booktitle = "Multivariate Density Estimation: Theory, Practice, and Visualization", url = "http://dx.doi.org/10.1002/9780470316849.fmatter")
- private double[] initializeBandwidth(double[][] data) {
- MeanVariance mv0 = new MeanVariance();
- MeanVariance mv1 = new MeanVariance();
- // For Kernel bandwidth.
- for(double[] projected : data) {
- mv0.put(projected[0]);
- mv1.put(projected[1]);
+ CanvasSize canvas = proj.estimateViewport();
+ String imguri = ThumbnailRegistryEntry.INTERNAL_PREFIX + ThumbnailRegistryEntry.registerImage(img);
+ Element itag = svgp.svgElement(SVGConstants.SVG_IMAGE_TAG);
+ SVGUtil.setAtt(itag, SVGConstants.SVG_IMAGE_RENDERING_ATTRIBUTE, SVGConstants.SVG_OPTIMIZE_SPEED_VALUE);
+ SVGUtil.setAtt(itag, SVGConstants.SVG_X_ATTRIBUTE, canvas.minx);
+ SVGUtil.setAtt(itag, SVGConstants.SVG_Y_ATTRIBUTE, canvas.miny);
+ SVGUtil.setAtt(itag, SVGConstants.SVG_WIDTH_ATTRIBUTE, canvas.maxx - canvas.minx);
+ SVGUtil.setAtt(itag, SVGConstants.SVG_HEIGHT_ATTRIBUTE, canvas.maxy - canvas.miny);
+ SVGUtil.setAtt(itag, SVGConstants.SVG_STYLE_ATTRIBUTE, SVGConstants.CSS_OPACITY_PROPERTY + ": .5");
+ itag.setAttributeNS(SVGConstants.XLINK_NAMESPACE_URI, SVGConstants.XLINK_HREF_QNAME, imguri);
+
+ layer.appendChild(itag);
}
- // Set bandwidths according to Scott's rule:
- // Note: in projected space, d=2.
- double[] bandwidth = new double[2];
- bandwidth[0] = MathUtil.SQRT5 * mv0.getSampleStddev() * Math.pow(rel.size(), -1 / 6.);
- bandwidth[1] = MathUtil.SQRT5 * mv1.getSampleStddev() * Math.pow(rel.size(), -1 / 6.);
- return bandwidth;
- }
- private void renderImage() {
- // TODO: SAMPLE? Do region queries?
- // Project the data just once, keep a copy.
- double[][] data = new double[rel.size()][];
- {
- int i = 0;
- for(DBIDIter iditer = rel.iterDBIDs(); iditer.valid(); iditer.advance()) {
- data[i] = proj.fastProjectDataToRenderSpace(rel.get(iditer));
- i++;
+ @Reference(authors = "D. W. Scott", title = "Multivariate density estimation", booktitle = "Multivariate Density Estimation: Theory, Practice, and Visualization", url = "http://dx.doi.org/10.1002/9780470316849.fmatter")
+ private double[] initializeBandwidth(double[][] data) {
+ MeanVariance mv0 = new MeanVariance();
+ MeanVariance mv1 = new MeanVariance();
+ // For Kernel bandwidth.
+ for(double[] projected : data) {
+ mv0.put(projected[0]);
+ mv1.put(projected[1]);
}
+ // Set bandwidths according to Scott's rule:
+ // Note: in projected space, d=2.
+ double[] bandwidth = new double[2];
+ bandwidth[0] = MathUtil.SQRT5 * mv0.getSampleStddev() * Math.pow(rel.size(), -1 / 6.);
+ bandwidth[1] = MathUtil.SQRT5 * mv1.getSampleStddev() * Math.pow(rel.size(), -1 / 6.);
+ return bandwidth;
}
- double[] bandwidth = initializeBandwidth(data);
- // Compare by first component
- Comparator<double[]> comp0 = new Comparator<double[]>() {
- @Override
- public int compare(double[] o1, double[] o2) {
- return Double.compare(o1[0], o2[0]);
- }
- };
- // Compare by second component
- Comparator<double[]> comp1 = new Comparator<double[]>() {
- @Override
- public int compare(double[] o1, double[] o2) {
- return Double.compare(o1[1], o2[1]);
+
+ private void renderImage() {
+ // TODO: SAMPLE? Do region queries?
+ // Project the data just once, keep a copy.
+ double[][] data = new double[rel.size()][];
+ {
+ int i = 0;
+ for(DBIDIter iditer = rel.iterDBIDs(); iditer.valid(); iditer.advance()) {
+ data[i] = proj.fastProjectDataToRenderSpace(rel.get(iditer));
+ i++;
+ }
}
- };
- // TODO: choose comparator order based on smaller bandwidth?
- Arrays.sort(data, comp0);
-
- CanvasSize canvas = proj.estimateViewport();
- double min0 = canvas.minx, max0 = canvas.maxx, ste0 = (max0 - min0) / resolution;
- double min1 = canvas.miny, max1 = canvas.maxy, ste1 = (max1 - min1) / resolution;
-
- double kernf = 9. / (16 * bandwidth[0] * bandwidth[1]);
- double maxdens = 0.0;
- double[][] dens = new double[resolution][resolution];
- {
- // TODO: incrementally update the loff/roff values?
- for(int x = 0; x < resolution; x++) {
- double xlow = min0 + ste0 * x, xhig = xlow + ste0;
- int loff = unflip(Arrays.binarySearch(data, new double[] { xlow - bandwidth[0] }, comp0));
- int roff = unflip(Arrays.binarySearch(data, new double[] { xhig + bandwidth[0] }, comp0));
- // Resort by second component
- Arrays.sort(data, loff, roff, comp1);
- for(int y = 0; y < resolution; y++) {
- double ylow = min1 + ste1 * y, yhig = ylow + ste1;
- int boff = unflip(Arrays.binarySearch(data, loff, roff, new double[] { 0, ylow - bandwidth[1] }, comp1));
- int toff = unflip(Arrays.binarySearch(data, loff, roff, new double[] { 0, yhig + bandwidth[1] }, comp1));
- for(int pos = boff; pos < toff; pos++) {
- double[] val = data[pos];
- double d0 = (val[0] < xlow) ? (xlow - val[0]) : (val[0] > xhig) ? (val[0] - xhig) : 0;
- double d1 = (val[1] < ylow) ? (ylow - val[1]) : (val[1] > yhig) ? (val[1] - yhig) : 0;
- d0 = d0 / bandwidth[0];
- d1 = d1 / bandwidth[1];
- dens[x][y] += kernf * (1 - d0 * d0) * (1 - d1 * d1);
+ double[] bandwidth = initializeBandwidth(data);
+ // Compare by first component
+ Comparator<double[]> comp0 = new Comparator<double[]>() {
+ @Override
+ public int compare(double[] o1, double[] o2) {
+ return Double.compare(o1[0], o2[0]);
+ }
+ };
+ // Compare by second component
+ Comparator<double[]> comp1 = new Comparator<double[]>() {
+ @Override
+ public int compare(double[] o1, double[] o2) {
+ return Double.compare(o1[1], o2[1]);
+ }
+ };
+ // TODO: choose comparator order based on smaller bandwidth?
+ Arrays.sort(data, comp0);
+
+ CanvasSize canvas = proj.estimateViewport();
+ double min0 = canvas.minx, max0 = canvas.maxx, ste0 = (max0 - min0) / resolution;
+ double min1 = canvas.miny, max1 = canvas.maxy, ste1 = (max1 - min1) / resolution;
+
+ double kernf = 9. / (16 * bandwidth[0] * bandwidth[1]);
+ double maxdens = 0.0;
+ double[][] dens = new double[resolution][resolution];
+ {
+ // TODO: incrementally update the loff/roff values?
+ for(int x = 0; x < resolution; x++) {
+ double xlow = min0 + ste0 * x, xhig = xlow + ste0;
+ int loff = unflip(Arrays.binarySearch(data, new double[] { xlow - bandwidth[0] }, comp0));
+ int roff = unflip(Arrays.binarySearch(data, new double[] { xhig + bandwidth[0] }, comp0));
+ // Resort by second component
+ Arrays.sort(data, loff, roff, comp1);
+ for(int y = 0; y < resolution; y++) {
+ double ylow = min1 + ste1 * y, yhig = ylow + ste1;
+ int boff = unflip(Arrays.binarySearch(data, loff, roff, new double[] { 0, ylow - bandwidth[1] }, comp1));
+ int toff = unflip(Arrays.binarySearch(data, loff, roff, new double[] { 0, yhig + bandwidth[1] }, comp1));
+ for(int pos = boff; pos < toff; pos++) {
+ double[] val = data[pos];
+ double d0 = (val[0] < xlow) ? (xlow - val[0]) : (val[0] > xhig) ? (val[0] - xhig) : 0;
+ double d1 = (val[1] < ylow) ? (ylow - val[1]) : (val[1] > yhig) ? (val[1] - yhig) : 0;
+ d0 = d0 / bandwidth[0];
+ d1 = d1 / bandwidth[1];
+ dens[x][y] += kernf * (1 - d0 * d0) * (1 - d1 * d1);
+ }
+ maxdens = Math.max(maxdens, dens[x][y]);
}
- maxdens = Math.max(maxdens, dens[x][y]);
+ // Restore original sorting, as the intervals overlap
+ Arrays.sort(data, loff, roff, comp0);
}
- // Restore original sorting, as the intervals overlap
- Arrays.sort(data, loff, roff, comp0);
}
- }
- img = new BufferedImage(resolution, resolution, BufferedImage.TYPE_INT_ARGB);
- {
- for(int x = 0; x < resolution; x++) {
- for(int y = 0; y < resolution; y++) {
- int rgb = KMLOutputHandler.getColorForValue(dens[x][y] / maxdens).getRGB();
- img.setRGB(x, y, rgb);
+ img = new BufferedImage(resolution, resolution, BufferedImage.TYPE_INT_ARGB);
+ {
+ for(int x = 0; x < resolution; x++) {
+ for(int y = 0; y < resolution; y++) {
+ int rgb = KMLOutputHandler.getColorForValue(dens[x][y] / maxdens).getRGB();
+ img.setRGB(x, y, rgb);
+ }
}
}
}
- }
- private int unflip(int binarySearch) {
- if(binarySearch < 0) {
- return (-binarySearch) - 1;
- }
- else {
- return binarySearch;
- }
- }
-
- /**
- * The visualization factory
- *
- * @author Erich Schubert
- *
- * @apiviz.stereotype factory
- * @apiviz.uses DensityEstimationOverlay oneway - - «create»
- */
- public static class Factory extends AbstractVisFactory {
- /**
- * Constructor, adhering to
- * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
- */
- public Factory() {
- super();
- }
-
- @Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new DensityEstimationOverlay(task);
- }
-
- @Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(result, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, p.getRelation(), p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA + 1);
- task.put(VisualizationTask.META_VISIBLE_DEFAULT, false);
- baseResult.getHierarchy().add(p, task);
+ private int unflip(int binarySearch) {
+ if(binarySearch < 0) {
+ return (-binarySearch) - 1;
+ }
+ else {
+ return binarySearch;
}
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/index/TreeMBRVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/index/TreeMBRVisualization.java
index 61635625..9d466a8f 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/index/TreeMBRVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/index/TreeMBRVisualization.java
@@ -60,14 +60,10 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.AbstractScatter
*
* @author Erich Schubert
*
- * @apiviz.has AbstractRStarTree oneway - - visualizes
- * @apiviz.uses SVGHyperCube
- *
- * @param <N> Tree node type
- * @param <E> Tree entry type
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-// TODO: listen for tree changes instead of data changes?
-public class TreeMBRVisualization<N extends AbstractRStarTreeNode<N, E>, E extends SpatialEntry> extends AbstractScatterplotVisualization implements DataStoreListener {
+public class TreeMBRVisualization extends AbstractVisFactory {
/**
* Generic tag to indicate the type of element. Used in IDs, CSS-Classes etc.
*/
@@ -79,109 +75,152 @@ public class TreeMBRVisualization<N extends AbstractRStarTreeNode<N, E>, E exten
public static final String NAME = "Index MBRs";
/**
- * Fill parameter.
- */
- protected boolean fill = false;
-
- /**
- * The tree we visualize
+ * Settings
*/
- protected AbstractRStarTree<N, E> tree;
+ protected Parameterizer settings;
/**
* Constructor.
*
- * @param task Visualization task
- * @param fill Fill flag
+ * @param settings Settings
*/
- @SuppressWarnings("unchecked")
- public TreeMBRVisualization(VisualizationTask task, boolean fill) {
- super(task);
- this.tree = AbstractRStarTree.class.cast(task.getResult());
- this.fill = fill;
- incrementalRedraw();
- context.addDataStoreListener(this);
+ public TreeMBRVisualization(Parameterizer settings) {
+ super();
+ this.settings = settings;
}
@Override
- protected void redraw() {
- int projdim = proj.getVisibleDimensions2D().cardinality();
- ColorLibrary colors = context.getStyleLibrary().getColorSet(StyleLibrary.PLOT);
-
- if(tree != null) {
- E root = tree.getRootEntry();
- for(int i = 0; i < tree.getHeight(); i++) {
- CSSClass cls = new CSSClass(this, INDEX + i);
- // Relative depth of this level. 1.0 = toplevel
- final double relDepth = 1. - (((double) i) / tree.getHeight());
- if(fill) {
- cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, colors.getColor(i));
- cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, relDepth * context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT));
- cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, colors.getColor(i));
- cls.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, 0.1 / (projdim - 1));
- }
- else {
- cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, colors.getColor(i));
- cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, relDepth * context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT));
- cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance<RStarTreeNode, SpatialEntry>(task);
+ }
+
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<AbstractRStarTree<RStarTreeNode, SpatialEntry>> trees = ResultUtil.filterResults(result, AbstractRStarTree.class);
+ for(AbstractRStarTree<RStarTreeNode, SpatialEntry> tree : trees) {
+ if(tree instanceof Result) {
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, (Result) tree, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_BACKGROUND + 1;
+ baseResult.getHierarchy().add((Result) tree, task);
+ baseResult.getHierarchy().add(p, task);
}
- cls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
- cls.setStatement(SVGConstants.CSS_STROKE_LINEJOIN_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
- svgp.addCSSClassOrLogError(cls);
}
- visualizeRTreeEntry(svgp, layer, proj, tree, root, 0);
}
}
/**
- * Recursively draw the MBR rectangles.
+ * Instance for a particular tree
*
- * @param svgp SVG Plot
- * @param layer Layer
- * @param proj Projection
- * @param rtree Rtree to visualize
- * @param entry Current entry
- * @param depth Current depth
+ * @author Erich Schubert
+ *
+ * @apiviz.has AbstractRStarTree oneway - - visualizes
+ * @apiviz.uses SVGHyperCube
+ *
+ * @param <N> Tree node type
+ * @param <E> Tree entry type
*/
- private void visualizeRTreeEntry(SVGPlot svgp, Element layer, Projection2D proj, AbstractRStarTree<? extends N, E> rtree, E entry, int depth) {
- SpatialComparable mbr = entry;
+ // TODO: listen for tree changes instead of data changes?
+ public class Instance<N extends AbstractRStarTreeNode<N, E>, E extends SpatialEntry> extends AbstractScatterplotVisualization implements DataStoreListener {
+ /**
+ * The tree we visualize
+ */
+ protected AbstractRStarTree<N, E> tree;
- if(fill) {
- Element r = SVGHyperCube.drawFilled(svgp, INDEX + depth, proj, SpatialUtil.getMin(mbr), SpatialUtil.getMax(mbr));
- layer.appendChild(r);
+ /**
+ * Constructor.
+ *
+ * @param task Visualization task
+ */
+ @SuppressWarnings("unchecked")
+ public Instance(VisualizationTask task) {
+ super(task);
+ this.tree = AbstractRStarTree.class.cast(task.getResult());
+ incrementalRedraw();
+ context.addDataStoreListener(this);
}
- else {
- Element r = SVGHyperCube.drawFrame(svgp, proj, SpatialUtil.getMin(mbr), SpatialUtil.getMax(mbr));
- SVGUtil.setCSSClass(r, INDEX + depth);
- layer.appendChild(r);
+
+ @Override
+ protected void redraw() {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ int projdim = proj.getVisibleDimensions2D().cardinality();
+ ColorLibrary colors = style.getColorSet(StyleLibrary.PLOT);
+
+ if(tree != null) {
+ E root = tree.getRootEntry();
+ for(int i = 0; i < tree.getHeight(); i++) {
+ CSSClass cls = new CSSClass(this, INDEX + i);
+ // Relative depth of this level. 1.0 = toplevel
+ final double relDepth = 1. - (((double) i) / tree.getHeight());
+ if(settings.fill) {
+ cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, colors.getColor(i));
+ cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, relDepth * style.getLineWidth(StyleLibrary.PLOT));
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, colors.getColor(i));
+ cls.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, 0.1 / (projdim - 1));
+ }
+ else {
+ cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, colors.getColor(i));
+ cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, relDepth * style.getLineWidth(StyleLibrary.PLOT));
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
+ }
+ cls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+ cls.setStatement(SVGConstants.CSS_STROKE_LINEJOIN_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+ svgp.addCSSClassOrLogError(cls);
+ }
+ visualizeRTreeEntry(svgp, layer, proj, tree, root, 0);
+ }
}
- if(!entry.isLeafEntry()) {
- N node = rtree.getNode(entry);
- for(int i = 0; i < node.getNumEntries(); i++) {
- E child = node.getEntry(i);
- if(!child.isLeafEntry()) {
- visualizeRTreeEntry(svgp, layer, proj, rtree, child, depth + 1);
+ /**
+ * Recursively draw the MBR rectangles.
+ *
+ * @param svgp SVG Plot
+ * @param layer Layer
+ * @param proj Projection
+ * @param rtree Rtree to visualize
+ * @param entry Current entry
+ * @param depth Current depth
+ */
+ private void visualizeRTreeEntry(SVGPlot svgp, Element layer, Projection2D proj, AbstractRStarTree<? extends N, E> rtree, E entry, int depth) {
+ SpatialComparable mbr = entry;
+
+ if(settings.fill) {
+ Element r = SVGHyperCube.drawFilled(svgp, INDEX + depth, proj, SpatialUtil.getMin(mbr), SpatialUtil.getMax(mbr));
+ layer.appendChild(r);
+ }
+ else {
+ Element r = SVGHyperCube.drawFrame(svgp, proj, SpatialUtil.getMin(mbr), SpatialUtil.getMax(mbr));
+ SVGUtil.setCSSClass(r, INDEX + depth);
+ layer.appendChild(r);
+ }
+
+ if(!entry.isLeafEntry()) {
+ N node = rtree.getNode(entry);
+ for(int i = 0; i < node.getNumEntries(); i++) {
+ E child = node.getEntry(i);
+ if(!child.isLeafEntry()) {
+ visualizeRTreeEntry(svgp, layer, proj, rtree, child, depth + 1);
+ }
}
}
}
- }
- @Override
- public void destroy() {
- super.destroy();
- context.removeDataStoreListener(this);
+ @Override
+ public void destroy() {
+ super.destroy();
+ context.removeDataStoreListener(this);
+ }
}
/**
- * Factory
+ * Parameterization class.
*
* @author Erich Schubert
*
- * @apiviz.stereotype factory
- * @apiviz.uses TreeMBRVisualization oneway - - «create»
+ * @apiviz.exclude
*/
- public static class Factory extends AbstractVisFactory {
+ public static class Parameterizer extends AbstractParameterizer {
/**
* Flag for half-transparent filling of bubbles.
*
@@ -189,67 +228,22 @@ public class TreeMBRVisualization<N extends AbstractRStarTreeNode<N, E>, E exten
* Key: {@code -index.fill}
* </p>
*/
- public static final OptionID FILL_ID = OptionID.getOrCreateOptionID("index.fill", "Partially transparent filling of index pages.");
+ public static final OptionID FILL_ID = new OptionID("index.fill", "Partially transparent filling of index pages.");
- /**
- * Fill parameter.
- */
protected boolean fill = false;
- /**
- * Constructor.
- *
- * @param fill
- */
- public Factory(boolean fill) {
- super();
- this.fill = fill;
- }
-
@Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new TreeMBRVisualization<RStarTreeNode, SpatialEntry>(task, fill);
- }
-
- @Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<AbstractRStarTree<RStarTreeNode, SpatialEntry>> trees = ResultUtil.filterResults(result, AbstractRStarTree.class);
- for(AbstractRStarTree<RStarTreeNode, SpatialEntry> tree : trees) {
- if(tree instanceof Result) {
- Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, (Result) tree, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_BACKGROUND + 1);
- baseResult.getHierarchy().add((Result) tree, task);
- baseResult.getHierarchy().add(p, task);
- }
- }
+ protected void makeOptions(Parameterization config) {
+ super.makeOptions(config);
+ Flag fillF = new Flag(FILL_ID);
+ if(config.grab(fillF)) {
+ fill = fillF.isTrue();
}
}
- /**
- * Parameterization class.
- *
- * @author Erich Schubert
- *
- * @apiviz.exclude
- */
- public static class Parameterizer extends AbstractParameterizer {
- protected boolean fill = false;
-
- @Override
- protected void makeOptions(Parameterization config) {
- super.makeOptions(config);
- Flag fillF = new Flag(FILL_ID);
- if(config.grab(fillF)) {
- fill = fillF.getValue();
- }
- }
-
- @Override
- protected Factory makeInstance() {
- return new Factory(fill);
- }
+ @Override
+ protected TreeMBRVisualization makeInstance() {
+ return new TreeMBRVisualization(this);
}
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/index/TreeSphereVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/index/TreeSphereVisualization.java
index daebab02..e89be028 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/index/TreeSphereVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/index/TreeSphereVisualization.java
@@ -65,14 +65,10 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.AbstractScatter
*
* @author Erich Schubert
*
- * @apiviz.has AbstractMTree oneway - - visualizes
- * @apiviz.uses SVGHyperSphere
- *
- * @param <N> Tree node type
- * @param <E> Tree entry type
+ * @apiviz.stereotype factory
+ * @apiviz.uses TreeSphereVisualization oneway - - «create»
*/
-// TODO: listen for tree changes!
-public class TreeSphereVisualization<D extends NumberDistance<D, ?>, N extends AbstractMTreeNode<?, D, N, E>, E extends MTreeEntry<D>> extends AbstractScatterplotVisualization implements DataStoreListener {
+public class TreeSphereVisualization extends AbstractVisFactory {
/**
* Generic tag to indicate the type of element. Used in IDs, CSS-Classes etc.
*/
@@ -92,37 +88,40 @@ public class TreeSphereVisualization<D extends NumberDistance<D, ?>, N extends A
MANHATTAN, EUCLIDEAN, LPCROSS
}
- protected double p;
-
/**
- * Drawing mode (distance) to use
+ * Settings
*/
- protected Modus dist = Modus.LPCROSS;
+ protected Parameterizer settings;
/**
- * The tree we visualize
+ * Constructor.
+ *
+ * @param settings Settings
*/
- protected AbstractMTree<?, D, N, E> tree;
+ public TreeSphereVisualization(Parameterizer settings) {
+ super();
+ this.settings = settings;
+ }
- /**
- * Fill parameter.
- */
- protected boolean fill = false;
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
+ for (ScatterPlotProjector<?> p : ps) {
+ Collection<AbstractMTree<?, DoubleDistance, ?, ?>> trees = ResultUtil.filterResults(result, AbstractMTree.class);
+ for (AbstractMTree<?, DoubleDistance, ?, ?> tree : trees) {
+ if (canVisualize(tree) && tree instanceof Result) {
+ final VisualizationTask task = new VisualizationTask(NAME, (Result) tree, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_BACKGROUND + 1;
+ baseResult.getHierarchy().add((Result) tree, task);
+ baseResult.getHierarchy().add(p, task);
+ }
+ }
+ }
+ }
- /**
- * Constructor
- *
- * @param task Task
- * @param fill fill flag
- */
- @SuppressWarnings("unchecked")
- public TreeSphereVisualization(VisualizationTask task, boolean fill) {
- super(task);
- this.tree = AbstractMTree.class.cast(task.getResult());
- this.p = getLPNormP(this.tree);
- this.fill = fill;
- incrementalRedraw();
- context.addDataStoreListener(this);
+ @Override
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance<DoubleDistance, MTreeNode<Object, DoubleDistance>, MTreeEntry<DoubleDistance>>(task);
}
/**
@@ -131,14 +130,14 @@ public class TreeSphereVisualization<D extends NumberDistance<D, ?>, N extends A
* @param tree Tree to visualize
* @return p value
*/
- public static Double getLPNormP(AbstractMTree<?, ?, ?, ?> tree) {
- // Note: we deliberately lose generics here, so the compilers complain less
- // on the next typecheck and cast!
+ public static double getLPNormP(AbstractMTree<?, ?, ?, ?> tree) {
+ // Note: we deliberately lose generics here, so the compilers complain
+ // less on the next typecheck and cast!
DistanceFunction<?, ?> distanceFunction = tree.getDistanceQuery().getDistanceFunction();
- if(LPNormDistanceFunction.class.isInstance(distanceFunction)) {
+ if (LPNormDistanceFunction.class.isInstance(distanceFunction)) {
return ((LPNormDistanceFunction) distanceFunction).getP();
}
- return null;
+ return 0;
}
/**
@@ -148,170 +147,160 @@ public class TreeSphereVisualization<D extends NumberDistance<D, ?>, N extends A
* @return whether the tree is visualizable
*/
public static boolean canVisualize(AbstractMTree<?, ?, ?, ?> tree) {
- Double p = getLPNormP(tree);
- return (p != null);
- }
-
- @Override
- protected void redraw() {
- int projdim = proj.getVisibleDimensions2D().cardinality();
- ColorLibrary colors = context.getStyleLibrary().getColorSet(StyleLibrary.PLOT);
-
- p = getLPNormP(tree);
- if(tree != null) {
- if(ManhattanDistanceFunction.class.isInstance(tree.getDistanceQuery())) {
- dist = Modus.MANHATTAN;
- }
- else if(EuclideanDistanceFunction.class.isInstance(tree.getDistanceQuery())) {
- dist = Modus.EUCLIDEAN;
- }
- else {
- dist = Modus.LPCROSS;
- }
- E root = tree.getRootEntry();
- final int mtheight = tree.getHeight();
- for(int i = 0; i < mtheight; i++) {
- CSSClass cls = new CSSClass(this, INDEX + i);
- // Relative depth of this level. 1.0 = toplevel
- final double relDepth = 1. - (((double) i) / mtheight);
- if(fill) {
- cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, colors.getColor(i));
- cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, relDepth * context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT));
- cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, colors.getColor(i));
- cls.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, 0.1 / (projdim - 1));
- cls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
- cls.setStatement(SVGConstants.CSS_STROKE_LINEJOIN_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
- }
- else {
- cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, colors.getColor(i));
- cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, relDepth * context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT));
- cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
- cls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
- cls.setStatement(SVGConstants.CSS_STROKE_LINEJOIN_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
- }
- svgp.addCSSClassOrLogError(cls);
- }
- visualizeMTreeEntry(svgp, this.layer, proj, tree, root, 0);
- }
+ return getLPNormP(tree) > 0;
}
/**
- * Recursively draw the MBR rectangles.
- *
- * @param svgp SVG Plot
- * @param layer Layer
- * @param proj Projection
- * @param mtree Mtree to visualize
- * @param entry Current entry
- * @param depth Current depth
- */
- private void visualizeMTreeEntry(SVGPlot svgp, Element layer, Projection2D proj, AbstractMTree<?, D, N, E> mtree, E entry, int depth) {
- DBID roid = entry.getRoutingObjectID();
- if(roid != null) {
- NumberVector<?, ?> ro = rel.get(roid);
- D rad = entry.getCoveringRadius();
-
- final Element r;
- if(dist == Modus.MANHATTAN) {
- r = SVGHyperSphere.drawManhattan(svgp, proj, ro, rad);
- }
- else if(dist == Modus.EUCLIDEAN) {
- r = SVGHyperSphere.drawEuclidean(svgp, proj, ro, rad);
- }
- // TODO: add visualizer for infinity norm?
- else {
- // r = SVGHyperSphere.drawCross(svgp, proj, ro, rad);
- r = SVGHyperSphere.drawLp(svgp, proj, ro, rad, p);
- }
- SVGUtil.setCSSClass(r, INDEX + (depth - 1));
- layer.appendChild(r);
- }
-
- if(!entry.isLeafEntry()) {
- N node = mtree.getNode(entry);
- for(int i = 0; i < node.getNumEntries(); i++) {
- E child = node.getEntry(i);
- if(!child.isLeafEntry()) {
- visualizeMTreeEntry(svgp, layer, proj, mtree, child, depth + 1);
- }
- }
- }
- }
-
- @Override
- public void destroy() {
- super.destroy();
- context.removeDataStoreListener(this);
- }
-
- /**
- * Factory
+ * Instance for a particular tree.
*
* @author Erich Schubert
*
- * @apiviz.stereotype factory
- * @apiviz.uses TreeSphereVisualization oneway - - «create»
+ * @apiviz.has AbstractMTree oneway - - visualizes
+ * @apiviz.uses SVGHyperSphere
+ *
+ * @param <N> Tree node type
+ * @param <E> Tree entry type
*/
- public static class Factory extends AbstractVisFactory {
+ // TODO: listen for tree changes!
+ public class Instance<D extends NumberDistance<D, ?>, N extends AbstractMTreeNode<?, D, N, E>, E extends MTreeEntry<D>> extends AbstractScatterplotVisualization implements DataStoreListener {
+ protected double p;
+
/**
- * Fill parameter.
+ * Drawing mode (distance) to use
*/
- protected boolean fill = false;
+ protected Modus dist = Modus.LPCROSS;
+
+ /**
+ * The tree we visualize
+ */
+ protected AbstractMTree<?, D, N, E> tree;
/**
- * Constructor.
+ * Constructor
*
- * @param fill
+ * @param task Task
*/
- public Factory(boolean fill) {
- super();
- this.fill = fill;
+ @SuppressWarnings("unchecked")
+ public Instance(VisualizationTask task) {
+ super(task);
+ this.tree = AbstractMTree.class.cast(task.getResult());
+ this.p = getLPNormP(this.tree);
+ incrementalRedraw();
+ context.addDataStoreListener(this);
}
@Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : ps) {
- Collection<AbstractMTree<?, DoubleDistance, ?, ?>> trees = ResultUtil.filterResults(result, AbstractMTree.class);
- for(AbstractMTree<?, DoubleDistance, ?, ?> tree : trees) {
- if(canVisualize(tree) && tree instanceof Result) {
- final VisualizationTask task = new VisualizationTask(NAME, (Result) tree, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_BACKGROUND + 1);
- baseResult.getHierarchy().add((Result) tree, task);
- baseResult.getHierarchy().add(p, task);
+ protected void redraw() {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ int projdim = proj.getVisibleDimensions2D().cardinality();
+ ColorLibrary colors = style.getColorSet(StyleLibrary.PLOT);
+
+ p = getLPNormP(tree);
+ if (tree != null) {
+ if (ManhattanDistanceFunction.class.isInstance(tree.getDistanceQuery())) {
+ dist = Modus.MANHATTAN;
+ } else if (EuclideanDistanceFunction.class.isInstance(tree.getDistanceQuery())) {
+ dist = Modus.EUCLIDEAN;
+ } else {
+ dist = Modus.LPCROSS;
+ }
+ E root = tree.getRootEntry();
+ final int mtheight = tree.getHeight();
+ for (int i = 0; i < mtheight; i++) {
+ CSSClass cls = new CSSClass(this, INDEX + i);
+ // Relative depth of this level. 1.0 = toplevel
+ final double relDepth = 1. - (((double) i) / mtheight);
+ if (settings.fill) {
+ cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, colors.getColor(i));
+ cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, relDepth * style.getLineWidth(StyleLibrary.PLOT));
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, colors.getColor(i));
+ cls.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, 0.1 / (projdim - 1));
+ cls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+ cls.setStatement(SVGConstants.CSS_STROKE_LINEJOIN_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+ } else {
+ cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, colors.getColor(i));
+ cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, relDepth * style.getLineWidth(StyleLibrary.PLOT));
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
+ cls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+ cls.setStatement(SVGConstants.CSS_STROKE_LINEJOIN_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
}
+ svgp.addCSSClassOrLogError(cls);
}
+ visualizeMTreeEntry(svgp, this.layer, proj, tree, root, 0);
}
}
- @Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new TreeSphereVisualization<DoubleDistance, MTreeNode<Object, DoubleDistance>, MTreeEntry<DoubleDistance>>(task, fill);
- }
-
/**
- * Parameterization class.
+ * Recursively draw the MBR rectangles.
*
- * @author Erich Schubert
- *
- * @apiviz.exclude
+ * @param svgp SVG Plot
+ * @param layer Layer
+ * @param proj Projection
+ * @param mtree Mtree to visualize
+ * @param entry Current entry
+ * @param depth Current depth
*/
- public static class Parameterizer extends AbstractParameterizer {
- protected boolean fill = false;
+ private void visualizeMTreeEntry(SVGPlot svgp, Element layer, Projection2D proj, AbstractMTree<?, D, N, E> mtree, E entry, int depth) {
+ DBID roid = entry.getRoutingObjectID();
+ if (roid != null) {
+ NumberVector<?> ro = rel.get(roid);
+ D rad = entry.getCoveringRadius();
- @Override
- protected void makeOptions(Parameterization config) {
- super.makeOptions(config);
- Flag fillF = new Flag(TreeMBRVisualization.Factory.FILL_ID);
- if(config.grab(fillF)) {
- fill = fillF.getValue();
+ final Element r;
+ if (dist == Modus.MANHATTAN) {
+ r = SVGHyperSphere.drawManhattan(svgp, proj, ro, rad);
+ } else if (dist == Modus.EUCLIDEAN) {
+ r = SVGHyperSphere.drawEuclidean(svgp, proj, ro, rad);
+ }
+ // TODO: add visualizer for infinity norm?
+ else {
+ // r = SVGHyperSphere.drawCross(svgp, proj, ro, rad);
+ r = SVGHyperSphere.drawLp(svgp, proj, ro, rad, p);
}
+ SVGUtil.setCSSClass(r, INDEX + (depth - 1));
+ layer.appendChild(r);
}
- @Override
- protected Factory makeInstance() {
- return new Factory(fill);
+ if (!entry.isLeafEntry()) {
+ N node = mtree.getNode(entry);
+ for (int i = 0; i < node.getNumEntries(); i++) {
+ E child = node.getEntry(i);
+ if (!child.isLeafEntry()) {
+ visualizeMTreeEntry(svgp, layer, proj, mtree, child, depth + 1);
+ }
+ }
}
}
+
+ @Override
+ public void destroy() {
+ super.destroy();
+ context.removeDataStoreListener(this);
+ }
+ }
+
+ /**
+ * Parameterization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public static class Parameterizer extends AbstractParameterizer {
+ protected boolean fill = false;
+
+ @Override
+ protected void makeOptions(Parameterization config) {
+ super.makeOptions(config);
+ Flag fillF = new Flag(TreeMBRVisualization.Parameterizer.FILL_ID);
+ if (config.grab(fillF)) {
+ fill = fillF.isTrue();
+ }
+ }
+
+ @Override
+ protected TreeSphereVisualization makeInstance() {
+ return new TreeSphereVisualization(this);
+ }
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/outlier/BubbleVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/outlier/BubbleVisualization.java
index 769dc53d..ebe9612d 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/outlier/BubbleVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/outlier/BubbleVisualization.java
@@ -50,6 +50,7 @@ import de.lmu.ifi.dbs.elki.visualization.css.CSSClass;
import de.lmu.ifi.dbs.elki.visualization.projector.ScatterPlotProjector;
import de.lmu.ifi.dbs.elki.visualization.style.ClassStylingPolicy;
import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary;
+import de.lmu.ifi.dbs.elki.visualization.style.StyleResult;
import de.lmu.ifi.dbs.elki.visualization.style.StylingPolicy;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
@@ -66,10 +67,11 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.thumbs.ThumbnailVisualizati
* @author Remigius Wojdanowski
* @author Erich Schubert
*
- * @apiviz.has OutlierResult oneway - - visualizes
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
@Reference(authors = "E. Achtert, H.-P. Kriegel, L. Reichert, E. Schubert, R. Wojdanowski, A. Zimek", title = "Visual Evaluation of Outlier Detection Models", booktitle = "Proceedings of the 15th International Conference on Database Systems for Advanced Applications (DASFAA), Tsukuba, Japan, 2010", url = "http://dx.doi.org/10.1007/978-3-642-12098-5_34")
-public class BubbleVisualization extends AbstractScatterplotVisualization implements DataStoreListener {
+public class BubbleVisualization extends AbstractVisFactory {
/**
* Generic tag to indicate the type of element. Used in IDs, CSS-Classes etc.
*/
@@ -81,160 +83,207 @@ public class BubbleVisualization extends AbstractScatterplotVisualization implem
public static final String NAME = "Outlier Bubbles";
/**
- * Fill parameter.
+ * Current settings
*/
- protected boolean fill;
-
- /**
- * Scaling function to use for Bubbles
- */
- protected ScalingFunction scaling;
-
- /**
- * The outlier result to visualize
- */
- protected OutlierResult result;
+ protected Parameterizer settings;
/**
* Constructor.
*
- * @param task Visualization task
- * @param scaling Scaling function
- * @param fill Fill flag
+ * @param settings Settings
*/
- public BubbleVisualization(VisualizationTask task, ScalingFunction scaling, boolean fill) {
- super(task);
- this.result = task.getResult();
- this.scaling = scaling;
- this.fill = fill;
- context.addDataStoreListener(this);
- context.addResultListener(this);
- incrementalRedraw();
+ public BubbleVisualization(Parameterizer settings) {
+ super();
+ this.settings = settings;
+ thumbmask |= ThumbnailVisualization.ON_DATA | ThumbnailVisualization.ON_STYLE;
}
@Override
- public void destroy() {
- super.destroy();
- context.removeResultListener(this);
- context.removeDataStoreListener(this);
+ public Visualization makeVisualization(VisualizationTask task) {
+ if(settings.scaling != null && settings.scaling instanceof OutlierScalingFunction) {
+ final OutlierResult outlierResult = task.getResult();
+ ((OutlierScalingFunction) settings.scaling).prepare(outlierResult);
+ }
+ return new Instance(task);
}
@Override
- public void redraw() {
- StylingPolicy stylepolicy = context.getStyleResult().getStylingPolicy();
- // bubble size
- final double bubble_size = context.getStyleLibrary().getSize(StyleLibrary.BUBBLEPLOT);
- if(stylepolicy instanceof ClassStylingPolicy) {
- ClassStylingPolicy colors = (ClassStylingPolicy) stylepolicy;
- setupCSS(svgp, colors);
- // draw data
- for(DBIDIter objId = sample.getSample().iter(); objId.valid(); objId.advance()) {
- final Double radius = getScaledForId(objId);
- if(radius > 0.01 && !Double.isInfinite(radius)) {
- final NumberVector<?, ?> vec = rel.get(objId);
- if(vec != null) {
- double[] v = proj.fastProjectDataToRenderSpace(vec);
- Element circle = svgp.svgCircle(v[0], v[1], radius * bubble_size);
- SVGUtil.addCSSClass(circle, BUBBLE + colors.getStyleForDBID(objId));
- layer.appendChild(circle);
- }
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<OutlierResult> ors = ResultUtil.filterResults(result, OutlierResult.class);
+ for(OutlierResult o : ors) {
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
+ boolean vis = true;
+ // Quick and dirty hack: hide if parent result is also an outlier result
+ // Since that probably is already visible and we're redundant.
+ for(Result r : o.getHierarchy().getParents(o)) {
+ if(r instanceof OutlierResult) {
+ vis = false;
+ break;
}
}
- }
- else {
- // draw data
- for(DBIDIter objId = sample.getSample().iter(); objId.valid(); objId.advance()) {
- final Double radius = getScaledForId(objId);
- if(radius > 0.01 && !Double.isInfinite(radius)) {
- final NumberVector<?, ?> vec = rel.get(objId);
- if(vec != null) {
- double[] v = proj.fastProjectDataToRenderSpace(vec);
- Element circle = svgp.svgCircle(v[0], v[1], radius * bubble_size);
- int color = stylepolicy.getColorForDBID(objId);
- final StringBuffer style = new StringBuffer();
- if(fill) {
- style.append(SVGConstants.CSS_FILL_PROPERTY + ":").append(SVGUtil.colorToString(color));
- style.append(SVGConstants.CSS_FILL_OPACITY_PROPERTY + ":0.5");
- }
- else {
- style.append(SVGConstants.CSS_STROKE_VALUE + ":").append(SVGUtil.colorToString(color));
- style.append(SVGConstants.CSS_FILL_PROPERTY + ":" + SVGConstants.CSS_NONE_VALUE);
- }
- SVGUtil.setAtt(circle, SVGConstants.SVG_STYLE_ATTRIBUTE, style.toString());
- layer.appendChild(circle);
- }
+ for(ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, o, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_DATA;
+ if(!vis) {
+ task.initDefaultVisibility(false);
}
+ baseResult.getHierarchy().add(o, task);
+ baseResult.getHierarchy().add(p, task);
}
}
}
- @Override
- public void resultChanged(Result current) {
- super.resultChanged(current);
- if(sample == current || context.getStyleResult() == current) {
- synchronizedRedraw();
- }
- }
-
/**
- * Registers the Bubble-CSS-Class at a SVGPlot.
+ * Factory for producing bubble visualizations
*
- * @param svgp the SVGPlot to register the Tooltip-CSS-Class.
- * @param policy Clustering to use
+ * @author Erich Schubert
+ *
+ * @apiviz.has OutlierResult oneway - - visualizes
*/
- private void setupCSS(SVGPlot svgp, ClassStylingPolicy policy) {
- ColorLibrary colors = context.getStyleLibrary().getColorSet(StyleLibrary.PLOT);
+ public class Instance extends AbstractScatterplotVisualization implements DataStoreListener {
+ /**
+ * The outlier result to visualize
+ */
+ protected OutlierResult result;
- // creating IDs manually because cluster often return a null-ID.
- for(int clusterID = policy.getMinStyle(); clusterID < policy.getMaxStyle(); clusterID++) {
- CSSClass bubble = new CSSClass(svgp, BUBBLE + clusterID);
- bubble.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT));
+ /**
+ * Constructor.
+ *
+ * @param task Visualization task
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
+ this.result = task.getResult();
+ context.addDataStoreListener(this);
+ context.addResultListener(this);
+ incrementalRedraw();
+ }
- String color = colors.getColor(clusterID);
+ @Override
+ public void destroy() {
+ super.destroy();
+ context.removeResultListener(this);
+ context.removeDataStoreListener(this);
+ }
- if(fill) {
- bubble.setStatement(SVGConstants.CSS_FILL_PROPERTY, color);
- bubble.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, 0.5);
+ @Override
+ public void redraw() {
+ final StyleResult style = context.getStyleResult();
+ StylingPolicy stylepolicy = style.getStylingPolicy();
+ // bubble size
+ final double bubble_size = style.getStyleLibrary().getSize(StyleLibrary.BUBBLEPLOT);
+ if(stylepolicy instanceof ClassStylingPolicy) {
+ ClassStylingPolicy colors = (ClassStylingPolicy) stylepolicy;
+ setupCSS(svgp, colors);
+ // draw data
+ for(DBIDIter objId = sample.getSample().iter(); objId.valid(); objId.advance()) {
+ final double radius = getScaledForId(objId);
+ if(radius > 0.01 && !Double.isInfinite(radius)) {
+ final NumberVector<?> vec = rel.get(objId);
+ if(vec != null) {
+ double[] v = proj.fastProjectDataToRenderSpace(vec);
+ Element circle = svgp.svgCircle(v[0], v[1], radius * bubble_size);
+ SVGUtil.addCSSClass(circle, BUBBLE + colors.getStyleForDBID(objId));
+ layer.appendChild(circle);
+ }
+ }
+ }
}
else {
- // for diamond-shaped strokes, see bugs.sun.com, bug ID 6294396
- bubble.setStatement(SVGConstants.CSS_STROKE_VALUE, color);
- bubble.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
+ // draw data
+ for(DBIDIter objId = sample.getSample().iter(); objId.valid(); objId.advance()) {
+ final double radius = getScaledForId(objId);
+ if(radius > 0.01 && !Double.isInfinite(radius)) {
+ final NumberVector<?> vec = rel.get(objId);
+ if(vec != null) {
+ double[] v = proj.fastProjectDataToRenderSpace(vec);
+ Element circle = svgp.svgCircle(v[0], v[1], radius * bubble_size);
+ int color = stylepolicy.getColorForDBID(objId);
+ final StringBuilder cssstyle = new StringBuilder();
+ if(settings.fill) {
+ cssstyle.append(SVGConstants.CSS_FILL_PROPERTY).append(':').append(SVGUtil.colorToString(color));
+ cssstyle.append(SVGConstants.CSS_FILL_OPACITY_PROPERTY).append(":0.5");
+ }
+ else {
+ cssstyle.append(SVGConstants.CSS_STROKE_VALUE).append(':').append(SVGUtil.colorToString(color));
+ cssstyle.append(SVGConstants.CSS_FILL_PROPERTY).append(':').append(SVGConstants.CSS_NONE_VALUE);
+ }
+ SVGUtil.setAtt(circle, SVGConstants.SVG_STYLE_ATTRIBUTE, cssstyle.toString());
+ layer.appendChild(circle);
+ }
+ }
+ }
}
-
- svgp.addCSSClassOrLogError(bubble);
}
- }
- /**
- * Convenience method to apply scalings in the right order.
- *
- * @param id object ID to get scaled score for
- * @return a Double representing a outlierness-score, after it has modified by
- * the given scales.
- */
- protected double getScaledForId(DBIDRef id) {
- double d = result.getScores().get(id).doubleValue();
- if(Double.isNaN(d) || Double.isInfinite(d)) {
- return 0.0;
+ @Override
+ public void resultChanged(Result current) {
+ super.resultChanged(current);
+ if(sample == current || context.getStyleResult() == current) {
+ synchronizedRedraw();
+ }
}
- if(scaling == null) {
- return result.getOutlierMeta().normalizeScore(d);
+
+ /**
+ * Registers the Bubble-CSS-Class at a SVGPlot.
+ *
+ * @param svgp the SVGPlot to register the Tooltip-CSS-Class.
+ * @param policy Clustering to use
+ */
+ private void setupCSS(SVGPlot svgp, ClassStylingPolicy policy) {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ ColorLibrary colors = style.getColorSet(StyleLibrary.PLOT);
+
+ // creating IDs manually because cluster often return a null-ID.
+ for(int clusterID = policy.getMinStyle(); clusterID < policy.getMaxStyle(); clusterID++) {
+ CSSClass bubble = new CSSClass(svgp, BUBBLE + clusterID);
+ bubble.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.PLOT));
+
+ String color = colors.getColor(clusterID);
+
+ if(settings.fill) {
+ bubble.setStatement(SVGConstants.CSS_FILL_PROPERTY, color);
+ bubble.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, 0.5);
+ }
+ else {
+ // for diamond-shaped strokes, see bugs.sun.com, bug ID 6294396
+ bubble.setStatement(SVGConstants.CSS_STROKE_VALUE, color);
+ bubble.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
+ }
+
+ svgp.addCSSClassOrLogError(bubble);
+ }
}
- else {
- return scaling.getScaled(d);
+
+ /**
+ * Convenience method to apply scalings in the right order.
+ *
+ * @param id object ID to get scaled score for
+ * @return a Double representing a outlierness-score, after it has modified
+ * by the given scales.
+ */
+ protected double getScaledForId(DBIDRef id) {
+ double d = result.getScores().get(id).doubleValue();
+ if(Double.isNaN(d) || Double.isInfinite(d)) {
+ return 0.0;
+ }
+ if(settings.scaling == null) {
+ return result.getOutlierMeta().normalizeScore(d);
+ }
+ else {
+ return settings.scaling.getScaled(d);
+ }
}
}
/**
- * Factory for producing bubble visualizations
+ * Parameterization class.
*
* @author Erich Schubert
*
- * @apiviz.stereotype factory
- * @apiviz.uses BubbleVisualization oneway - - «create»
+ * @apiviz.exclude
*/
- public static class Factory extends AbstractVisFactory {
+ public static class Parameterizer extends AbstractParameterizer {
/**
* Flag for half-transparent filling of bubbles.
*
@@ -242,7 +291,7 @@ public class BubbleVisualization extends AbstractScatterplotVisualization implem
* Key: {@code -bubble.fill}
* </p>
*/
- public static final OptionID FILL_ID = OptionID.getOrCreateOptionID("bubble.fill", "Half-transparent filling of bubbles.");
+ public static final OptionID FILL_ID = new OptionID("bubble.fill", "Half-transparent filling of bubbles.");
/**
* Parameter for scaling functions
@@ -251,7 +300,7 @@ public class BubbleVisualization extends AbstractScatterplotVisualization implem
* Key: {@code -bubble.scaling}
* </p>
*/
- public static final OptionID SCALING_ID = OptionID.getOrCreateOptionID("bubble.scaling", "Additional scaling function for bubbles.");
+ public static final OptionID SCALING_ID = new OptionID("bubble.scaling", "Additional scaling function for bubbles.");
/**
* Fill parameter.
@@ -263,90 +312,23 @@ public class BubbleVisualization extends AbstractScatterplotVisualization implem
*/
protected ScalingFunction scaling;
- /**
- * Constructor.
- *
- * @param fill
- * @param scaling
- */
- public Factory(boolean fill, ScalingFunction scaling) {
- super();
- this.fill = fill;
- this.scaling = scaling;
- thumbmask |= ThumbnailVisualization.ON_DATA | ThumbnailVisualization.ON_STYLE;
- }
-
@Override
- public Visualization makeVisualization(VisualizationTask task) {
- if(this.scaling != null && this.scaling instanceof OutlierScalingFunction) {
- final OutlierResult outlierResult = task.getResult();
- ((OutlierScalingFunction) this.scaling).prepare(outlierResult);
+ protected void makeOptions(Parameterization config) {
+ super.makeOptions(config);
+ Flag fillF = new Flag(FILL_ID);
+ if(config.grab(fillF)) {
+ fill = fillF.isTrue();
}
- return new BubbleVisualization(task, scaling, fill);
- }
- @Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<OutlierResult> ors = ResultUtil.filterResults(result, OutlierResult.class);
- for(OutlierResult o : ors) {
- Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
- boolean vis = true;
- // Quick and dirty hack: hide if parent result is also an outlier result
- // Since that probably is already visible and we're redundant.
- for(Result r : o.getHierarchy().getParents(o)) {
- if(r instanceof OutlierResult) {
- vis = false;
- break;
- }
- }
- for(ScatterPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, o, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA);
- if(!vis) {
- task.put(VisualizationTask.META_VISIBLE_DEFAULT, false);
- }
- baseResult.getHierarchy().add(o, task);
- baseResult.getHierarchy().add(p, task);
- }
+ ObjectParameter<ScalingFunction> scalingP = new ObjectParameter<ScalingFunction>(SCALING_ID, OutlierScalingFunction.class, true);
+ if(config.grab(scalingP)) {
+ scaling = scalingP.instantiateClass(config);
}
}
- /**
- * Parameterization class.
- *
- * @author Erich Schubert
- *
- * @apiviz.exclude
- */
- public static class Parameterizer extends AbstractParameterizer {
- /**
- * Fill parameter.
- */
- protected boolean fill = false;
-
- /**
- * Scaling function to use for Bubbles
- */
- protected ScalingFunction scaling = null;
-
- @Override
- protected void makeOptions(Parameterization config) {
- super.makeOptions(config);
- Flag fillF = new Flag(FILL_ID);
- if(config.grab(fillF)) {
- fill = fillF.getValue();
- }
-
- ObjectParameter<ScalingFunction> scalingP = new ObjectParameter<ScalingFunction>(SCALING_ID, OutlierScalingFunction.class, true);
- if(config.grab(scalingP)) {
- scaling = scalingP.instantiateClass(config);
- }
- }
-
- @Override
- protected Factory makeInstance() {
- return new Factory(fill, scaling);
- }
+ @Override
+ protected BubbleVisualization makeInstance() {
+ return new BubbleVisualization(this);
}
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/outlier/COPVectorVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/outlier/COPVectorVisualization.java
new file mode 100644
index 00000000..e953f9ae
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/outlier/COPVectorVisualization.java
@@ -0,0 +1,190 @@
+package de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.outlier;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.List;
+
+import org.apache.batik.util.SVGConstants;
+import org.w3c.dom.Element;
+
+import de.lmu.ifi.dbs.elki.algorithm.outlier.COP;
+import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.database.datastore.DataStoreListener;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.math.linearalgebra.VMath;
+import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector;
+import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
+import de.lmu.ifi.dbs.elki.result.Result;
+import de.lmu.ifi.dbs.elki.result.ResultUtil;
+import de.lmu.ifi.dbs.elki.result.outlier.OutlierResult;
+import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
+import de.lmu.ifi.dbs.elki.utilities.documentation.Title;
+import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
+import de.lmu.ifi.dbs.elki.visualization.css.CSSClass;
+import de.lmu.ifi.dbs.elki.visualization.projector.ScatterPlotProjector;
+import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.AbstractScatterplotVisualization;
+
+/**
+ * Visualize error vectors as produced by COP.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
+ * @apiviz.has OutlierResult oneway - - visualizes
+ */
+@Title("COP: Correlation Outlier Probability")
+@Reference(authors = "Hans-Peter Kriegel, Peer Kröger, Erich Schubert, Arthur Zimek", title = "Outlier Detection in Arbitrarily Oriented Subspaces", booktitle = "Proc. IEEE International Conference on Data Mining (ICDM 2012)")
+public class COPVectorVisualization extends AbstractVisFactory {
+ /**
+ * A short name characterizing this Visualizer.
+ */
+ public static final String NAME = "Error Vectors";
+
+ /**
+ * Constructor.
+ */
+ public COPVectorVisualization() {
+ super();
+ }
+
+ @Override
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
+ }
+
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ List<OutlierResult> ors = ResultUtil.filterResults(result, OutlierResult.class);
+ for (OutlierResult o : ors) {
+ List<Relation<?>> rels = ResultUtil.filterResults(o, Relation.class);
+ for (Relation<?> rel : rels) {
+ if (!rel.getShortName().equals(COP.COP_ERRORVEC)) {
+ continue;
+ }
+ List<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
+ boolean vis = true;
+ for (ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, rel, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_DATA;
+ if (!vis) {
+ task.initDefaultVisibility(false);
+ }
+ baseResult.getHierarchy().add(o, task);
+ baseResult.getHierarchy().add(p, task);
+ }
+ }
+ }
+ }
+
+ /**
+ * Visualize error vectors as produced by COP.
+ *
+ * @author Erich Schubert
+ */
+ public class Instance extends AbstractScatterplotVisualization implements DataStoreListener {
+ /**
+ * Generic tag to indicate the type of element. Used in IDs, CSS-Classes
+ * etc.
+ */
+ public static final String VEC = "copvec";
+
+ /**
+ * The outlier result to visualize
+ */
+ protected Relation<Vector> result;
+
+ /**
+ * Constructor.
+ *
+ * @param task Visualization task
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
+ this.result = task.getResult();
+ context.addDataStoreListener(this);
+ incrementalRedraw();
+ }
+
+ @Override
+ public void destroy() {
+ super.destroy();
+ context.removeDataStoreListener(this);
+ }
+
+ @Override
+ public void redraw() {
+ setupCSS(svgp);
+ for (DBIDIter objId = sample.getSample().iter(); objId.valid(); objId.advance()) {
+ Vector evec = result.get(objId);
+ if (evec == null) {
+ continue;
+ }
+ double[] ev = proj.fastProjectRelativeDataToRenderSpace(evec);
+ // TODO: avoid hard-coded plot threshold
+ if (VMath.euclideanLength(ev) < 0.01) {
+ continue;
+ }
+ final NumberVector<?> vec = rel.get(objId);
+ if (vec == null) {
+ continue;
+ }
+ double[] v = proj.fastProjectDataToRenderSpace(vec);
+ Element arrow = svgp.svgLine(v[0], v[1], v[0] + ev[0], v[1] + ev[1]);
+ SVGUtil.addCSSClass(arrow, VEC);
+ layer.appendChild(arrow);
+ }
+ }
+
+ @Override
+ public void resultChanged(Result current) {
+ if (sample == current) {
+ synchronizedRedraw();
+ }
+ }
+
+ /**
+ * Registers the COP error vector-CSS-Class at a SVGPlot.
+ *
+ * @param svgp the SVGPlot to register the Tooltip-CSS-Class.
+ */
+ private void setupCSS(SVGPlot svgp) {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ CSSClass bubble = new CSSClass(svgp, VEC);
+ bubble.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.PLOT) / 2);
+
+ // ColorLibrary colors = style.getColorSet(StyleLibrary.PLOT);
+ String color = "red"; // TODO: use style library
+ bubble.setStatement(SVGConstants.CSS_STROKE_VALUE, color);
+ bubble.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
+ svgp.addCSSClassOrLogError(bubble);
+ }
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/DistanceFunctionVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/DistanceFunctionVisualization.java
new file mode 100644
index 00000000..257a3c54
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/DistanceFunctionVisualization.java
@@ -0,0 +1,370 @@
+package de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.selection;
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.Collection;
+
+import org.apache.batik.util.SVGConstants;
+import org.w3c.dom.Element;
+
+import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.data.VectorUtil;
+import de.lmu.ifi.dbs.elki.database.datastore.DataStoreListener;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
+import de.lmu.ifi.dbs.elki.database.ids.DistanceDBIDPair;
+import de.lmu.ifi.dbs.elki.distance.distancefunction.ArcCosineDistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distancefunction.CosineDistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distancefunction.LPNormDistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResultIter;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
+import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
+import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance;
+import de.lmu.ifi.dbs.elki.index.preprocessed.knn.AbstractMaterializeKNNPreprocessor;
+import de.lmu.ifi.dbs.elki.logging.LoggingUtil;
+import de.lmu.ifi.dbs.elki.math.linearalgebra.VMath;
+import de.lmu.ifi.dbs.elki.result.DBIDSelection;
+import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
+import de.lmu.ifi.dbs.elki.result.Result;
+import de.lmu.ifi.dbs.elki.result.ResultUtil;
+import de.lmu.ifi.dbs.elki.result.SelectionResult;
+import de.lmu.ifi.dbs.elki.utilities.exceptions.ObjectNotFoundException;
+import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
+import de.lmu.ifi.dbs.elki.visualization.css.CSSClass;
+import de.lmu.ifi.dbs.elki.visualization.projections.CanvasSize;
+import de.lmu.ifi.dbs.elki.visualization.projections.Projection2D;
+import de.lmu.ifi.dbs.elki.visualization.projector.ScatterPlotProjector;
+import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGHyperSphere;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGPath;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.AbstractScatterplotVisualization;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.thumbs.ThumbnailVisualization;
+
+/**
+ * Factory for visualizers to generate an SVG-Element containing dots as markers
+ * representing the kNN of the selected Database objects.
+ *
+ * To use this, add a kNN preprocessor index to your database!
+ *
+ * @author Erich Schubert
+ * @author Robert Rödler
+ *
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
+ */
+// FIXME: for >2 dimensions, cosine doesn't seem to be correct yet.
+public class DistanceFunctionVisualization extends AbstractVisFactory {
+ /**
+ * A short name characterizing this Visualizer.
+ */
+ public static final String NAME = "k Nearest Neighbor Visualization";
+
+ /**
+ * Constructor
+ */
+ public DistanceFunctionVisualization() {
+ super();
+ thumbmask |= ThumbnailVisualization.ON_DATA | ThumbnailVisualization.ON_SELECTION;
+ }
+
+ @Override
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance<DoubleDistance>(task);
+ }
+
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<AbstractMaterializeKNNPreprocessor<?, ?, ?>> kNNIndex = ResultUtil.filterResults(result, AbstractMaterializeKNNPreprocessor.class);
+ for(AbstractMaterializeKNNPreprocessor<?, ?, ?> kNN : kNNIndex) {
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, kNN, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_DATA - 1;
+ baseResult.getHierarchy().add(kNN, task);
+ baseResult.getHierarchy().add(p, task);
+ }
+ }
+ }
+
+ /**
+ * Get the "p" value of an Lp norm.
+ *
+ * @param kNN kNN preprocessor
+ * @return p of LP norm, or NaN
+ */
+ public static double getLPNormP(AbstractMaterializeKNNPreprocessor<?, ?, ?> kNN) {
+ DistanceFunction<?, ?> distanceFunction = kNN.getDistanceQuery().getDistanceFunction();
+ if(LPNormDistanceFunction.class.isInstance(distanceFunction)) {
+ return ((LPNormDistanceFunction) distanceFunction).getP();
+ }
+ return Double.NaN;
+ }
+
+ /**
+ * Test whether the given preprocessor used an angular distance function
+ *
+ * @param kNN kNN preprocessor
+ * @return true when angular
+ */
+ public static boolean isAngularDistance(AbstractMaterializeKNNPreprocessor<?, ?, ?> kNN) {
+ DistanceFunction<?, ?> distanceFunction = kNN.getDistanceQuery().getDistanceFunction();
+ if(CosineDistanceFunction.class.isInstance(distanceFunction)) {
+ return true;
+ }
+ if(ArcCosineDistanceFunction.class.isInstance(distanceFunction)) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Visualizes Cosine and ArcCosine distance functions
+ *
+ * @param svgp SVG Plot
+ * @param proj Visualization projection
+ * @param mid mean vector
+ * @param angle Opening angle in radians
+ * @return path element
+ */
+ public static Element drawCosine(SVGPlot svgp, Projection2D proj, NumberVector<?> mid, double angle) {
+ // Project origin
+ double[] pointOfOrigin = proj.fastProjectDataToRenderSpace(new double[proj.getInputDimensionality()]);
+
+ // direction of the selected Point
+ double[] selPoint = proj.fastProjectDataToRenderSpace(mid);
+
+ double[] range1, range2;
+ {
+ // Rotation plane:
+ double[] p1 = proj.fastProjectRenderToDataSpace(new double[] { selPoint[0] + 10, selPoint[1] });
+ double[] p2 = proj.fastProjectRenderToDataSpace(new double[] { selPoint[0], selPoint[1] + 10 });
+ double[] pm = mid.getColumnVector().getArrayRef();
+ // Compute relative vectors
+ VMath.minusEquals(p1, pm);
+ VMath.minusEquals(p2, pm);
+ // Scale p1 and p2 to unit length:
+ VMath.timesEquals(p1, 1. / VMath.euclideanLength(p1));
+ VMath.timesEquals(p2, 1. / VMath.euclideanLength(p2));
+ {
+ double test = VMath.scalarProduct(p1, p2);
+ if(Math.abs(test) > 1E-10) {
+ LoggingUtil.warning("Projection does not seem to be orthogonal?");
+ }
+ }
+ // Project onto p1, p2:
+ double l1 = VMath.scalarProduct(pm, p1), l2 = VMath.scalarProduct(pm, p2);
+ // Rotate projection by + and - angle
+ // Using sin(-x) = -sin(x) and cos(-x)=cos(x)
+ final double cangle = Math.cos(angle), sangle = Math.sin(angle);
+ double r11 = +cangle * l1 - sangle * l2, r12 = +sangle * l1 + cangle * l2;
+ double r21 = +cangle * l1 + sangle * l2, r22 = -sangle * l1 + cangle * l2;
+ // Build rotated vectors - remove projected component, add rotated
+ // component:
+ double[] r1 = VMath.copy(pm), r2 = VMath.copy(pm);
+ VMath.plusTimesEquals(r1, p1, -l1 + r11);
+ VMath.plusTimesEquals(r1, p2, -l2 + r12);
+ VMath.plusTimesEquals(r2, p1, -l1 + r21);
+ VMath.plusTimesEquals(r2, p2, -l2 + r22);
+ // Project to render space:
+ range1 = proj.fastProjectDataToRenderSpace(r1);
+ range2 = proj.fastProjectDataToRenderSpace(r2);
+ }
+
+ // Continue lines to viewport.
+ {
+ CanvasSize viewport = proj.estimateViewport();
+ VMath.minusEquals(range1, pointOfOrigin);
+ VMath.minusEquals(range2, pointOfOrigin);
+ VMath.timesEquals(range1, viewport.continueToMargin(pointOfOrigin, range1));
+ VMath.timesEquals(range2, viewport.continueToMargin(pointOfOrigin, range2));
+ VMath.plusEquals(range1, pointOfOrigin);
+ VMath.plusEquals(range2, pointOfOrigin);
+ // Go backwards into the other direction - the origin might not be in the
+ // viewport!
+ double[] start1 = VMath.minus(pointOfOrigin, range1);
+ double[] start2 = VMath.minus(pointOfOrigin, range2);
+ VMath.timesEquals(start1, viewport.continueToMargin(range1, start1));
+ VMath.timesEquals(start2, viewport.continueToMargin(range2, start2));
+ VMath.plusEquals(start1, range1);
+ VMath.plusEquals(start2, range2);
+
+ // TODO: add filled variant?
+ SVGPath path = new SVGPath();
+ path.moveTo(start1);
+ path.lineTo(range1);
+ path.moveTo(start2);
+ path.lineTo(range2);
+ return path.makeElement(svgp);
+ }
+ }
+
+ /**
+ * Instance, visualizing a particular set of kNNs
+ *
+ * @author Robert Rödler
+ * @author Erich Schubert
+ *
+ * @apiviz.has SelectionResult oneway - - visualizes
+ * @apiviz.has DBIDSelection oneway - - visualizes
+ *
+ * @param <D> Distance type
+ */
+ public class Instance<D extends NumberDistance<D, ?>> extends AbstractScatterplotVisualization implements DataStoreListener {
+ /**
+ * Generic tags to indicate the type of element. Used in IDs, CSS-Classes
+ * etc.
+ */
+ public static final String KNNMARKER = "kNNMarker";
+
+ public static final String KNNDIST = "kNNDist";
+
+ public static final String DISTANCEFUNCTION = "distancefunction";
+
+ /**
+ * The selection result we work on
+ */
+ private AbstractMaterializeKNNPreprocessor<? extends NumberVector<?>, D, ?> result;
+
+ /**
+ * Constructor
+ *
+ * @param task VisualizationTask
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
+ this.result = task.getResult();
+ context.addDataStoreListener(this);
+ context.addResultListener(this);
+ incrementalRedraw();
+ }
+
+ @Override
+ protected void redraw() {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ addCSSClasses(svgp);
+ final double p = getLPNormP(result);
+ final boolean angular = isAngularDistance(result);
+
+ final double size = style.getSize(StyleLibrary.SELECTION);
+ DBIDSelection selContext = context.getSelection();
+ if(selContext != null) {
+ DBIDs selection = selContext.getSelectedIds();
+
+ for(DBIDIter i = selection.iter(); i.valid(); i.advance()) {
+ final KNNResult<D> knn = result.get(i);
+ for(DistanceDBIDResultIter<D> iter = knn.iter(); iter.valid(); iter.advance()) {
+ try {
+ double[] v = proj.fastProjectDataToRenderSpace(rel.get(iter));
+ Element dot = svgp.svgCircle(v[0], v[1], size);
+ SVGUtil.addCSSClass(dot, KNNMARKER);
+ layer.appendChild(dot);
+
+ Element lbl = svgp.svgText(v[0] + size, v[1] + size, iter.getDistance().toString());
+ SVGUtil.addCSSClass(lbl, KNNDIST);
+ layer.appendChild(lbl);
+ }
+ catch(ObjectNotFoundException e) {
+ // ignore
+ }
+ }
+ // Last element
+ DistanceDBIDPair<D> last = knn.get(knn.size() - 1);
+ // Draw hypersphere if possible
+ {
+ final Element dist;
+ if(p == 1.0) {
+ dist = SVGHyperSphere.drawManhattan(svgp, proj, rel.get(i), last.getDistance());
+ }
+ else if(p == 2.0) {
+ dist = SVGHyperSphere.drawEuclidean(svgp, proj, rel.get(i), last.getDistance());
+ }
+ else if(!Double.isNaN(p)) {
+ dist = SVGHyperSphere.drawLp(svgp, proj, rel.get(i), last.getDistance(), p);
+ }
+ else if(angular) {
+ final NumberVector<?> refvec = rel.get(i);
+ // Recompute the angle - it could be cosine or arccosine distance
+ double maxangle = Math.acos(VectorUtil.cosAngle(refvec, rel.get(last)));
+ dist = drawCosine(svgp, proj, refvec, maxangle);
+ }
+ else {
+ dist = null;
+ }
+ if(dist != null) {
+ SVGUtil.addCSSClass(dist, DISTANCEFUNCTION);
+ layer.appendChild(dist);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Adds the required CSS-Classes
+ *
+ * @param svgp SVG-Plot
+ */
+ private void addCSSClasses(SVGPlot svgp) {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ // Class for the distance markers
+ if(!svgp.getCSSClassManager().contains(KNNMARKER)) {
+ CSSClass cls = new CSSClass(this, KNNMARKER);
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_DARKGREEN_VALUE);
+ cls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, style.getOpacity(StyleLibrary.SELECTION));
+ svgp.addCSSClassOrLogError(cls);
+ }
+ // Class for the distance function
+ if(!svgp.getCSSClassManager().contains(DISTANCEFUNCTION)) {
+ CSSClass cls = new CSSClass(this, DISTANCEFUNCTION);
+ cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, SVGConstants.CSS_RED_VALUE);
+ cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.PLOT));
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
+ cls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+ cls.setStatement(SVGConstants.CSS_STROKE_LINEJOIN_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+ svgp.addCSSClassOrLogError(cls);
+ }
+ // Class for the distance label
+ if(!svgp.getCSSClassManager().contains(KNNDIST)) {
+ CSSClass cls = new CSSClass(this, KNNDIST);
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_BLACK_VALUE);
+ cls.setStatement(SVGConstants.CSS_FONT_SIZE_PROPERTY, style.getTextSize(StyleLibrary.PLOT));
+ cls.setStatement(SVGConstants.CSS_FONT_FAMILY_PROPERTY, style.getFontFamily(StyleLibrary.PLOT));
+ svgp.addCSSClassOrLogError(cls);
+ }
+ }
+
+ @Override
+ public void resultChanged(Result current) {
+ if(current instanceof SelectionResult) {
+ synchronizedRedraw();
+ return;
+ }
+ super.resultChanged(current);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/MoveObjectsToolVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/MoveObjectsToolVisualization.java
index 6b466a88..c2282bf1 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/MoveObjectsToolVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/MoveObjectsToolVisualization.java
@@ -56,179 +56,179 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.AbstractScatter
* @author Heidi Kolb
* @author Erich Schubert
*
- * @apiviz.has NumberVector oneway - - edits
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class MoveObjectsToolVisualization extends AbstractScatterplotVisualization implements DragListener {
+public class MoveObjectsToolVisualization extends AbstractVisFactory {
/**
* A short name characterizing this Visualizer.
*/
private static final String NAME = "Move Objects";
/**
- * CSS tag for our event rectangle
+ * Constructor
*/
- protected static final String CSS_ARROW = "moveArrow";
-
- /**
- * Element for the rectangle to add listeners
- */
- private Element etag;
-
- /**
- * Element to contain the drag arrow
- */
- private Element rtag;
-
- /**
- * Constructor.
- *
- * @param task Task
- */
- public MoveObjectsToolVisualization(VisualizationTask task) {
- super(task);
- incrementalRedraw();
+ public MoveObjectsToolVisualization() {
+ super();
}
@Override
- public void resultChanged(Result current) {
- if(sample == current) {
- synchronizedRedraw();
- }
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
}
@Override
- protected void redraw() {
- addCSSClasses(svgp);
-
- rtag = svgp.svgElement(SVGConstants.SVG_G_TAG);
- SVGUtil.addCSSClass(rtag, CSS_ARROW);
- layer.appendChild(rtag);
-
- DragableArea drag = new DragableArea(svgp, -0.6 * StyleLibrary.SCALE, -0.7 * StyleLibrary.SCALE, 1.3 * StyleLibrary.SCALE, 1.4 * StyleLibrary.SCALE, this);
- etag = drag.getElement();
- layer.appendChild(etag);
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<UpdatableDatabase> dbs = ResultUtil.filterResults(result, UpdatableDatabase.class);
+ if(dbs.isEmpty()) {
+ return;
+ }
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, p.getRelation(), p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_INTERACTIVE;
+ task.tool = true;
+ task.thumbnail = false;
+ task.noexport = true;
+ task.initDefaultVisibility(false);
+ // baseResult.getHierarchy().add(p.getRelation(), task);
+ baseResult.getHierarchy().add(p, task);
+ }
}
/**
- * Updates the objects with the given DBIDs It will be moved depending on the
- * given Vector
+ * Instance.
+ *
+ * @author Heidi Kolb
+ * @author Erich Schubert
*
- * @param dbids - DBIDs of the objects to move
- * @param movingVector - Vector for moving object
+ * @apiviz.has NumberVector oneway - - edits
*/
- // TODO: move to DatabaseUtil?
- private void updateDB(DBIDs dbids, Vector movingVector) {
- NumberVector<?, ?> nv = null;
- throw new AbortException("FIXME: INCOMPLETE TRANSITION");
- /*
- * database.accumulateDataStoreEvents();
- * Representation<DatabaseObjectMetadata> mrep =
- * database.getMetadataQuery(); for(DBID dbid : dbids) { NV obj =
- * database.get(dbid); // Copy metadata to keep DatabaseObjectMetadata meta
- * = mrep.get(dbid);
- *
- * Vector v = proj.projectDataToRenderSpace(obj); v.set(0, v.get(0) +
- * movingVector.get(0)); v.set(1, v.get(1) + movingVector.get(1)); NV nv =
- * proj.projectRenderToDataSpace(v, obj); nv.setID(obj.getID());
- *
- * try { database.delete(dbid); database.insert(new Pair<NV,
- * DatabaseObjectMetadata>(nv, meta)); } catch(UnableToComplyException e) {
- * de.lmu.ifi.dbs.elki.logging.LoggingUtil.exception(e); } }
- * database.flushDataStoreEvents();
+ public class Instance extends AbstractScatterplotVisualization implements DragListener {
+ /**
+ * CSS tag for our event rectangle
*/
- }
+ protected static final String CSS_ARROW = "moveArrow";
- /**
- * Delete the children of the element
- *
- * @param container SVG-Element
- */
- private void deleteChildren(Element container) {
- while(container.hasChildNodes()) {
- container.removeChild(container.getLastChild());
+ /**
+ * Element for the rectangle to add listeners
+ */
+ private Element etag;
+
+ /**
+ * Element to contain the drag arrow
+ */
+ private Element rtag;
+
+ /**
+ * Constructor.
+ *
+ * @param task Task
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
+ incrementalRedraw();
}
- }
- /**
- * Adds the required CSS-Classes
- *
- * @param svgp SVGPlot
- */
- private void addCSSClasses(SVGPlot svgp) {
- // Class for the rectangle to add eventListeners
- if(!svgp.getCSSClassManager().contains(CSS_ARROW)) {
- final CSSClass acls = new CSSClass(this, CSS_ARROW);
- final StyleLibrary style = context.getStyleLibrary();
- acls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, style.getColor(StyleLibrary.SELECTION_ACTIVE));
- acls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.SELECTION_ACTIVE));
- acls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
- svgp.addCSSClassOrLogError(acls);
+ @Override
+ public void resultChanged(Result current) {
+ if(sample == current) {
+ synchronizedRedraw();
+ }
}
- }
- @Override
- public boolean startDrag(SVGPoint startPoint, Event evt) {
- return true;
- }
+ @Override
+ protected void redraw() {
+ addCSSClasses(svgp);
- @Override
- public boolean duringDrag(SVGPoint startPoint, SVGPoint dragPoint, Event evt, boolean inside) {
- deleteChildren(rtag);
- rtag.appendChild(svgp.svgLine(startPoint.getX(), startPoint.getY(), dragPoint.getX(), dragPoint.getY()));
- return true;
- }
+ rtag = svgp.svgElement(SVGConstants.SVG_G_TAG);
+ SVGUtil.addCSSClass(rtag, CSS_ARROW);
+ layer.appendChild(rtag);
- @Override
- public boolean endDrag(SVGPoint startPoint, SVGPoint dragPoint, Event evt, boolean inside) {
- Vector movingVector = new Vector(2);
- movingVector.set(0, dragPoint.getX() - startPoint.getX());
- movingVector.set(1, dragPoint.getY() - startPoint.getY());
- if(context.getSelection() != null) {
- updateDB(context.getSelection().getSelectedIds(), movingVector);
+ DragableArea drag = new DragableArea(svgp, -0.6 * StyleLibrary.SCALE, -0.7 * StyleLibrary.SCALE, 1.3 * StyleLibrary.SCALE, 1.4 * StyleLibrary.SCALE, this);
+ etag = drag.getElement();
+ layer.appendChild(etag);
}
- deleteChildren(rtag);
- return true;
- }
- /**
- * Factory for tool visualizations for changing objects in the database
- *
- * @author Heidi Kolb
- * @author Erich Schubert
- *
- * @apiviz.stereotype factory
- * @apiviz.uses MoveObjectsToolVisualization oneway - - «create»
- */
- public static class Factory extends AbstractVisFactory {
/**
- * Constructor
+ * Updates the objects with the given DBIDs It will be moved depending on
+ * the given Vector
+ *
+ * @param dbids - DBIDs of the objects to move
+ * @param movingVector - Vector for moving object
*/
- public Factory() {
- super();
+ // TODO: move to DatabaseUtil?
+ private void updateDB(DBIDs dbids, Vector movingVector) {
+ NumberVector<?> nv = null;
+ throw new AbortException("FIXME: INCOMPLETE TRANSITION");
+ /*
+ * database.accumulateDataStoreEvents();
+ * Representation<DatabaseObjectMetadata> mrep =
+ * database.getMetadataQuery(); for(DBID dbid : dbids) { NV obj =
+ * database.get(dbid); // Copy metadata to keep DatabaseObjectMetadata
+ * meta = mrep.get(dbid);
+ *
+ * Vector v = proj.projectDataToRenderSpace(obj); v.set(0, v.get(0) +
+ * movingVector.get(0)); v.set(1, v.get(1) + movingVector.get(1)); NV nv =
+ * proj.projectRenderToDataSpace(v, obj); nv.setID(obj.getID());
+ *
+ * try { database.delete(dbid); database.insert(new Pair<NV,
+ * DatabaseObjectMetadata>(nv, meta)); } catch(UnableToComplyException e)
+ * { de.lmu.ifi.dbs.elki.logging.LoggingUtil.exception(e); } }
+ * database.flushDataStoreEvents();
+ */
+ }
+
+ /**
+ * Delete the children of the element
+ *
+ * @param container SVG-Element
+ */
+ private void deleteChildren(Element container) {
+ while(container.hasChildNodes()) {
+ container.removeChild(container.getLastChild());
+ }
+ }
+
+ /**
+ * Adds the required CSS-Classes
+ *
+ * @param svgp SVGPlot
+ */
+ private void addCSSClasses(SVGPlot svgp) {
+ // Class for the rectangle to add eventListeners
+ if(!svgp.getCSSClassManager().contains(CSS_ARROW)) {
+ final CSSClass acls = new CSSClass(this, CSS_ARROW);
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ acls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, style.getColor(StyleLibrary.SELECTION_ACTIVE));
+ acls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.SELECTION_ACTIVE));
+ acls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+ svgp.addCSSClassOrLogError(acls);
+ }
}
@Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new MoveObjectsToolVisualization(task);
+ public boolean startDrag(SVGPoint startPoint, Event evt) {
+ return true;
}
@Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<UpdatableDatabase> dbs = ResultUtil.filterResults(result, UpdatableDatabase.class);
- if(dbs.isEmpty()) {
- return;
- }
- Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, p.getRelation(), p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_INTERACTIVE);
- task.put(VisualizationTask.META_TOOL, true);
- task.put(VisualizationTask.META_NOTHUMB, true);
- task.put(VisualizationTask.META_NOEXPORT, true);
- task.put(VisualizationTask.META_VISIBLE_DEFAULT, false);
- // baseResult.getHierarchy().add(p.getRelation(), task);
- baseResult.getHierarchy().add(p, task);
+ public boolean duringDrag(SVGPoint startPoint, SVGPoint dragPoint, Event evt, boolean inside) {
+ deleteChildren(rtag);
+ rtag.appendChild(svgp.svgLine(startPoint.getX(), startPoint.getY(), dragPoint.getX(), dragPoint.getY()));
+ return true;
+ }
+
+ @Override
+ public boolean endDrag(SVGPoint startPoint, SVGPoint dragPoint, Event evt, boolean inside) {
+ Vector movingVector = new Vector(2);
+ movingVector.set(0, dragPoint.getX() - startPoint.getX());
+ movingVector.set(1, dragPoint.getY() - startPoint.getY());
+ if(context.getSelection() != null) {
+ updateDB(context.getSelection().getSelectedIds(), movingVector);
}
+ deleteChildren(rtag);
+ return true;
}
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionConvexHullVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionConvexHullVisualization.java
index 8a28ab3e..f3d41e08 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionConvexHullVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionConvexHullVisualization.java
@@ -58,114 +58,115 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.thumbs.ThumbnailVisualizati
*
* @author Robert Rödler
*
- * @apiviz.has SelectionResult oneway - - visualizes
- * @apiviz.has DBIDSelection oneway - - visualizes
- * @apiviz.uses GrahamScanConvexHull2D
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class SelectionConvexHullVisualization extends AbstractScatterplotVisualization implements DataStoreListener {
+public class SelectionConvexHullVisualization extends AbstractVisFactory {
/**
* A short name characterizing this Visualizer.
*/
private static final String NAME = "Convex Hull of Selection";
/**
- * Generic tag to indicate the type of element. Used in IDs, CSS-Classes etc.
+ * Constructor
*/
- public static final String SELECTEDHULL = "selectionConvexHull";
-
- /**
- * Constructor.
- *
- * @param task Task
- */
- public SelectionConvexHullVisualization(VisualizationTask task) {
- super(task);
- context.addResultListener(this);
- context.addDataStoreListener(this);
- incrementalRedraw();
+ public SelectionConvexHullVisualization() {
+ super();
+ thumbmask |= ThumbnailVisualization.ON_DATA | ThumbnailVisualization.ON_SELECTION;
}
@Override
- protected void redraw() {
- addCSSClasses(svgp);
- DBIDSelection selContext = context.getSelection();
- if(selContext != null) {
- DBIDs selection = selContext.getSelectedIds();
- GrahamScanConvexHull2D hull = new GrahamScanConvexHull2D();
- for(DBIDIter iter = selection.iter(); iter.valid(); iter.advance()) {
- try {
- hull.add(new Vector(proj.fastProjectDataToRenderSpace(rel.get(iter))));
- }
- catch(ObjectNotFoundException e) {
- // ignore
- }
- }
- Polygon chres = hull.getHull();
- if(chres != null && chres.size() >= 3) {
- SVGPath path = new SVGPath(chres);
-
- Element selHull = path.makeElement(svgp);
- SVGUtil.addCSSClass(selHull, SELECTEDHULL);
- // TODO: use relative selection size for opacity?
- layer.appendChild(selHull);
- }
- }
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
}
- /**
- * Adds the required CSS-Classes
- *
- * @param svgp SVG-Plot
- */
- private void addCSSClasses(SVGPlot svgp) {
- // Class for the dot markers
- if(!svgp.getCSSClassManager().contains(SELECTEDHULL)) {
- CSSClass cls = new CSSClass(this, SELECTEDHULL);
- // cls = new CSSClass(this, CONVEXHULL);
- cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, context.getStyleLibrary().getColor(StyleLibrary.SELECTION));
- cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, context.getStyleLibrary().getLineWidth(StyleLibrary.SELECTION));
- cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, context.getStyleLibrary().getColor(StyleLibrary.SELECTION));
- cls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, ".25");
- cls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
- cls.setStatement(SVGConstants.CSS_STROKE_LINEJOIN_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
- svgp.addCSSClassOrLogError(cls);
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<SelectionResult> selectionResults = ResultUtil.filterResults(result, SelectionResult.class);
+ for(SelectionResult selres : selectionResults) {
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, selres, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_DATA - 2;
+ baseResult.getHierarchy().add(selres, task);
+ baseResult.getHierarchy().add(p, task);
+ }
}
}
/**
- * Factory for visualizers to generate an SVG-Element containing the convex
- * hull of the selected points
+ * Instance
*
* @author Robert Rödler
*
- * @apiviz.stereotype factory
- * @apiviz.uses SelectionConvexHullVisualization oneway - - «create»
+ * @apiviz.has SelectionResult oneway - - visualizes
+ * @apiviz.has DBIDSelection oneway - - visualizes
+ * @apiviz.uses GrahamScanConvexHull2D
*/
- public static class Factory extends AbstractVisFactory {
+ public class Instance extends AbstractScatterplotVisualization implements DataStoreListener {
/**
- * Constructor
+ * Generic tag to indicate the type of element. Used in IDs, CSS-Classes
+ * etc.
*/
- public Factory() {
- super();
- thumbmask |= ThumbnailVisualization.ON_DATA | ThumbnailVisualization.ON_SELECTION;
- }
+ public static final String SELECTEDHULL = "selectionConvexHull";
- @Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new SelectionConvexHullVisualization(task);
+ /**
+ * Constructor.
+ *
+ * @param task Task
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
+ context.addResultListener(this);
+ context.addDataStoreListener(this);
+ incrementalRedraw();
}
@Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<SelectionResult> selectionResults = ResultUtil.filterResults(result, SelectionResult.class);
- for(SelectionResult selres : selectionResults) {
- Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, selres, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA - 2);
- baseResult.getHierarchy().add(selres, task);
- baseResult.getHierarchy().add(p, task);
+ protected void redraw() {
+ addCSSClasses(svgp);
+ DBIDSelection selContext = context.getSelection();
+ if(selContext != null) {
+ DBIDs selection = selContext.getSelectedIds();
+ GrahamScanConvexHull2D hull = new GrahamScanConvexHull2D();
+ for(DBIDIter iter = selection.iter(); iter.valid(); iter.advance()) {
+ try {
+ hull.add(new Vector(proj.fastProjectDataToRenderSpace(rel.get(iter))));
+ }
+ catch(ObjectNotFoundException e) {
+ // ignore
+ }
}
+ Polygon chres = hull.getHull();
+ if(chres != null && chres.size() >= 3) {
+ SVGPath path = new SVGPath(chres);
+
+ Element selHull = path.makeElement(svgp);
+ SVGUtil.addCSSClass(selHull, SELECTEDHULL);
+ // TODO: use relative selection size for opacity?
+ layer.appendChild(selHull);
+ }
+ }
+ }
+
+ /**
+ * Adds the required CSS-Classes
+ *
+ * @param svgp SVG-Plot
+ */
+ private void addCSSClasses(SVGPlot svgp) {
+ // Class for the dot markers
+ if(!svgp.getCSSClassManager().contains(SELECTEDHULL)) {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ CSSClass cls = new CSSClass(this, SELECTEDHULL);
+ // cls = new CSSClass(this, CONVEXHULL);
+ cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, style.getColor(StyleLibrary.SELECTION));
+ cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.SELECTION));
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, style.getColor(StyleLibrary.SELECTION));
+ cls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, ".25");
+ cls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+ cls.setStatement(SVGConstants.CSS_STROKE_LINEJOIN_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+ svgp.addCSSClassOrLogError(cls);
}
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionCubeVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionCubeVisualization.java
index 5151e81e..6d4c14ab 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionCubeVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionCubeVisualization.java
@@ -28,13 +28,13 @@ import java.util.Collection;
import org.apache.batik.util.SVGConstants;
import org.w3c.dom.Element;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.result.DBIDSelection;
import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
import de.lmu.ifi.dbs.elki.result.RangeSelection;
import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
import de.lmu.ifi.dbs.elki.result.SelectionResult;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
@@ -59,141 +59,179 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.thumbs.ThumbnailVisualizati
*
* @author Heidi Kolb
*
- * @apiviz.has SelectionResult oneway - - visualizes
- * @apiviz.has RangeSelection oneway - - visualizes
- * @apiviz.uses SVGHyperCube
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class SelectionCubeVisualization extends AbstractScatterplotVisualization {
+public class SelectionCubeVisualization extends AbstractVisFactory {
/**
* A short name characterizing this Visualizer.
*/
private static final String NAME = "Selection Range";
/**
- * Generic tag to indicate the type of element. Used in IDs, CSS-Classes etc.
+ * Settings
*/
- public static final String MARKER = "selectionCubeMarker";
+ protected Parameterizer settings;
/**
- * CSS class for the filled cube
- */
- public static final String CSS_CUBE = "selectionCube";
-
- /**
- * CSS class for the cube frame
- */
- public static final String CSS_CUBEFRAME = "selectionCubeFrame";
-
- /**
- * Fill parameter.
+ * Constructor.
+ *
+ * @param settings Settings
*/
- protected boolean nofill = false;
-
- public SelectionCubeVisualization(VisualizationTask task, boolean nofill) {
- super(task);
- this.nofill = nofill;
- addCSSClasses(svgp);
- context.addResultListener(this);
- incrementalRedraw();
+ public SelectionCubeVisualization(Parameterizer settings) {
+ super();
+ this.settings = settings;
+ thumbmask |= ThumbnailVisualization.ON_DATA | ThumbnailVisualization.ON_SELECTION;
}
@Override
- public void destroy() {
- context.removeResultListener(this);
- super.destroy();
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
}
- /**
- * Adds the required CSS-Classes
- *
- * @param svgp SVG-Plot
- */
- private void addCSSClasses(SVGPlot svgp) {
- final StyleLibrary style = context.getStyleLibrary();
- // Class for the cube
- if(!svgp.getCSSClassManager().contains(CSS_CUBE)) {
- CSSClass cls = new CSSClass(this, CSS_CUBE);
- cls.setStatement(SVGConstants.CSS_STROKE_VALUE, style.getColor(StyleLibrary.SELECTION));
- cls.setStatement(SVGConstants.CSS_STROKE_OPACITY_PROPERTY, style.getOpacity(StyleLibrary.SELECTION));
- cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.PLOT));
- cls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
- cls.setStatement(SVGConstants.CSS_STROKE_LINEJOIN_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
- if(nofill) {
- cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
- }
- else {
- cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, style.getColor(StyleLibrary.SELECTION));
- cls.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, style.getOpacity(StyleLibrary.SELECTION));
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<SelectionResult> selectionResults = ResultUtil.filterResults(result, SelectionResult.class);
+ for(SelectionResult selres : selectionResults) {
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, selres, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_DATA - 2;
+ baseResult.getHierarchy().add(selres, task);
+ baseResult.getHierarchy().add(p, task);
}
- svgp.addCSSClassOrLogError(cls);
- }
- // Class for the cube frame
- if(!svgp.getCSSClassManager().contains(CSS_CUBEFRAME)) {
- CSSClass cls = new CSSClass(this, CSS_CUBEFRAME);
- cls.setStatement(SVGConstants.CSS_STROKE_VALUE, style.getColor(StyleLibrary.SELECTION));
- cls.setStatement(SVGConstants.CSS_STROKE_OPACITY_PROPERTY, style.getOpacity(StyleLibrary.SELECTION));
- cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.SELECTION));
-
- svgp.addCSSClassOrLogError(cls);
}
}
/**
- * Generates a cube and a frame depending on the selection stored in the
- * context
+ * Instance.
+ *
+ * @author Heidi Kolb
*
- * @param svgp The plot
- * @param proj The projection
+ * @apiviz.has SelectionResult oneway - - visualizes
+ * @apiviz.has RangeSelection oneway - - visualizes
+ * @apiviz.uses SVGHyperCube
*/
- private void setSVGRect(SVGPlot svgp, Projection2D proj) {
- DBIDSelection selContext = context.getSelection();
- if(selContext instanceof RangeSelection) {
- DoubleDoublePair[] ranges = ((RangeSelection) selContext).getRanges();
- int dim = DatabaseUtil.dimensionality(rel);
+ public class Instance extends AbstractScatterplotVisualization {
+ /**
+ * Generic tag to indicate the type of element. Used in IDs, CSS-Classes
+ * etc.
+ */
+ public static final String MARKER = "selectionCubeMarker";
+
+ /**
+ * CSS class for the filled cube
+ */
+ public static final String CSS_CUBE = "selectionCube";
+
+ /**
+ * CSS class for the cube frame
+ */
+ public static final String CSS_CUBEFRAME = "selectionCubeFrame";
+
+ public Instance(VisualizationTask task) {
+ super(task);
+ addCSSClasses(svgp);
+ context.addResultListener(this);
+ incrementalRedraw();
+ }
- double[] min = new double[dim];
- double[] max = new double[dim];
- for(int d = 0; d < dim; d++) {
- if(ranges != null && ranges[d] != null) {
- min[d] = ranges[d].first;
- max[d] = ranges[d].second;
+ @Override
+ public void destroy() {
+ context.removeResultListener(this);
+ super.destroy();
+ }
+
+ /**
+ * Adds the required CSS-Classes
+ *
+ * @param svgp SVG-Plot
+ */
+ private void addCSSClasses(SVGPlot svgp) {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ // Class for the cube
+ if(!svgp.getCSSClassManager().contains(CSS_CUBE)) {
+ CSSClass cls = new CSSClass(this, CSS_CUBE);
+ cls.setStatement(SVGConstants.CSS_STROKE_VALUE, style.getColor(StyleLibrary.SELECTION));
+ cls.setStatement(SVGConstants.CSS_STROKE_OPACITY_PROPERTY, style.getOpacity(StyleLibrary.SELECTION));
+ cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.PLOT));
+ cls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+ cls.setStatement(SVGConstants.CSS_STROKE_LINEJOIN_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+ if(settings.nofill) {
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
}
else {
- min[d] = proj.getScale(d).getMin();
- max[d] = proj.getScale(d).getMax();
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, style.getColor(StyleLibrary.SELECTION));
+ cls.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, style.getOpacity(StyleLibrary.SELECTION));
}
+ svgp.addCSSClassOrLogError(cls);
}
- if(nofill) {
- Element r = SVGHyperCube.drawFrame(svgp, proj, min, max);
- SVGUtil.setCSSClass(r, CSS_CUBEFRAME);
- layer.appendChild(r);
- }
- else {
- Element r = SVGHyperCube.drawFilled(svgp, CSS_CUBE, proj, min, max);
- layer.appendChild(r);
+ // Class for the cube frame
+ if(!svgp.getCSSClassManager().contains(CSS_CUBEFRAME)) {
+ CSSClass cls = new CSSClass(this, CSS_CUBEFRAME);
+ cls.setStatement(SVGConstants.CSS_STROKE_VALUE, style.getColor(StyleLibrary.SELECTION));
+ cls.setStatement(SVGConstants.CSS_STROKE_OPACITY_PROPERTY, style.getOpacity(StyleLibrary.SELECTION));
+ cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.SELECTION));
+
+ svgp.addCSSClassOrLogError(cls);
}
+ }
+
+ /**
+ * Generates a cube and a frame depending on the selection stored in the
+ * context
+ *
+ * @param svgp The plot
+ * @param proj The projection
+ */
+ private void setSVGRect(SVGPlot svgp, Projection2D proj) {
+ DBIDSelection selContext = context.getSelection();
+ if(selContext instanceof RangeSelection) {
+ DoubleDoublePair[] ranges = ((RangeSelection) selContext).getRanges();
+ int dim = RelationUtil.dimensionality(rel);
+
+ double[] min = new double[dim];
+ double[] max = new double[dim];
+ for(int d = 0; d < dim; d++) {
+ if(ranges != null && ranges[d] != null) {
+ min[d] = ranges[d].first;
+ max[d] = ranges[d].second;
+ }
+ else {
+ min[d] = proj.getScale(d).getMin();
+ max[d] = proj.getScale(d).getMax();
+ }
+ }
+ if(settings.nofill) {
+ Element r = SVGHyperCube.drawFrame(svgp, proj, min, max);
+ SVGUtil.setCSSClass(r, CSS_CUBEFRAME);
+ layer.appendChild(r);
+ }
+ else {
+ Element r = SVGHyperCube.drawFilled(svgp, CSS_CUBE, proj, min, max);
+ layer.appendChild(r);
+ }
+ }
}
- }
- @Override
- protected void redraw() {
- DBIDSelection selContext = context.getSelection();
- if(selContext != null && selContext instanceof RangeSelection) {
- setSVGRect(svgp, proj);
+ @Override
+ protected void redraw() {
+ DBIDSelection selContext = context.getSelection();
+ if(selContext instanceof RangeSelection) {
+ setSVGRect(svgp, proj);
+ }
}
}
/**
- * Factory for visualizers to generate an SVG-Element containing a cube as
- * marker representing the selected range for each dimension
+ * Parameterization class.
*
- * @author Heidi Kolb
+ * @author Erich Schubert
*
- * @apiviz.stereotype factory
- * @apiviz.uses SelectionCubeVisualization oneway - - «create»
+ * @apiviz.exclude
*/
- public static class Factory extends AbstractVisFactory {
+ public static class Parameterizer extends AbstractParameterizer {
/**
* Flag for half-transparent filling of selection cubes.
*
@@ -201,66 +239,25 @@ public class SelectionCubeVisualization extends AbstractScatterplotVisualization
* Key: {@code -selectionrange.nofill}
* </p>
*/
- public static final OptionID NOFILL_ID = OptionID.getOrCreateOptionID("selectionrange.nofill", "Use wireframe style for selection ranges.");
+ public static final OptionID NOFILL_ID = new OptionID("selectionrange.nofill", "Use wireframe style for selection ranges.");
/**
* Fill parameter.
*/
- protected boolean nofill = false;
-
- /**
- * Constructor.
- *
- * @param nofill
- */
- public Factory(boolean nofill) {
- super();
- this.nofill = nofill;
- thumbmask |= ThumbnailVisualization.ON_DATA | ThumbnailVisualization.ON_SELECTION;
- }
+ protected boolean nofill;
@Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new SelectionCubeVisualization(task, nofill);
- }
-
- @Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<SelectionResult> selectionResults = ResultUtil.filterResults(result, SelectionResult.class);
- for(SelectionResult selres : selectionResults) {
- Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, selres, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA - 2);
- baseResult.getHierarchy().add(selres, task);
- baseResult.getHierarchy().add(p, task);
- }
+ protected void makeOptions(Parameterization config) {
+ super.makeOptions(config);
+ Flag nofillF = new Flag(NOFILL_ID);
+ if(config.grab(nofillF)) {
+ nofill = nofillF.isTrue();
}
}
- /**
- * Parameterization class.
- *
- * @author Erich Schubert
- *
- * @apiviz.exclude
- */
- public static class Parameterizer extends AbstractParameterizer {
- protected boolean nofill;
-
- @Override
- protected void makeOptions(Parameterization config) {
- super.makeOptions(config);
- Flag nofillF = new Flag(NOFILL_ID);
- if(config.grab(nofillF)) {
- nofill = nofillF.getValue();
- }
- }
-
- @Override
- protected Factory makeInstance() {
- return new Factory(nofill);
- }
+ @Override
+ protected SelectionCubeVisualization makeInstance() {
+ return new SelectionCubeVisualization(this);
}
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionDotVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionDotVisualization.java
index 1dc5ce13..b70755c4 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionDotVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionDotVisualization.java
@@ -54,110 +54,111 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.thumbs.ThumbnailVisualizati
*
* @author Heidi Kolb
*
- * @apiviz.has SelectionResult oneway - - visualizes
- * @apiviz.has DBIDSelection oneway - - visualizes
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class SelectionDotVisualization extends AbstractScatterplotVisualization implements DataStoreListener {
+public class SelectionDotVisualization extends AbstractVisFactory {
/**
* A short name characterizing this Visualizer.
*/
private static final String NAME = "Selection";
/**
- * Generic tag to indicate the type of element. Used in IDs, CSS-Classes etc.
+ * Constructor
*/
- public static final String MARKER = "selectionDotMarker";
-
- /**
- * Constructor.
- *
- * @param task Task
- */
- public SelectionDotVisualization(VisualizationTask task) {
- super(task);
- context.addResultListener(this);
- context.addDataStoreListener(this);
- incrementalRedraw();
+ public SelectionDotVisualization() {
+ super();
+ thumbmask |= ThumbnailVisualization.ON_DATA | ThumbnailVisualization.ON_SELECTION;
}
@Override
- public void destroy() {
- context.removeResultListener(this);
- super.destroy();
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
}
@Override
- protected void redraw() {
- addCSSClasses(svgp);
- final double size = context.getStyleLibrary().getSize(StyleLibrary.SELECTION);
- DBIDSelection selContext = context.getSelection();
- if(selContext != null) {
- DBIDs selection = selContext.getSelectedIds();
- for(DBIDIter iter = selection.iter(); iter.valid(); iter.advance()) {
- try {
- double[] v = proj.fastProjectDataToRenderSpace(rel.get(iter));
- Element dot = svgp.svgCircle(v[0], v[1], size);
- SVGUtil.addCSSClass(dot, MARKER);
- layer.appendChild(dot);
- }
- catch(ObjectNotFoundException e) {
- // ignore
- }
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<SelectionResult> selectionResults = ResultUtil.filterResults(result, SelectionResult.class);
+ for(SelectionResult selres : selectionResults) {
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, selres, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_DATA - 1;
+ baseResult.getHierarchy().add(selres, task);
+ baseResult.getHierarchy().add(p, task);
}
}
}
/**
- * Adds the required CSS-Classes
- *
- * @param svgp SVG-Plot
- */
- private void addCSSClasses(SVGPlot svgp) {
- // Class for the dot markers
- if(!svgp.getCSSClassManager().contains(MARKER)) {
- CSSClass cls = new CSSClass(this, MARKER);
- final StyleLibrary style = context.getStyleLibrary();
- cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, style.getColor(StyleLibrary.SELECTION));
- cls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, style.getOpacity(StyleLibrary.SELECTION));
- svgp.addCSSClassOrLogError(cls);
- }
- }
-
- /**
- * Factory for visualizers to generate an SVG-Element containing dots as
- * markers representing the selected Database's objects.
+ * Instance
*
* @author Heidi Kolb
*
- * @apiviz.stereotype factory
- * @apiviz.uses SelectionDotVisualization oneway - - «create»
+ * @apiviz.has SelectionResult oneway - - visualizes
+ * @apiviz.has DBIDSelection oneway - - visualizes
*/
- public static class Factory extends AbstractVisFactory {
+ public class Instance extends AbstractScatterplotVisualization implements DataStoreListener {
+ /**
+ * Generic tag to indicate the type of element. Used in IDs, CSS-Classes
+ * etc.
+ */
+ public static final String MARKER = "selectionDotMarker";
+
/**
- * Constructor
+ * Constructor.
+ *
+ * @param task Task
*/
- public Factory() {
- super();
- thumbmask |= ThumbnailVisualization.ON_DATA | ThumbnailVisualization.ON_SELECTION;
+ public Instance(VisualizationTask task) {
+ super(task);
+ context.addResultListener(this);
+ context.addDataStoreListener(this);
+ incrementalRedraw();
}
@Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new SelectionDotVisualization(task);
+ public void destroy() {
+ context.removeResultListener(this);
+ super.destroy();
}
@Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<SelectionResult> selectionResults = ResultUtil.filterResults(result, SelectionResult.class);
- for(SelectionResult selres : selectionResults) {
- Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, selres, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA - 1);
- baseResult.getHierarchy().add(selres, task);
- baseResult.getHierarchy().add(p, task);
+ protected void redraw() {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ addCSSClasses(svgp);
+ final double size = style.getSize(StyleLibrary.SELECTION);
+ DBIDSelection selContext = context.getSelection();
+ if(selContext != null) {
+ DBIDs selection = selContext.getSelectedIds();
+ for(DBIDIter iter = selection.iter(); iter.valid(); iter.advance()) {
+ try {
+ double[] v = proj.fastProjectDataToRenderSpace(rel.get(iter));
+ Element dot = svgp.svgCircle(v[0], v[1], size);
+ SVGUtil.addCSSClass(dot, MARKER);
+ layer.appendChild(dot);
+ }
+ catch(ObjectNotFoundException e) {
+ // ignore
+ }
}
}
}
+
+ /**
+ * Adds the required CSS-Classes
+ *
+ * @param svgp SVG-Plot
+ */
+ private void addCSSClasses(SVGPlot svgp) {
+ // Class for the dot markers
+ if(!svgp.getCSSClassManager().contains(MARKER)) {
+ CSSClass cls = new CSSClass(this, MARKER);
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, style.getColor(StyleLibrary.SELECTION));
+ cls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, style.getOpacity(StyleLibrary.SELECTION));
+ svgp.addCSSClassOrLogError(cls);
+ }
+ }
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionToolCubeVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionToolCubeVisualization.java
index bc16fd83..f088d219 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionToolCubeVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionToolCubeVisualization.java
@@ -35,6 +35,7 @@ import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.ModifiableDBIDs;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.result.DBIDSelection;
import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
@@ -42,7 +43,6 @@ import de.lmu.ifi.dbs.elki.result.RangeSelection;
import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
import de.lmu.ifi.dbs.elki.result.SelectionResult;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.utilities.pairs.DoubleDoublePair;
import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
import de.lmu.ifi.dbs.elki.visualization.batikutil.DragableArea;
@@ -57,18 +57,18 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
import de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.AbstractScatterplotVisualization;
/**
- * Tool-Visualization for the tool to select ranges
+ * Tool-Visualization for the tool to select ranges.
*
* @author Heidi Kolb
*
- * @apiviz.has SelectionResult oneway - - updates
- * @apiviz.has RangeSelection oneway - - updates
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class SelectionToolCubeVisualization extends AbstractScatterplotVisualization implements DragableArea.DragListener {
+public class SelectionToolCubeVisualization extends AbstractVisFactory {
/**
* The logger for this class.
*/
- protected static final Logging logger = Logging.getLogger(SelectionToolCubeVisualization.class);
+ private static final Logging LOG = Logging.getLogger(SelectionToolCubeVisualization.class);
/**
* A short name characterizing this Visualizer.
@@ -76,219 +76,218 @@ public class SelectionToolCubeVisualization extends AbstractScatterplotVisualiza
private static final String NAME = "Range Selection";
/**
- * Generic tag to indicate the type of element. Used in IDs, CSS-Classes etc.
+ * Constructor, adhering to
+ * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}.
*/
- private static final String CSS_RANGEMARKER = "selectionRangeMarker";
-
- /**
- * Dimension
- */
- private int dim;
-
- /**
- * Element for selection rectangle
- */
- private Element rtag;
-
- /**
- * Element for the rectangle to add listeners
- */
- private Element etag;
-
- /**
- * Constructor.
- *
- * @param task Task
- */
- public SelectionToolCubeVisualization(VisualizationTask task) {
- super(task);
- this.dim = DatabaseUtil.dimensionality(rel);
- incrementalRedraw();
+ public SelectionToolCubeVisualization() {
+ super();
}
@Override
- protected void redraw() {
- addCSSClasses(svgp);
-
- // rtag: tag for the selected rect
- rtag = svgp.svgElement(SVGConstants.SVG_G_TAG);
- SVGUtil.addCSSClass(rtag, CSS_RANGEMARKER);
- layer.appendChild(rtag);
-
- // etag: sensitive area
- DragableArea drag = new DragableArea(svgp, -0.6 * StyleLibrary.SCALE, -0.7 * StyleLibrary.SCALE, 1.3 * StyleLibrary.SCALE, 1.4 * StyleLibrary.SCALE, this);
- etag = drag.getElement();
- layer.appendChild(etag);
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
}
- /**
- * Delete the children of the element
- *
- * @param container SVG-Element
- */
- private void deleteChildren(Element container) {
- while(container.hasChildNodes()) {
- container.removeChild(container.getLastChild());
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<SelectionResult> selectionResults = ResultUtil.filterResults(result, SelectionResult.class);
+ for (SelectionResult selres : selectionResults) {
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
+ for (ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, selres, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_INTERACTIVE;
+ task.tool = true;
+ task.thumbnail = false;
+ task.noexport = true;
+ task.initDefaultVisibility(false);
+ baseResult.getHierarchy().add(selres, task);
+ baseResult.getHierarchy().add(p, task);
+ }
}
}
/**
- * Set the selected ranges and the mask for the actual dimensions in the
- * context
+ * Instance.
*
- * @param x1 x-value of the first dimension
- * @param x2 x-value of the second dimension
- * @param y1 y-value of the first dimension
- * @param y2 y-value of the second dimension
+ * @author Heidi Kolb
+ *
+ * @apiviz.has SelectionResult oneway - - updates
+ * @apiviz.has RangeSelection oneway - - updates
*/
- private void updateSelectionRectKoordinates(double x1, double x2, double y1, double y2, DoubleDoublePair[] ranges) {
- BitSet actDim = proj.getVisibleDimensions2D();
- double[] v1 = new double[dim];
- double[] v2 = new double[dim];
- v1[0] = x1;
- v1[1] = y1;
- v2[0] = x2;
- v2[1] = y2;
-
- double[] nv1 = proj.fastProjectRenderToDataSpace(v1);
- double[] nv2 = proj.fastProjectRenderToDataSpace(v2);
-
- for(int d = actDim.nextSetBit(0); d >= 0; d = actDim.nextSetBit(d + 1)) {
- ranges[d] = new DoubleDoublePair(Math.min(nv1[d], nv2[d]), Math.max(nv1[d], nv2[d]));
- }
- }
+ public class Instance extends AbstractScatterplotVisualization implements DragableArea.DragListener {
+ /**
+ * Generic tag to indicate the type of element. Used in IDs, CSS-Classes
+ * etc.
+ */
+ private static final String CSS_RANGEMARKER = "selectionRangeMarker";
- @Override
- public boolean startDrag(SVGPoint startPoint, Event evt) {
- return true;
- }
+ /**
+ * Dimension.
+ */
+ private int dim;
- @Override
- public boolean duringDrag(SVGPoint startPoint, SVGPoint dragPoint, Event evt, boolean inside) {
- deleteChildren(rtag);
- double x = Math.min(startPoint.getX(), dragPoint.getX());
- double y = Math.min(startPoint.getY(), dragPoint.getY());
- double width = Math.abs(startPoint.getX() - dragPoint.getX());
- double height = Math.abs(startPoint.getY() - dragPoint.getY());
- rtag.appendChild(svgp.svgRect(x, y, width, height));
- return true;
- }
+ /**
+ * Element for selection rectangle.
+ */
+ private Element rtag;
- @Override
- public boolean endDrag(SVGPoint startPoint, SVGPoint dragPoint, Event evt, boolean inside) {
- deleteChildren(rtag);
- if(startPoint.getX() != dragPoint.getX() || startPoint.getY() != dragPoint.getY()) {
- updateSelection(proj, startPoint, dragPoint);
- }
- return true;
- }
+ /**
+ * Element for the rectangle to add listeners.
+ */
+ private Element etag;
- /**
- * Update the selection in the context.
- *
- * @param proj The projection
- * @param p1 First Point of the selected rectangle
- * @param p2 Second Point of the selected rectangle
- */
- private void updateSelection(Projection proj, SVGPoint p1, SVGPoint p2) {
- if(p1 == null || p2 == null) {
- logger.warning("no rect selected: p1: " + p1 + " p2: " + p2);
- return;
+ /**
+ * Constructor.
+ *
+ * @param task Task
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
+ this.dim = RelationUtil.dimensionality(rel);
+ incrementalRedraw();
}
- DBIDSelection selContext = context.getSelection();
- ModifiableDBIDs selection;
- if(selContext != null) {
- selection = DBIDUtil.newHashSet(selContext.getSelectedIds());
- }
- else {
- selection = DBIDUtil.newHashSet();
+ @Override
+ protected void redraw() {
+ addCSSClasses(svgp);
+
+ // rtag: tag for the selected rect
+ rtag = svgp.svgElement(SVGConstants.SVG_G_TAG);
+ SVGUtil.addCSSClass(rtag, CSS_RANGEMARKER);
+ layer.appendChild(rtag);
+
+ // etag: sensitive area
+ DragableArea drag = new DragableArea(svgp, -0.6 * StyleLibrary.SCALE, -0.7 * StyleLibrary.SCALE, 1.3 * StyleLibrary.SCALE, 1.4 * StyleLibrary.SCALE, this);
+ etag = drag.getElement();
+ layer.appendChild(etag);
}
- DoubleDoublePair[] ranges;
-
- double x1 = Math.min(p1.getX(), p2.getX());
- double x2 = Math.max(p1.getX(), p2.getX());
- double y1 = Math.max(p1.getY(), p2.getY());
- double y2 = Math.min(p1.getY(), p2.getY());
- if(selContext instanceof RangeSelection) {
- ranges = ((RangeSelection) selContext).getRanges();
- }
- else {
- ranges = new DoubleDoublePair[dim];
- }
- updateSelectionRectKoordinates(x1, x2, y1, y2, ranges);
-
- selection.clear();
- candidates: for(DBIDIter iditer = rel.iterDBIDs(); iditer.valid(); iditer.advance()) {
- NumberVector<?, ?> dbTupel = rel.get(iditer);
- for(int i = 0; i < dim; i++) {
- if(ranges != null && ranges[i] != null) {
- if(dbTupel.doubleValue(i + 1) < ranges[i].first || dbTupel.doubleValue(i + 1) > ranges[i].second) {
- continue candidates;
- }
- }
+ /**
+ * Delete the children of the element.
+ *
+ * @param container SVG-Element
+ */
+ private void deleteChildren(Element container) {
+ while (container.hasChildNodes()) {
+ container.removeChild(container.getLastChild());
}
- selection.add(iditer);
}
- context.setSelection(new RangeSelection(selection, ranges));
- }
- /**
- * Adds the required CSS-Classes
- *
- * @param svgp SVG-Plot
- */
- protected void addCSSClasses(SVGPlot svgp) {
- // Class for the range marking
- if(!svgp.getCSSClassManager().contains(CSS_RANGEMARKER)) {
- final CSSClass rcls = new CSSClass(this, CSS_RANGEMARKER);
- final StyleLibrary style = context.getStyleLibrary();
- rcls.setStatement(SVGConstants.CSS_FILL_PROPERTY, style.getColor(StyleLibrary.SELECTION_ACTIVE));
- rcls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, style.getOpacity(StyleLibrary.SELECTION_ACTIVE));
- svgp.addCSSClassOrLogError(rcls);
- }
- }
-
- /**
- * Factory for tool visualizations for selecting ranges and the inclosed
- * objects
- *
- * @author Heidi Kolb
- *
- * @apiviz.stereotype factory
- * @apiviz.uses SelectionToolCubeVisualization oneway - - «create»
- */
- public static class Factory extends AbstractVisFactory {
/**
- * Constructor, adhering to
- * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
+ * Set the selected ranges and the mask for the actual dimensions in the
+ * context.
+ *
+ * @param x1 x-value of the first dimension
+ * @param x2 x-value of the second dimension
+ * @param y1 y-value of the first dimension
+ * @param y2 y-value of the second dimension
+ * @param ranges Ranges to update
*/
- public Factory() {
- super();
+ private void updateSelectionRectKoordinates(double x1, double x2, double y1, double y2, DoubleDoublePair[] ranges) {
+ BitSet actDim = proj.getVisibleDimensions2D();
+ double[] v1 = new double[dim];
+ double[] v2 = new double[dim];
+ v1[0] = x1;
+ v1[1] = y1;
+ v2[0] = x2;
+ v2[1] = y2;
+
+ double[] nv1 = proj.fastProjectRenderToDataSpace(v1);
+ double[] nv2 = proj.fastProjectRenderToDataSpace(v2);
+
+ for (int d = actDim.nextSetBit(0); d >= 0; d = actDim.nextSetBit(d + 1)) {
+ ranges[d] = new DoubleDoublePair(Math.min(nv1[d], nv2[d]), Math.max(nv1[d], nv2[d]));
+ }
+ }
+
+ @Override
+ public boolean startDrag(SVGPoint startPoint, Event evt) {
+ return true;
}
@Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new SelectionToolCubeVisualization(task);
+ public boolean duringDrag(SVGPoint startPoint, SVGPoint dragPoint, Event evt, boolean inside) {
+ deleteChildren(rtag);
+ double x = Math.min(startPoint.getX(), dragPoint.getX());
+ double y = Math.min(startPoint.getY(), dragPoint.getY());
+ double width = Math.abs(startPoint.getX() - dragPoint.getX());
+ double height = Math.abs(startPoint.getY() - dragPoint.getY());
+ rtag.appendChild(svgp.svgRect(x, y, width, height));
+ return true;
}
@Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<SelectionResult> selectionResults = ResultUtil.filterResults(result, SelectionResult.class);
- for(SelectionResult selres : selectionResults) {
- Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, selres, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_INTERACTIVE);
- task.put(VisualizationTask.META_TOOL, true);
- task.put(VisualizationTask.META_NOTHUMB, true);
- task.put(VisualizationTask.META_NOEXPORT, true);
- task.put(VisualizationTask.META_VISIBLE_DEFAULT, false);
- baseResult.getHierarchy().add(selres, task);
- baseResult.getHierarchy().add(p, task);
+ public boolean endDrag(SVGPoint startPoint, SVGPoint dragPoint, Event evt, boolean inside) {
+ deleteChildren(rtag);
+ if (startPoint.getX() != dragPoint.getX() || startPoint.getY() != dragPoint.getY()) {
+ updateSelection(proj, startPoint, dragPoint);
+ }
+ return true;
+ }
+
+ /**
+ * Update the selection in the context.
+ *
+ * @param proj The projection
+ * @param p1 First Point of the selected rectangle
+ * @param p2 Second Point of the selected rectangle
+ */
+ private void updateSelection(Projection proj, SVGPoint p1, SVGPoint p2) {
+ if (p1 == null || p2 == null) {
+ LOG.warning("no rect selected: p1: " + p1 + " p2: " + p2);
+ return;
+ }
+
+ DBIDSelection selContext = context.getSelection();
+ ModifiableDBIDs selection;
+ if (selContext != null) {
+ selection = DBIDUtil.newHashSet(selContext.getSelectedIds());
+ } else {
+ selection = DBIDUtil.newHashSet();
+ }
+ DoubleDoublePair[] ranges;
+
+ double x1 = Math.min(p1.getX(), p2.getX());
+ double x2 = Math.max(p1.getX(), p2.getX());
+ double y1 = Math.max(p1.getY(), p2.getY());
+ double y2 = Math.min(p1.getY(), p2.getY());
+
+ if (selContext instanceof RangeSelection) {
+ ranges = ((RangeSelection) selContext).getRanges();
+ } else {
+ ranges = new DoubleDoublePair[dim];
+ }
+ updateSelectionRectKoordinates(x1, x2, y1, y2, ranges);
+
+ selection.clear();
+ candidates: for (DBIDIter iditer = rel.iterDBIDs(); iditer.valid(); iditer.advance()) {
+ NumberVector<?> dbTupel = rel.get(iditer);
+ for (int i = 0; i < dim; i++) {
+ if (ranges != null && ranges[i] != null) {
+ if (dbTupel.doubleValue(i) < ranges[i].first || dbTupel.doubleValue(i) > ranges[i].second) {
+ continue candidates;
+ }
+ }
}
+ selection.add(iditer);
+ }
+ context.setSelection(new RangeSelection(selection, ranges));
+ }
+
+ /**
+ * Adds the required CSS-Classes.
+ *
+ * @param svgp SVG-Plot
+ */
+ protected void addCSSClasses(SVGPlot svgp) {
+ // Class for the range marking
+ if (!svgp.getCSSClassManager().contains(CSS_RANGEMARKER)) {
+ final CSSClass rcls = new CSSClass(this, CSS_RANGEMARKER);
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ rcls.setStatement(SVGConstants.CSS_FILL_PROPERTY, style.getColor(StyleLibrary.SELECTION_ACTIVE));
+ rcls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, style.getOpacity(StyleLibrary.SELECTION_ACTIVE));
+ svgp.addCSSClassOrLogError(rcls);
}
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionToolDotVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionToolDotVisualization.java
index b52f37c9..6fa8200c 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionToolDotVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionToolDotVisualization.java
@@ -56,21 +56,16 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.AbstractScatter
*
* @author Heidi Kolb
*
- * @apiviz.has SelectionResult oneway - - updates
- * @apiviz.has DBIDSelection oneway - - updates
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance - - «create»
*/
-public class SelectionToolDotVisualization extends AbstractScatterplotVisualization implements DragableArea.DragListener {
+public class SelectionToolDotVisualization extends AbstractVisFactory {
/**
* A short name characterizing this Visualizer.
*/
private static final String NAME = "Object Selection";
/**
- * CSS class of the selection rectangle while selecting.
- */
- private static final String CSS_RANGEMARKER = "selectionRangeMarker";
-
- /**
* Input modes
*
* @apiviz.exclude
@@ -80,194 +75,199 @@ public class SelectionToolDotVisualization extends AbstractScatterplotVisualizat
}
/**
- * Element for selection rectangle
- */
- Element rtag;
-
- /**
- * Element for the rectangle to add listeners
+ * Constructor, adhering to
+ * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
*/
- Element etag;
-
- /**
- * Constructor.
- *
- * @param task Task
- */
- public SelectionToolDotVisualization(VisualizationTask task) {
- super(task);
- incrementalRedraw();
+ public SelectionToolDotVisualization() {
+ super();
}
@Override
- protected void redraw() {
- addCSSClasses(svgp);
-
- //
- rtag = svgp.svgElement(SVGConstants.SVG_G_TAG);
- SVGUtil.addCSSClass(rtag, CSS_RANGEMARKER);
- layer.appendChild(rtag);
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
+ }
- // etag: sensitive area
- DragableArea drag = new DragableArea(svgp, -0.6 * StyleLibrary.SCALE, -0.7 * StyleLibrary.SCALE, 1.3 * StyleLibrary.SCALE, 1.4 * StyleLibrary.SCALE, this);
- etag = drag.getElement();
- layer.appendChild(etag);
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<SelectionResult> selectionResults = ResultUtil.filterResults(result, SelectionResult.class);
+ for(SelectionResult selres : selectionResults) {
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, selres, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_INTERACTIVE;
+ task.tool = true;
+ task.thumbnail = false;
+ task.noexport = true;
+ task.initDefaultVisibility(false);
+ baseResult.getHierarchy().add(selres, task);
+ baseResult.getHierarchy().add(p, task);
+ }
+ }
}
/**
- * Delete the children of the element
+ * Instance
*
- * @param container SVG-Element
+ * @author Heidi Kolb
+ *
+ * @apiviz.has SelectionResult oneway - - updates
+ * @apiviz.has DBIDSelection oneway - - updates
*/
- private void deleteChildren(Element container) {
- while(container.hasChildNodes()) {
- container.removeChild(container.getLastChild());
+ public class Instance extends AbstractScatterplotVisualization implements DragableArea.DragListener {
+ /**
+ * CSS class of the selection rectangle while selecting.
+ */
+ private static final String CSS_RANGEMARKER = "selectionRangeMarker";
+
+ /**
+ * Element for selection rectangle
+ */
+ Element rtag;
+
+ /**
+ * Element for the rectangle to add listeners
+ */
+ Element etag;
+
+ /**
+ * Constructor.
+ *
+ * @param task Task
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
+ incrementalRedraw();
}
- }
- @Override
- public boolean startDrag(SVGPoint startPoint, Event evt) {
- return true;
- }
+ @Override
+ protected void redraw() {
+ addCSSClasses(svgp);
- @Override
- public boolean duringDrag(SVGPoint startPoint, SVGPoint dragPoint, Event evt, boolean inside) {
- deleteChildren(rtag);
- double x = Math.min(startPoint.getX(), dragPoint.getX());
- double y = Math.min(startPoint.getY(), dragPoint.getY());
- double width = Math.abs(startPoint.getX() - dragPoint.getX());
- double height = Math.abs(startPoint.getY() - dragPoint.getY());
- rtag.appendChild(svgp.svgRect(x, y, width, height));
- return true;
- }
+ //
+ rtag = svgp.svgElement(SVGConstants.SVG_G_TAG);
+ SVGUtil.addCSSClass(rtag, CSS_RANGEMARKER);
+ layer.appendChild(rtag);
- @Override
- public boolean endDrag(SVGPoint startPoint, SVGPoint dragPoint, Event evt, boolean inside) {
- Mode mode = getInputMode(evt);
- deleteChildren(rtag);
- if(startPoint.getX() != dragPoint.getX() || startPoint.getY() != dragPoint.getY()) {
- updateSelection(mode, proj, startPoint, dragPoint);
+ // etag: sensitive area
+ DragableArea drag = new DragableArea(svgp, -0.6 * StyleLibrary.SCALE, -0.7 * StyleLibrary.SCALE, 1.3 * StyleLibrary.SCALE, 1.4 * StyleLibrary.SCALE, this);
+ etag = drag.getElement();
+ layer.appendChild(etag);
}
- return true;
- }
- /**
- * Get the current input mode, on each mouse event.
- *
- * @param evt Mouse event.
- * @return current input mode
- */
- private Mode getInputMode(Event evt) {
- if(evt instanceof DOMMouseEvent) {
- DOMMouseEvent domme = (DOMMouseEvent) evt;
- // TODO: visual indication of mode possible?
- if(domme.getShiftKey()) {
- return Mode.ADD;
- }
- else if(domme.getCtrlKey()) {
- return Mode.INVERT;
- }
- else {
- return Mode.REPLACE;
+ /**
+ * Delete the children of the element
+ *
+ * @param container SVG-Element
+ */
+ private void deleteChildren(Element container) {
+ while(container.hasChildNodes()) {
+ container.removeChild(container.getLastChild());
}
}
- // Default mode is replace.
- return Mode.REPLACE;
- }
- /**
- * Updates the selection in the context.<br>
- *
- * @param mode Input mode
- * @param proj
- * @param p1 first point of the selected rectangle
- * @param p2 second point of the selected rectangle
- */
- private void updateSelection(Mode mode, Projection2D proj, SVGPoint p1, SVGPoint p2) {
- DBIDSelection selContext = context.getSelection();
- // Note: we rely on SET semantics below!
- HashSetModifiableDBIDs selection;
- if(selContext == null || mode == Mode.REPLACE) {
- selection = DBIDUtil.newHashSet();
+ @Override
+ public boolean startDrag(SVGPoint startPoint, Event evt) {
+ return true;
}
- else {
- selection = DBIDUtil.newHashSet(selContext.getSelectedIds());
+
+ @Override
+ public boolean duringDrag(SVGPoint startPoint, SVGPoint dragPoint, Event evt, boolean inside) {
+ deleteChildren(rtag);
+ double x = Math.min(startPoint.getX(), dragPoint.getX());
+ double y = Math.min(startPoint.getY(), dragPoint.getY());
+ double width = Math.abs(startPoint.getX() - dragPoint.getX());
+ double height = Math.abs(startPoint.getY() - dragPoint.getY());
+ rtag.appendChild(svgp.svgRect(x, y, width, height));
+ return true;
}
- for(DBIDIter iditer = rel.iterDBIDs(); iditer.valid(); iditer.advance()) {
- double[] vec = proj.fastProjectDataToRenderSpace(rel.get(iditer));
- if(vec[0] >= Math.min(p1.getX(), p2.getX()) && vec[0] <= Math.max(p1.getX(), p2.getX()) && vec[1] >= Math.min(p1.getY(), p2.getY()) && vec[1] <= Math.max(p1.getY(), p2.getY())) {
- if(mode == Mode.INVERT) {
- if(!selection.contains(iditer)) {
- selection.add(iditer);
- }
- else {
- selection.remove(iditer);
- }
+
+ @Override
+ public boolean endDrag(SVGPoint startPoint, SVGPoint dragPoint, Event evt, boolean inside) {
+ Mode mode = getInputMode(evt);
+ deleteChildren(rtag);
+ if(startPoint.getX() != dragPoint.getX() || startPoint.getY() != dragPoint.getY()) {
+ updateSelection(mode, proj, startPoint, dragPoint);
+ }
+ return true;
+ }
+
+ /**
+ * Get the current input mode, on each mouse event.
+ *
+ * @param evt Mouse event.
+ * @return current input mode
+ */
+ private Mode getInputMode(Event evt) {
+ if(evt instanceof DOMMouseEvent) {
+ DOMMouseEvent domme = (DOMMouseEvent) evt;
+ // TODO: visual indication of mode possible?
+ if(domme.getShiftKey()) {
+ return Mode.ADD;
+ }
+ else if(domme.getCtrlKey()) {
+ return Mode.INVERT;
}
else {
- // In REPLACE and ADD, add objects.
- // The difference was done before by not re-using the selection.
- // Since we are using a set, we can just add in any case.
- selection.add(iditer);
+ return Mode.REPLACE;
}
}
+ // Default mode is replace.
+ return Mode.REPLACE;
}
- context.setSelection(new DBIDSelection(selection));
- }
- /**
- * Adds the required CSS-Classes
- *
- * @param svgp SVG-Plot
- */
- protected void addCSSClasses(SVGPlot svgp) {
- // Class for the range marking
- if(!svgp.getCSSClassManager().contains(CSS_RANGEMARKER)) {
- final CSSClass rcls = new CSSClass(this, CSS_RANGEMARKER);
- final StyleLibrary style = context.getStyleLibrary();
- rcls.setStatement(SVGConstants.CSS_FILL_PROPERTY, style.getColor(StyleLibrary.SELECTION_ACTIVE));
- rcls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, style.getOpacity(StyleLibrary.SELECTION_ACTIVE));
- svgp.addCSSClassOrLogError(rcls);
- }
- }
-
- /**
- * Factory for tool visualizations for selecting objects
- *
- * @author Heidi Kolb
- *
- * @apiviz.stereotype factory
- * @apiviz.uses SelectionToolDotVisualization - - «create»
- */
- public static class Factory extends AbstractVisFactory {
/**
- * Constructor, adhering to
- * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
+ * Updates the selection in the context.<br>
+ *
+ * @param mode Input mode
+ * @param proj
+ * @param p1 first point of the selected rectangle
+ * @param p2 second point of the selected rectangle
*/
- public Factory() {
- super();
- }
-
- @Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new SelectionToolDotVisualization(task);
+ private void updateSelection(Mode mode, Projection2D proj, SVGPoint p1, SVGPoint p2) {
+ DBIDSelection selContext = context.getSelection();
+ // Note: we rely on SET semantics below!
+ HashSetModifiableDBIDs selection;
+ if(selContext == null || mode == Mode.REPLACE) {
+ selection = DBIDUtil.newHashSet();
+ }
+ else {
+ selection = DBIDUtil.newHashSet(selContext.getSelectedIds());
+ }
+ for(DBIDIter iditer = rel.iterDBIDs(); iditer.valid(); iditer.advance()) {
+ double[] vec = proj.fastProjectDataToRenderSpace(rel.get(iditer));
+ if(vec[0] >= Math.min(p1.getX(), p2.getX()) && vec[0] <= Math.max(p1.getX(), p2.getX()) && vec[1] >= Math.min(p1.getY(), p2.getY()) && vec[1] <= Math.max(p1.getY(), p2.getY())) {
+ if(mode == Mode.INVERT) {
+ if(!selection.contains(iditer)) {
+ selection.add(iditer);
+ }
+ else {
+ selection.remove(iditer);
+ }
+ }
+ else {
+ // In REPLACE and ADD, add objects.
+ // The difference was done before by not re-using the selection.
+ // Since we are using a set, we can just add in any case.
+ selection.add(iditer);
+ }
+ }
+ }
+ context.setSelection(new DBIDSelection(selection));
}
- @Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<SelectionResult> selectionResults = ResultUtil.filterResults(result, SelectionResult.class);
- for(SelectionResult selres : selectionResults) {
- Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, selres, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_INTERACTIVE);
- task.put(VisualizationTask.META_TOOL, true);
- task.put(VisualizationTask.META_NOTHUMB, true);
- task.put(VisualizationTask.META_NOEXPORT, true);
- task.put(VisualizationTask.META_VISIBLE_DEFAULT, false);
- baseResult.getHierarchy().add(selres, task);
- baseResult.getHierarchy().add(p, task);
- }
+ /**
+ * Adds the required CSS-Classes
+ *
+ * @param svgp SVG-Plot
+ */
+ protected void addCSSClasses(SVGPlot svgp) {
+ // Class for the range marking
+ if(!svgp.getCSSClassManager().contains(CSS_RANGEMARKER)) {
+ final CSSClass rcls = new CSSClass(this, CSS_RANGEMARKER);
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ rcls.setStatement(SVGConstants.CSS_FILL_PROPERTY, style.getColor(StyleLibrary.SELECTION_ACTIVE));
+ rcls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, style.getOpacity(StyleLibrary.SELECTION_ACTIVE));
+ svgp.addCSSClassOrLogError(rcls);
}
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/thumbs/ThumbnailVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/thumbs/ThumbnailVisualization.java
index 9ea016a5..dd502146 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/thumbs/ThumbnailVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/thumbs/ThumbnailVisualization.java
@@ -91,7 +91,8 @@ public class ThumbnailVisualization extends AbstractVisualization implements Thu
protected int tresolution;
/**
- * The event mask. See {@link #ON_DATA}, {@link #ON_SELECTION}, {@link #ON_STYLE}, {@link #NO_PROJECTION}
+ * The event mask. See {@link #ON_DATA}, {@link #ON_SELECTION},
+ * {@link #ON_STYLE}, {@link #NO_PROJECTION}
*/
private int mask;
@@ -110,14 +111,13 @@ public class ThumbnailVisualization extends AbstractVisualization implements Thu
public ThumbnailVisualization(VisFactory visFactory, VisualizationTask task, int mask) {
super(task);
this.visFactory = visFactory;
- Integer tres = task.getGenerics(VisualizationTask.THUMBNAIL_RESOLUTION, Integer.class);
- this.tresolution = tres;
+ this.tresolution = task.thumbsize;
this.layer = task.getPlot().svgElement(SVGConstants.SVG_G_TAG);
this.thumbid = -1;
this.thumb = null;
this.mask = mask;
// Listen for database events only when needed.
- if((mask & ON_DATA) == ON_DATA) {
+ if ((mask & ON_DATA) == ON_DATA) {
context.addDataStoreListener(this);
}
// Listen for result changes, including the one we monitor
@@ -126,7 +126,7 @@ public class ThumbnailVisualization extends AbstractVisualization implements Thu
@Override
public void destroy() {
- if(pendingThumbnail != null) {
+ if (pendingThumbnail != null) {
ThumbnailThread.UNQUEUE(pendingThumbnail);
}
// TODO: remove image from registry?
@@ -136,7 +136,7 @@ public class ThumbnailVisualization extends AbstractVisualization implements Thu
@Override
public Element getLayer() {
- if(thumbid < 0) {
+ if (thumbid < 0) {
synchronizedRedraw();
}
return layer;
@@ -150,15 +150,14 @@ public class ThumbnailVisualization extends AbstractVisualization implements Thu
@Override
protected void incrementalRedraw() {
final Element oldcontainer;
- if(layer.hasChildNodes()) {
+ if (layer.hasChildNodes()) {
oldcontainer = layer;
layer = (Element) layer.cloneNode(false);
- }
- else {
+ } else {
oldcontainer = null;
}
redraw();
- if(oldcontainer != null && oldcontainer.getParentNode() != null) {
+ if (oldcontainer != null && oldcontainer.getParentNode() != null) {
oldcontainer.getParentNode().replaceChild(layer, oldcontainer);
}
}
@@ -168,14 +167,13 @@ public class ThumbnailVisualization extends AbstractVisualization implements Thu
*/
@Override
protected void redraw() {
- if(thumbid < 0) {
+ if (thumbid < 0) {
// LoggingUtil.warning("Generating new thumbnail " + this);
layer.appendChild(SVGUtil.svgWaitIcon(task.getPlot().getDocument(), 0, 0, task.getWidth(), task.getHeight()));
- if(pendingThumbnail == null) {
+ if (pendingThumbnail == null) {
pendingThumbnail = ThumbnailThread.QUEUE(this);
}
- }
- else {
+ } else {
// LoggingUtil.warning("Injecting Thumbnail " + this);
Element i = task.getPlot().svgElement(SVGConstants.SVG_IMAGE_TAG);
SVGUtil.setAtt(i, SVGConstants.SVG_X_ATTRIBUTE, 0);
@@ -196,7 +194,7 @@ public class ThumbnailVisualization extends AbstractVisualization implements Thu
// Work on a clone
VisualizationTask clone = task.clone(plot, context);
- clone.put(VisualizationTask.THUMBNAIL, false);
+ clone.thumbnail = false;
Visualization vis = visFactory.makeVisualization(clone);
plot.getRoot().appendChild(vis.getLayer());
@@ -208,13 +206,11 @@ public class ThumbnailVisualization extends AbstractVisualization implements Thu
// The visualization will not be used anymore.
vis.destroy();
synchronizedRedraw();
- }
- catch(Exception e) {
+ } catch (Exception e) {
final Logging logger = Logging.getLogger(task.getFactory().getClass());
- if(logger != null && logger.isDebugging()) {
+ if (logger != null && logger.isDebugging()) {
logger.exception("Thumbnail failed.", e);
- }
- else {
+ } else {
LoggingUtil.warning("Thumbnail for " + task.getFactory().getClass().getName() + " failed - enable debugging to see details.");
}
// TODO: hide the failed image?
@@ -231,11 +227,11 @@ public class ThumbnailVisualization extends AbstractVisualization implements Thu
@Override
public void resultChanged(Result current) {
- if((mask & ON_SELECTION) == ON_SELECTION && current instanceof SelectionResult) {
+ if ((mask & ON_SELECTION) == ON_SELECTION && current instanceof SelectionResult) {
refreshThumbnail();
return;
}
- if((mask & ON_STYLE) == ON_STYLE && current instanceof StyleResult) {
+ if ((mask & ON_STYLE) == ON_STYLE && current instanceof StyleResult) {
refreshThumbnail();
return;
}
@@ -245,4 +241,4 @@ public class ThumbnailVisualization extends AbstractVisualization implements Thu
}
super.resultChanged(current);
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/ClusterEvaluationVisFactory.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/ClusterEvaluationVisualization.java
index 5b58e842..4b57a503 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/ClusterEvaluationVisFactory.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/ClusterEvaluationVisualization.java
@@ -48,7 +48,7 @@ import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGScoreBar;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
-import de.lmu.ifi.dbs.elki.visualization.visualizers.StaticVisualization;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.StaticVisualizationInstance;
import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
/**
@@ -58,10 +58,10 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
* @author Sascha Goldhofer
*
* @apiviz.stereotype factory
- * @apiviz.uses StaticVisualization oneway - - «create»
+ * @apiviz.uses StaticVisualizationInstance oneway - - «create»
* @apiviz.has de.lmu.ifi.dbs.elki.evaluation.clustering.EvaluateClustering.ScoreResult oneway - - visualizes
*/
-public class ClusterEvaluationVisFactory extends AbstractVisFactory {
+public class ClusterEvaluationVisualization extends AbstractVisFactory {
/**
* Name for this visualizer.
*/
@@ -80,7 +80,7 @@ public class ClusterEvaluationVisFactory extends AbstractVisFactory {
/**
* Constructor.
*/
- public ClusterEvaluationVisFactory() {
+ public ClusterEvaluationVisualization() {
super();
}
@@ -91,7 +91,7 @@ public class ClusterEvaluationVisFactory extends AbstractVisFactory {
final VisualizationTask task = new VisualizationTask(NAME, sr, null, this);
task.width = .5;
task.height = 2.0;
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_STATIC);
+ task.level = VisualizationTask.LEVEL_STATIC;
baseResult.getHierarchy().add(sr, task);
}
}
@@ -181,11 +181,12 @@ public class ClusterEvaluationVisFactory extends AbstractVisFactory {
double cols = 10; // Math.max(10, (int) (i * task.getHeight() /
// task.getWidth()));
double rows = ypos;
- final double margin = task.getContext().getStyleLibrary().getSize(StyleLibrary.MARGIN);
+ final StyleLibrary style = task.getContext().getStyleResult().getStyleLibrary();
+ final double margin = style.getSize(StyleLibrary.MARGIN);
final String transform = SVGUtil.makeMarginTransform(task.getWidth(), task.getHeight(), cols, rows, margin / StyleLibrary.SCALE);
SVGUtil.setAtt(parent, SVGConstants.SVG_TRANSFORM_ATTRIBUTE, transform);
- return new StaticVisualization(task, parent);
+ return new StaticVisualizationInstance(task, parent);
}
@Override
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/HistogramVisFactory.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/HistogramVisualization.java
index 117c3c2c..d97b5117 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/HistogramVisFactory.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/HistogramVisualization.java
@@ -47,7 +47,7 @@ import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGSimpleLinearAxis;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
-import de.lmu.ifi.dbs.elki.visualization.visualizers.StaticVisualization;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.StaticVisualizationInstance;
import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
/**
@@ -56,10 +56,10 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
* @author Erich Schubert
*
* @apiviz.stereotype factory
- * @apiviz.uses StaticVisualization oneway - - «create»
+ * @apiviz.uses StaticVisualizationInstance oneway - - «create»
* @apiviz.has HistogramResult oneway - - visualizes
*/
-public class HistogramVisFactory extends AbstractVisFactory {
+public class HistogramVisualization extends AbstractVisFactory {
/**
* Histogram visualizer name
*/
@@ -74,7 +74,7 @@ public class HistogramVisFactory extends AbstractVisFactory {
* Constructor, adhering to
* {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
*/
- public HistogramVisFactory() {
+ public HistogramVisualization() {
super();
}
@@ -82,31 +82,30 @@ public class HistogramVisFactory extends AbstractVisFactory {
public Visualization makeVisualization(VisualizationTask task) {
VisualizerContext context = task.getContext();
SVGPlot svgp = task.getPlot();
- HistogramResult<? extends NumberVector<?, ?>> curve = task.getResult();
-
- double scale = StyleLibrary.SCALE;
- final double sizex = scale;
- final double sizey = scale * task.getHeight() / task.getWidth();
- final double margin = context.getStyleLibrary().getSize(StyleLibrary.MARGIN);
+ HistogramResult<? extends NumberVector<?>> curve = task.getResult();
+
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ final double sizex = StyleLibrary.SCALE;
+ final double sizey = StyleLibrary.SCALE * task.getHeight() / task.getWidth();
+ final double margin = style.getSize(StyleLibrary.MARGIN);
Element layer = SVGUtil.svgElement(svgp.getDocument(), SVGConstants.SVG_G_TAG);
final String transform = SVGUtil.makeMarginTransform(task.getWidth(), task.getHeight(), sizex, sizey, margin);
SVGUtil.setAtt(layer, SVGConstants.SVG_TRANSFORM_ATTRIBUTE, transform);
-
+
// find maximum, determine step size
- Integer dim = null;
+ int dim = -1;
DoubleMinMax xminmax = new DoubleMinMax();
DoubleMinMax yminmax = new DoubleMinMax();
- for(NumberVector<?, ?> vec : curve) {
- xminmax.put(vec.doubleValue(1));
- if(dim == null) {
+ for (NumberVector<?> vec : curve) {
+ xminmax.put(vec.doubleValue(0));
+ if (dim < 0) {
dim = vec.getDimensionality();
- }
- else {
+ } else {
// TODO: test and throw always
assert (dim == vec.getDimensionality());
}
- for(int i = 1; i < dim; i++) {
- yminmax.put(vec.doubleValue(i + 1));
+ for (int i = 0; i < dim; i++) {
+ yminmax.put(vec.doubleValue(i));
}
}
// Minimum should always start at 0 for histograms
@@ -118,42 +117,41 @@ public class HistogramVisFactory extends AbstractVisFactory {
double range = xminmax.getMax() - xminmax.getMin();
double binwidth = range / (size - 1);
- LinearScale xscale = new LinearScale(xminmax.getMin() - binwidth / 2, xminmax.getMax() + binwidth / 2);
+ LinearScale xscale = new LinearScale(xminmax.getMin() - binwidth * .5, xminmax.getMax() + binwidth * .5);
LinearScale yscale = new LinearScale(yminmax.getMin(), yminmax.getMax());
SVGPath[] path = new SVGPath[dim];
- for(int i = 0; i < dim; i++) {
- path[i] = new SVGPath(sizex * xscale.getScaled(xminmax.getMin() - binwidth / 2), sizey);
+ for (int i = 0; i < dim; i++) {
+ path[i] = new SVGPath(sizex * xscale.getScaled(xminmax.getMin() - binwidth * .5), sizey);
}
// draw curves.
- for(NumberVector<?, ?> vec : curve) {
- for(int d = 0; d < dim; d++) {
- path[d].lineTo(sizex * (xscale.getScaled(vec.doubleValue(1) - binwidth / 2)), sizey * (1 - yscale.getScaled(vec.doubleValue(d + 2))));
- path[d].lineTo(sizex * (xscale.getScaled(vec.doubleValue(1) + binwidth / 2)), sizey * (1 - yscale.getScaled(vec.doubleValue(d + 2))));
+ for (NumberVector<?> vec : curve) {
+ for (int d = 0; d < dim; d++) {
+ path[d].lineTo(sizex * (xscale.getScaled(vec.doubleValue(0) - binwidth * .5)), sizey * (1 - yscale.getScaled(vec.doubleValue(d + 1))));
+ path[d].lineTo(sizex * (xscale.getScaled(vec.doubleValue(0) + binwidth * .5)), sizey * (1 - yscale.getScaled(vec.doubleValue(d + 1))));
}
}
// close all histograms
- for(int i = 0; i < dim; i++) {
- path[i].lineTo(sizex * xscale.getScaled(xminmax.getMax() + binwidth / 2), sizey);
+ for (int i = 0; i < dim; i++) {
+ path[i].lineTo(sizex * xscale.getScaled(xminmax.getMax() + binwidth * .5), sizey);
}
// add axes
try {
- SVGSimpleLinearAxis.drawAxis(svgp, layer, yscale, 0, sizey, 0, 0, SVGSimpleLinearAxis.LabelStyle.LEFTHAND, context.getStyleLibrary());
- SVGSimpleLinearAxis.drawAxis(svgp, layer, xscale, 0, sizey, sizex, sizey, SVGSimpleLinearAxis.LabelStyle.RIGHTHAND, context.getStyleLibrary());
- }
- catch(CSSNamingConflict e) {
+ SVGSimpleLinearAxis.drawAxis(svgp, layer, yscale, 0, sizey, 0, 0, SVGSimpleLinearAxis.LabelStyle.LEFTHAND, style);
+ SVGSimpleLinearAxis.drawAxis(svgp, layer, xscale, 0, sizey, sizex, sizey, SVGSimpleLinearAxis.LabelStyle.RIGHTHAND, style);
+ } catch (CSSNamingConflict e) {
LoggingUtil.exception(e);
}
// Setup line styles and insert lines.
- ColorLibrary cl = context.getStyleLibrary().getColorSet(StyleLibrary.PLOT);
- for(int d = 0; d < dim; d++) {
+ ColorLibrary cl = style.getColorSet(StyleLibrary.PLOT);
+ for (int d = 0; d < dim; d++) {
CSSClass csscls = new CSSClass(this, SERIESID + "_" + d);
csscls.setStatement(SVGConstants.SVG_FILL_ATTRIBUTE, SVGConstants.SVG_NONE_VALUE);
csscls.setStatement(SVGConstants.SVG_STROKE_ATTRIBUTE, cl.getColor(d));
- csscls.setStatement(SVGConstants.SVG_STROKE_WIDTH_ATTRIBUTE, context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT));
+ csscls.setStatement(SVGConstants.SVG_STROKE_WIDTH_ATTRIBUTE, style.getLineWidth(StyleLibrary.PLOT));
svgp.addCSSClassOrLogError(csscls);
Element line = path[d].makeElement(svgp);
@@ -161,17 +159,17 @@ public class HistogramVisFactory extends AbstractVisFactory {
layer.appendChild(line);
}
- return new StaticVisualization(task, layer);
+ return new StaticVisualizationInstance(task, layer);
}
@Override
public void processNewResult(HierarchicalResult baseResult, Result newResult) {
- List<HistogramResult<? extends NumberVector<?, ?>>> histograms = ResultUtil.filterResults(newResult, HistogramResult.class);
- for(HistogramResult<? extends NumberVector<?, ?>> histogram : histograms) {
+ List<HistogramResult<? extends NumberVector<?>>> histograms = ResultUtil.filterResults(newResult, HistogramResult.class);
+ for (HistogramResult<? extends NumberVector<?>> histogram : histograms) {
final VisualizationTask task = new VisualizationTask(NAME, histogram, null, this);
task.width = 2.0;
task.height = 1.0;
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_STATIC);
+ task.level = VisualizationTask.LEVEL_STATIC;
baseResult.getHierarchy().add(histogram, task);
}
}
@@ -181,4 +179,4 @@ public class HistogramVisFactory extends AbstractVisFactory {
// TODO: depending on the histogram complexity?
return false;
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/KeyVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/KeyVisualization.java
index 62a4fb5c..f21e4df6 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/KeyVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/KeyVisualization.java
@@ -52,147 +52,182 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
/**
* Visualizer, displaying the key for a clustering.
- *
+ *
* @author Erich Schubert
*
- * @apiviz.has Clustering oneway - - visualizes
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class KeyVisualization extends AbstractVisualization {
+public class KeyVisualization extends AbstractVisFactory {
/**
* Name for this visualizer.
*/
private static final String NAME = "Cluster Key";
- /**
- * Clustering to display
- */
- private Clustering<Model> clustering;
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result newResult) {
+ // Find clusterings we can visualize:
+ Collection<Clustering<?>> clusterings = ResultUtil.filterResults(newResult, Clustering.class);
+ for(Clustering<?> c : clusterings) {
+ final int numc = c.getAllClusters().size();
+ if(numc > 0) {
+ // FIXME: compute from labels?
+ final double maxwidth = 10.;
+ final VisualizationTask task = new VisualizationTask(NAME, c, null, this);
+ final int cols = getPreferredColumns(1.0, 1.0, numc, maxwidth);
+ final int rows = (int) Math.ceil(numc / (double) cols);
+ final double div = Math.max(2. + rows, cols * maxwidth);
+ task.width = cols * maxwidth / div;
+ task.height = (2. + rows) / div;
+ task.level = VisualizationTask.LEVEL_STATIC;
+ task.nodetail = true;
+ baseResult.getHierarchy().add(c, task);
+ }
+ }
+ }
/**
- * Constructor.
+ * Compute the preferred number of columns.
*
- * @param task Visualization task
+ * @param width Target width
+ * @param height Target height
+ * @param numc Number of clusters
+ * @param maxwidth Max width of entries
+ * @return Preferred number of columns
*/
- public KeyVisualization(VisualizationTask task) {
- super(task);
- this.clustering = task.getResult();
- context.addResultListener(this);
+ public static int getPreferredColumns(double width, double height, int numc, double maxwidth) {
+ // Maximum width (compared to height) of labels - guess.
+ // FIXME: do we really need to do this three-step computation?
+ // Number of rows we'd use in a squared layout:
+ final double rows = Math.ceil(Math.pow(numc * maxwidth, height / (width + height)));
+ // Given this number of rows (plus two for header), use this many columns:
+ return (int) Math.ceil(numc / (rows + 2));
}
@Override
- public void destroy() {
- context.removeResultListener(this);
- super.destroy();
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
}
@Override
- public void resultChanged(Result current) {
- super.resultChanged(current);
- if(current == context.getStyleResult()) {
- incrementalRedraw();
- }
+ public boolean allowThumbnails(VisualizationTask task) {
+ return false;
}
- @Override
- protected void redraw() {
- SVGPlot svgp = task.getPlot();
- final List<Cluster<Model>> allcs = clustering.getAllClusters();
-
- StyleLibrary style = context.getStyleLibrary();
- MarkerLibrary ml = style.markers();
- layer = svgp.svgElement(SVGConstants.SVG_G_TAG);
-
- // Add a label for the clustering.
- {
- Element label = svgp.svgText(0.1, 0.7, clustering.getLongName());
- label.setAttribute(SVGConstants.SVG_STYLE_ATTRIBUTE, "font-size: 0.4; fill: "+style.getTextColor(StyleLibrary.DEFAULT));
- layer.appendChild(label);
+ /**
+ * Instance
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.has Clustering oneway - - visualizes
+ */
+ public class Instance extends AbstractVisualization {
+ /**
+ * Clustering to display
+ */
+ private Clustering<Model> clustering;
+
+ /**
+ * Constructor.
+ *
+ * @param task Visualization task
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
+ this.clustering = task.getResult();
+ context.addResultListener(this);
}
- // TODO: multi-column layout!
- int i = 0;
- for(Cluster<Model> c : allcs) {
- ml.useMarker(svgp, layer, 0.3, i + 1.5, i, 0.3);
- Element label = svgp.svgText(0.7, i + 1.7, c.getNameAutomatic());
- label.setAttribute(SVGConstants.SVG_STYLE_ATTRIBUTE, "font-size: 0.6; fill: "+style.getTextColor(StyleLibrary.DEFAULT));
- layer.appendChild(label);
- i++;
+ @Override
+ public void destroy() {
+ context.removeResultListener(this);
+ super.destroy();
}
- // Add a button to set style policy
- {
- StylingPolicy sp = context.getStyleResult().getStylingPolicy();
- if(sp instanceof ClusterStylingPolicy && ((ClusterStylingPolicy) sp).getClustering() == clustering) {
- // Don't show the button when active. May confuse people more than the disappearing button
-
- // SVGButton button = new SVGButton(.1, i + 1.1, 3.8, .7, .2);
- // button.setTitle("Active style", "darkgray");
- // layer.appendChild(button.render(svgp));
- }
- else {
- SVGButton button = new SVGButton(.1, i + 1.1, 3.8, .7, .2);
- button.setTitle("Set style", "black");
- Element elem = button.render(svgp);
- // Attach listener
- EventTarget etr = (EventTarget) elem;
- etr.addEventListener(SVGConstants.SVG_CLICK_EVENT_TYPE, new EventListener() {
- @Override
- public void handleEvent(Event evt) {
- setStylePolicy();
- }
- }, false);
- layer.appendChild(elem);
+ @Override
+ public void resultChanged(Result current) {
+ super.resultChanged(current);
+ if(current == context.getStyleResult()) {
+ incrementalRedraw();
}
}
- int rows = i + 2;
- int cols = Math.max(6, (int) (rows * task.getHeight() / task.getWidth()));
- final double margin = style.getSize(StyleLibrary.MARGIN);
- final String transform = SVGUtil.makeMarginTransform(task.getWidth(), task.getHeight(), cols, rows, margin / StyleLibrary.SCALE);
- SVGUtil.setAtt(layer, SVGConstants.SVG_TRANSFORM_ATTRIBUTE, transform);
- }
+ @Override
+ protected void redraw() {
+ SVGPlot svgp = task.getPlot();
+ StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ MarkerLibrary ml = style.markers();
+
+ // Maximum width (compared to height) of labels - guess.
+ // FIXME: compute from labels?
+ final double maxwidth = 10.;
+
+ final List<Cluster<Model>> allcs = clustering.getAllClusters();
+ final int numc = allcs.size();
+ final int cols = getPreferredColumns(task.getWidth(), task.getHeight(), numc, maxwidth);
+ final int rows = 2 + (int) Math.ceil(numc / (double) cols);
+ // We use a coordinate system based on rows, so columns are at c*maxwidth
+
+ layer = svgp.svgElement(SVGConstants.SVG_G_TAG);
+ // Add a label for the clustering.
+ {
+ Element label = svgp.svgText(0.1, 0.7, clustering.getLongName());
+ label.setAttribute(SVGConstants.SVG_STYLE_ATTRIBUTE, "font-size: 0.4; fill: " + style.getTextColor(StyleLibrary.DEFAULT));
+ layer.appendChild(label);
+ }
- /**
- * Trigger a style change.
- */
- protected void setStylePolicy() {
- context.getStyleResult().setStylingPolicy(new ClusterStylingPolicy(clustering, context.getStyleLibrary()));
- context.getHierarchy().resultChanged(context.getStyleResult());
- }
+ int i = 0;
+ for(Cluster<Model> c : allcs) {
+ final int col = i / rows;
+ final int row = i % rows;
+ ml.useMarker(svgp, layer, 0.3 + maxwidth * col, row + 1.5, i, 0.3);
+ Element label = svgp.svgText(0.7 + maxwidth * col, row + 1.7, c.getNameAutomatic());
+ label.setAttribute(SVGConstants.SVG_STYLE_ATTRIBUTE, "font-size: 0.6; fill: " + style.getTextColor(StyleLibrary.DEFAULT));
+ layer.appendChild(label);
+ i++;
+ }
- /**
- * Visualization factory
- *
- * @author Erich Schubert
- *
- * @apiviz.stereotype factory
- * @apiviz.uses KeyVisualization oneway - - «create»
- */
- public static class Factory extends AbstractVisFactory {
- @Override
- public void processNewResult(HierarchicalResult baseResult, Result newResult) {
- // Find clusterings we can visualize:
- Collection<Clustering<?>> clusterings = ResultUtil.filterResults(newResult, Clustering.class);
- for (Clustering<?> c : clusterings) {
- if(c.getAllClusters().size() > 0) {
- final VisualizationTask task = new VisualizationTask(NAME, c, null, this);
- task.width = 1.0;
- task.height = 1.0;
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_STATIC);
- task.put(VisualizationTask.META_NODETAIL, true);
- baseResult.getHierarchy().add(c, task);
+ // Add a button to set style policy
+ {
+ StylingPolicy sp = context.getStyleResult().getStylingPolicy();
+ if(sp instanceof ClusterStylingPolicy && ((ClusterStylingPolicy) sp).getClustering() == clustering) {
+ // Don't show the button when active. May confuse people more than the
+ // disappearing button?
+
+ // SVGButton button = new SVGButton(.1, rows + 1.1, 3.8, .7, .2);
+ // button.setTitle("Active style", "darkgray");
+ // layer.appendChild(button.render(svgp));
+ }
+ else {
+ SVGButton button = new SVGButton(.1, rows + 1.1, 3.8, .7, .2);
+ button.setTitle("Set style", "black");
+ Element elem = button.render(svgp);
+ // Attach listener
+ EventTarget etr = (EventTarget) elem;
+ etr.addEventListener(SVGConstants.SVG_CLICK_EVENT_TYPE, new EventListener() {
+ @Override
+ public void handleEvent(Event evt) {
+ setStylePolicy();
+ }
+ }, false);
+ layer.appendChild(elem);
}
}
- }
- @Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new KeyVisualization(task);
+ // int rows = i + 2;
+ // int cols = Math.max(6, (int) (rows * task.getHeight() /
+ // task.getWidth()));
+ final double margin = style.getSize(StyleLibrary.MARGIN);
+ final String transform = SVGUtil.makeMarginTransform(task.getWidth(), task.getHeight(), cols * maxwidth, rows, margin / StyleLibrary.SCALE);
+ SVGUtil.setAtt(layer, SVGConstants.SVG_TRANSFORM_ATTRIBUTE, transform);
}
- @Override
- public boolean allowThumbnails(VisualizationTask task) {
- return false;
+ /**
+ * Trigger a style change.
+ */
+ protected void setStylePolicy() {
+ context.getStyleResult().setStylingPolicy(new ClusterStylingPolicy(clustering, context.getStyleResult().getStyleLibrary()));
+ context.getHierarchy().resultChanged(context.getStyleResult());
}
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/LabelVisFactory.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/LabelVisualization.java
index b6eb4549..d29a6467 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/LabelVisFactory.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/LabelVisualization.java
@@ -35,7 +35,7 @@ import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
-import de.lmu.ifi.dbs.elki.visualization.visualizers.StaticVisualization;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.StaticVisualizationInstance;
import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
/**
@@ -45,9 +45,9 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
* @author Erich Schubert
*
* @apiviz.stereotype factory
- * @apiviz.uses StaticVisualization oneway - - «create»
+ * @apiviz.uses StaticVisualizationInstance oneway - - «create»
*/
-public class LabelVisFactory extends AbstractVisFactory {
+public class LabelVisualization extends AbstractVisFactory {
/**
* The label to render
*/
@@ -61,7 +61,7 @@ public class LabelVisFactory extends AbstractVisFactory {
/**
* Constructor. Solely for API purposes (Parameterizable!)
*/
- public LabelVisFactory() {
+ public LabelVisualization() {
super();
}
@@ -70,7 +70,7 @@ public class LabelVisFactory extends AbstractVisFactory {
*
* @param label Label to use
*/
- public LabelVisFactory(String label) {
+ public LabelVisualization(String label) {
this(label, false);
}
@@ -80,7 +80,7 @@ public class LabelVisFactory extends AbstractVisFactory {
* @param label Label to use
* @param rotated Rotated 90 deg to the left
*/
- public LabelVisFactory(String label, boolean rotated) {
+ public LabelVisualization(String label, boolean rotated) {
super();
this.label = label;
this.rotated = rotated;
@@ -96,7 +96,7 @@ public class LabelVisFactory extends AbstractVisFactory {
SVGPlot svgp = task.getPlot();
VisualizerContext context = task.getContext();
CSSClass cls = new CSSClass(svgp, "unmanaged");
- StyleLibrary style = context.getStyleLibrary();
+ StyleLibrary style = context.getStyleResult().getStyleLibrary();
double fontsize = style.getTextSize("overview.labels") / StyleLibrary.SCALE;
cls.setStatement(SVGConstants.CSS_FONT_SIZE_PROPERTY, SVGUtil.fmt(fontsize));
cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, style.getTextColor("overview.labels"));
@@ -104,17 +104,17 @@ public class LabelVisFactory extends AbstractVisFactory {
Element layer;
if(!rotated) {
- layer = svgp.svgText(task.getWidth() / 2, task.getHeight() / 2 + .35 * fontsize, this.label);
+ layer = svgp.svgText(task.getWidth() * .5, task.getHeight() * .5 + .35 * fontsize, this.label);
SVGUtil.setAtt(layer, SVGConstants.SVG_STYLE_ATTRIBUTE, cls.inlineCSS());
SVGUtil.setAtt(layer, SVGConstants.SVG_TEXT_ANCHOR_ATTRIBUTE, SVGConstants.SVG_MIDDLE_VALUE);
}
else {
- layer = svgp.svgText(- task.getHeight() / 2, task.getWidth() / 2 + .35 * fontsize, this.label);
+ layer = svgp.svgText(- task.getHeight() * .5, task.getWidth() * .5 + .35 * fontsize, this.label);
SVGUtil.setAtt(layer, SVGConstants.SVG_STYLE_ATTRIBUTE, cls.inlineCSS());
SVGUtil.setAtt(layer, SVGConstants.SVG_TEXT_ANCHOR_ATTRIBUTE, SVGConstants.SVG_MIDDLE_VALUE);
SVGUtil.setAtt(layer, SVGConstants.SVG_TRANSFORM_ATTRIBUTE, "rotate(-90)");
}
- return new StaticVisualization(task, layer);
+ return new StaticVisualizationInstance(task, layer);
}
@Override
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/PixmapVisualizer.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/PixmapVisualizer.java
index 9269f404..19cc6254 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/PixmapVisualizer.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/PixmapVisualizer.java
@@ -45,99 +45,99 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
*
* @author Erich Schubert
*
- * @apiviz.has PixmapResult oneway - 1 visualizes
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class PixmapVisualizer extends AbstractVisualization {
+public class PixmapVisualizer extends AbstractVisFactory {
/**
* Name for this visualizer.
*/
private static final String NAME = "Pixmap Visualizer";
/**
- * The actual pixmap result.
+ * Constructor, adhering to
+ * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
*/
- private PixmapResult result;
+ public PixmapVisualizer() {
+ super();
+ }
- /**
- * Constructor.
- *
- * @param task Visualization task
- */
- public PixmapVisualizer(VisualizationTask task) {
- super(task);
- this.result = task.getResult();
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<PixmapResult> prs = ResultUtil.filterResults(result, PixmapResult.class);
+ for(PixmapResult pr : prs) {
+ // Add plots, attach visualizer
+ final VisualizationTask task = new VisualizationTask(NAME, pr, null, this);
+ task.width = pr.getImage().getWidth() / (double) pr.getImage().getHeight();
+ task.height = 1.0;
+ task.level = VisualizationTask.LEVEL_STATIC;
+ baseResult.getHierarchy().add(pr, task);
+ }
+ }
+
+ @Override
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
}
@Override
- protected void redraw() {
- // TODO: Use width, height, imgratio, number of OPTICS plots!
- double scale = StyleLibrary.SCALE;
-
- final double sizex = scale;
- final double sizey = scale * task.getHeight() / task.getWidth();
- final double margin = 0.0; // context.getStyleLibrary().getSize(StyleLibrary.MARGIN);
- layer = SVGUtil.svgElement(svgp.getDocument(), SVGConstants.SVG_G_TAG);
- final String transform = SVGUtil.makeMarginTransform(task.getWidth(), task.getHeight(), sizex, sizey, margin);
- SVGUtil.setAtt(layer, SVGConstants.SVG_TRANSFORM_ATTRIBUTE, transform);
-
- RenderedImage img = result.getImage();
- // is ratio, target ratio
- double iratio = img.getHeight() / img.getWidth();
- double tratio = task.getHeight() / task.getWidth();
- // We want to place a (iratio, 1.0) object on a (tratio, 1.0) screen.
- // Both dimensions must fit:
- double zoom = (iratio >= tratio) ? Math.min(tratio / iratio, 1.0) : Math.max(iratio / tratio, 1.0);
-
- Element itag = svgp.svgElement(SVGConstants.SVG_IMAGE_TAG);
- SVGUtil.setAtt(itag, SVGConstants.SVG_IMAGE_RENDERING_ATTRIBUTE, SVGConstants.SVG_OPTIMIZE_SPEED_VALUE);
- SVGUtil.setAtt(itag, SVGConstants.SVG_X_ATTRIBUTE, 0);
- SVGUtil.setAtt(itag, SVGConstants.SVG_Y_ATTRIBUTE, 0);
- SVGUtil.setAtt(itag, SVGConstants.SVG_WIDTH_ATTRIBUTE, scale * zoom * iratio);
- SVGUtil.setAtt(itag, SVGConstants.SVG_HEIGHT_ATTRIBUTE, scale * zoom);
- itag.setAttributeNS(SVGConstants.XLINK_NAMESPACE_URI, SVGConstants.XLINK_HREF_QNAME, result.getAsFile().toURI().toString());
-
- layer.appendChild(itag);
+ public boolean allowThumbnails(VisualizationTask task) {
+ // Don't use thumbnails
+ return false;
}
/**
- * Factory class for pixmap visualizers.
+ * Instance.
*
* @author Erich Schubert
*
- * @apiviz.stereotype factory
- * @apiviz.uses PixmapVisualizer oneway - - «create»
+ * @apiviz.has PixmapResult oneway - 1 visualizes
*/
- public static class Factory extends AbstractVisFactory {
+ public class Instance extends AbstractVisualization {
/**
- * Constructor, adhering to
- * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
+ * The actual pixmap result.
*/
- public Factory() {
- super();
- }
-
- @Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<PixmapResult> prs = ResultUtil.filterResults(result, PixmapResult.class);
- for(PixmapResult pr : prs) {
- // Add plots, attach visualizer
- final VisualizationTask task = new VisualizationTask(NAME, pr, null, this);
- task.width = pr.getImage().getWidth() / pr.getImage().getHeight();
- task.height = 1.0;
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_STATIC);
- baseResult.getHierarchy().add(pr, task);
- }
- }
+ private PixmapResult result;
- @Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new PixmapVisualizer(task);
+ /**
+ * Constructor.
+ *
+ * @param task Visualization task
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
+ this.result = task.getResult();
}
@Override
- public boolean allowThumbnails(VisualizationTask task) {
- // Don't use thumbnails
- return false;
+ protected void redraw() {
+ // TODO: Use width, height, imgratio, number of OPTICS plots!
+ double scale = StyleLibrary.SCALE;
+
+ final double sizex = scale;
+ final double sizey = scale * task.getHeight() / task.getWidth();
+ final double margin = 0.0; // context.getStyleLibrary().getSize(StyleLibrary.MARGIN);
+ layer = SVGUtil.svgElement(svgp.getDocument(), SVGConstants.SVG_G_TAG);
+ final String transform = SVGUtil.makeMarginTransform(task.getWidth(), task.getHeight(), sizex, sizey, margin);
+ SVGUtil.setAtt(layer, SVGConstants.SVG_TRANSFORM_ATTRIBUTE, transform);
+
+ RenderedImage img = result.getImage();
+ // is ratio, target ratio
+ double iratio = img.getHeight() / img.getWidth();
+ double tratio = task.getHeight() / task.getWidth();
+ // We want to place a (iratio, 1.0) object on a (tratio, 1.0) screen.
+ // Both dimensions must fit:
+ double zoom = (iratio >= tratio) ? Math.min(tratio / iratio, 1.0) : Math.max(iratio / tratio, 1.0);
+
+ Element itag = svgp.svgElement(SVGConstants.SVG_IMAGE_TAG);
+ SVGUtil.setAtt(itag, SVGConstants.SVG_IMAGE_RENDERING_ATTRIBUTE, SVGConstants.SVG_OPTIMIZE_SPEED_VALUE);
+ SVGUtil.setAtt(itag, SVGConstants.SVG_X_ATTRIBUTE, 0);
+ SVGUtil.setAtt(itag, SVGConstants.SVG_Y_ATTRIBUTE, 0);
+ SVGUtil.setAtt(itag, SVGConstants.SVG_WIDTH_ATTRIBUTE, scale * zoom * iratio);
+ SVGUtil.setAtt(itag, SVGConstants.SVG_HEIGHT_ATTRIBUTE, scale * zoom);
+ itag.setAttributeNS(SVGConstants.XLINK_NAMESPACE_URI, SVGConstants.XLINK_HREF_QNAME, result.getAsFile().toURI().toString());
+
+ layer.appendChild(itag);
}
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/SettingsVisFactory.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/SettingsVisualization.java
index 2d726257..736f28e7 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/SettingsVisFactory.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/SettingsVisualization.java
@@ -41,7 +41,7 @@ import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
-import de.lmu.ifi.dbs.elki.visualization.visualizers.StaticVisualization;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.StaticVisualizationInstance;
import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
/**
@@ -50,11 +50,11 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
* @author Erich Schubert
*
* @apiviz.stereotype factory
- * @apiviz.uses StaticVisualization oneway - - «create»
+ * @apiviz.uses StaticVisualizationInstance oneway - - «create»
* @apiviz.has SettingsResult oneway - - visualizes
*/
// TODO: make this a menu item instead of a "visualization"?
-public class SettingsVisFactory extends AbstractVisFactory {
+public class SettingsVisualization extends AbstractVisFactory {
/**
* Name for this visualizer.
*/
@@ -64,7 +64,7 @@ public class SettingsVisFactory extends AbstractVisFactory {
* Constructor, adhering to
* {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
*/
- public SettingsVisFactory() {
+ public SettingsVisualization() {
super();
}
@@ -74,7 +74,7 @@ public class SettingsVisFactory extends AbstractVisFactory {
VisualizerContext context = task.getContext();
SVGPlot svgp = task.getPlot();
- Collection<Pair<Object, Parameter<?, ?>>> settings = sr.getSettings();
+ Collection<Pair<Object, Parameter<?>>> settings = sr.getSettings();
Element layer = svgp.svgElement(SVGConstants.SVG_G_TAG);
@@ -82,7 +82,7 @@ public class SettingsVisFactory extends AbstractVisFactory {
int i = 0;
Object last = null;
- for(Pair<Object, Parameter<?, ?>> setting : settings) {
+ for(Pair<Object, Parameter<?>> setting : settings) {
if(setting.first != last && setting.first != null) {
String name;
try {
@@ -128,11 +128,11 @@ public class SettingsVisFactory extends AbstractVisFactory {
int cols = Math.max(30, (int) (i * task.getHeight() / task.getWidth()));
int rows = i;
- final double margin = context.getStyleLibrary().getSize(StyleLibrary.MARGIN);
+ final double margin = context.getStyleResult().getStyleLibrary().getSize(StyleLibrary.MARGIN);
final String transform = SVGUtil.makeMarginTransform(task.getWidth(), task.getHeight(), cols, rows, margin / StyleLibrary.SCALE);
SVGUtil.setAtt(layer, SVGConstants.SVG_TRANSFORM_ATTRIBUTE, transform);
- return new StaticVisualization(task, layer);
+ return new StaticVisualizationInstance(task, layer);
}
@Override
@@ -142,7 +142,7 @@ public class SettingsVisFactory extends AbstractVisFactory {
final VisualizationTask task = new VisualizationTask(NAME, sr, null, this);
task.width = 1.0;
task.height = 1.0;
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_STATIC);
+ task.level = VisualizationTask.LEVEL_STATIC;
baseResult.getHierarchy().add(sr, task);
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/SimilarityMatrixVisualizer.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/SimilarityMatrixVisualizer.java
index c53a722a..f1404050 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/SimilarityMatrixVisualizer.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/SimilarityMatrixVisualizer.java
@@ -29,7 +29,7 @@ import java.util.Collection;
import org.apache.batik.util.SVGConstants;
import org.w3c.dom.Element;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.evaluation.similaritymatrix.ComputeSimilarityMatrixImage;
import de.lmu.ifi.dbs.elki.evaluation.similaritymatrix.ComputeSimilarityMatrixImage.SimilarityMatrix;
@@ -49,123 +49,121 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
*
* @author Erich Schubert
*
- * @apiviz.has SimilarityMatrix oneway - 1 visualizes
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class SimilarityMatrixVisualizer extends AbstractVisualization {
+public class SimilarityMatrixVisualizer extends AbstractVisFactory {
/**
* Name for this visualizer.
*/
private static final String NAME = "Similarity Matrix Visualizer";
/**
- * The actual pixmap result.
+ * Constructor, adhering to
+ * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
*/
- private SimilarityMatrix result;
-
- /**
- * Constructor.
- *
- * @param task Visualization task
- */
- public SimilarityMatrixVisualizer(VisualizationTask task) {
- super(task);
- this.result = task.getResult();
+ public SimilarityMatrixVisualizer() {
+ super();
}
@Override
- protected void redraw() {
- // TODO: Use width, height, imgratio, number of OPTICS plots!
- double scale = StyleLibrary.SCALE;
-
- final double sizex = scale;
- final double sizey = scale * task.getHeight() / task.getWidth();
- final double margin = context.getStyleLibrary().getSize(StyleLibrary.MARGIN);
- layer = SVGUtil.svgElement(svgp.getDocument(), SVGConstants.SVG_G_TAG);
- final String transform = SVGUtil.makeMarginTransform(task.getWidth(), task.getHeight(), sizex, sizey, margin);
- SVGUtil.setAtt(layer, SVGConstants.SVG_TRANSFORM_ATTRIBUTE, transform);
-
- RenderedImage img = result.getImage();
- // is ratio, target ratio
- double iratio = img.getHeight() / img.getWidth();
- double tratio = task.getHeight() / task.getWidth();
- // We want to place a (iratio, 1.0) object on a (tratio, 1.0) screen.
- // Both dimensions must fit:
- double zoom = (iratio >= tratio) ? Math.min(tratio / iratio, 1.0) : Math.max(iratio / tratio, 1.0);
-
- Element itag = svgp.svgElement(SVGConstants.SVG_IMAGE_TAG);
- SVGUtil.setAtt(itag, SVGConstants.SVG_IMAGE_RENDERING_ATTRIBUTE, SVGConstants.SVG_OPTIMIZE_SPEED_VALUE);
- SVGUtil.setAtt(itag, SVGConstants.SVG_X_ATTRIBUTE, margin * 0.75);
- SVGUtil.setAtt(itag, SVGConstants.SVG_Y_ATTRIBUTE, margin * 0.75);
- SVGUtil.setAtt(itag, SVGConstants.SVG_WIDTH_ATTRIBUTE, scale * zoom * iratio);
- SVGUtil.setAtt(itag, SVGConstants.SVG_HEIGHT_ATTRIBUTE, scale * zoom);
- itag.setAttributeNS(SVGConstants.XLINK_NAMESPACE_URI, SVGConstants.XLINK_HREF_QNAME, result.getAsFile().toURI().toString());
- layer.appendChild(itag);
-
- // Add object labels
- final int size = result.getIDs().size();
- final double hlsize = scale * zoom * iratio / size;
- final double vlsize = scale * zoom / size;
- int i = 0;
- final Relation<String> lrep = DatabaseUtil.guessObjectLabelRepresentation(result.getRelation().getDatabase());
- for(DBID id : result.getIDs()) {
- String label = lrep.get(id);
- if(label != null) {
- // Label on horizontal axis
- final double hlx = margin * 0.75 + hlsize * (i + .8);
- final double hly = margin * 0.7;
- Element lbl = svgp.svgText(hlx, hly, label);
- SVGUtil.setAtt(lbl, SVGConstants.SVG_TRANSFORM_ATTRIBUTE, "rotate(-90," + hlx + "," + hly + ")");
- SVGUtil.setAtt(lbl, SVGConstants.SVG_STYLE_ATTRIBUTE, "font-size: " + hlsize * 0.8);
- layer.appendChild(lbl);
- // Label on vertical axis
- Element lbl2 = svgp.svgText(margin * 0.7, margin * 0.75 + vlsize * (i + .8), label);
- SVGUtil.setAtt(lbl2, SVGConstants.SVG_TEXT_ANCHOR_ATTRIBUTE, SVGConstants.SVG_END_VALUE);
- SVGUtil.setAtt(lbl2, SVGConstants.SVG_STYLE_ATTRIBUTE, "font-size: " + vlsize * 0.8);
- layer.appendChild(lbl2);
- }
- i++;
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<ComputeSimilarityMatrixImage.SimilarityMatrix> prs = ResultUtil.filterResults(result, ComputeSimilarityMatrixImage.SimilarityMatrix.class);
+ for(ComputeSimilarityMatrixImage.SimilarityMatrix pr : prs) {
+ // Add plots, attach visualizer
+ final VisualizationTask task = new VisualizationTask(NAME, pr, null, this);
+ task.width = 1.0;
+ task.height = 1.0;
+ task.level = VisualizationTask.LEVEL_STATIC;
+ baseResult.getHierarchy().add(pr, task);
}
}
+ @Override
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
+ }
+
+ @Override
+ public boolean allowThumbnails(VisualizationTask task) {
+ // Don't use thumbnails
+ return false;
+ }
+
/**
- * Factory class for pixmap visualizers.
+ * Instance
*
* @author Erich Schubert
*
- * @apiviz.stereotype factory
- * @apiviz.uses PixmapVisualizer oneway - - «create»
+ * @apiviz.has SimilarityMatrix oneway - 1 visualizes
*/
- public static class Factory extends AbstractVisFactory {
+ public class Instance extends AbstractVisualization {
/**
- * Constructor, adhering to
- * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
+ * The actual pixmap result.
*/
- public Factory() {
- super();
- }
-
- @Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<ComputeSimilarityMatrixImage.SimilarityMatrix> prs = ResultUtil.filterResults(result, ComputeSimilarityMatrixImage.SimilarityMatrix.class);
- for(ComputeSimilarityMatrixImage.SimilarityMatrix pr : prs) {
- // Add plots, attach visualizer
- final VisualizationTask task = new VisualizationTask(NAME, pr, null, this);
- task.width = 1.0;
- task.height = 1.0;
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_STATIC);
- baseResult.getHierarchy().add(pr, task);
- }
- }
+ private SimilarityMatrix result;
- @Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new SimilarityMatrixVisualizer(task);
+ /**
+ * Constructor.
+ *
+ * @param task Visualization task
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
+ this.result = task.getResult();
}
@Override
- public boolean allowThumbnails(VisualizationTask task) {
- // Don't use thumbnails
- return false;
+ protected void redraw() {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ final double sizex = StyleLibrary.SCALE;
+ final double sizey = StyleLibrary.SCALE * task.getHeight() / task.getWidth();
+ final double margin = style.getSize(StyleLibrary.MARGIN);
+ layer = SVGUtil.svgElement(svgp.getDocument(), SVGConstants.SVG_G_TAG);
+ final String transform = SVGUtil.makeMarginTransform(task.getWidth(), task.getHeight(), sizex, sizey, margin);
+ SVGUtil.setAtt(layer, SVGConstants.SVG_TRANSFORM_ATTRIBUTE, transform);
+
+ RenderedImage img = result.getImage();
+ // is ratio, target ratio
+ double iratio = img.getHeight() / img.getWidth();
+ double tratio = task.getHeight() / task.getWidth();
+ // We want to place a (iratio, 1.0) object on a (tratio, 1.0) screen.
+ // Both dimensions must fit:
+ double zoom = (iratio >= tratio) ? Math.min(tratio / iratio, 1.0) : Math.max(iratio / tratio, 1.0);
+
+ Element itag = svgp.svgElement(SVGConstants.SVG_IMAGE_TAG);
+ SVGUtil.setAtt(itag, SVGConstants.SVG_IMAGE_RENDERING_ATTRIBUTE, SVGConstants.SVG_OPTIMIZE_SPEED_VALUE);
+ SVGUtil.setAtt(itag, SVGConstants.SVG_X_ATTRIBUTE, margin * 0.75);
+ SVGUtil.setAtt(itag, SVGConstants.SVG_Y_ATTRIBUTE, margin * 0.75);
+ SVGUtil.setAtt(itag, SVGConstants.SVG_WIDTH_ATTRIBUTE, StyleLibrary.SCALE * zoom * iratio);
+ SVGUtil.setAtt(itag, SVGConstants.SVG_HEIGHT_ATTRIBUTE, StyleLibrary.SCALE * zoom);
+ itag.setAttributeNS(SVGConstants.XLINK_NAMESPACE_URI, SVGConstants.XLINK_HREF_QNAME, result.getAsFile().toURI().toString());
+ layer.appendChild(itag);
+
+ // Add object labels
+ final int size = result.getIDs().size();
+ final double hlsize = StyleLibrary.SCALE * zoom * iratio / size;
+ final double vlsize = StyleLibrary.SCALE * zoom / size;
+ int i = 0;
+ final Relation<String> lrep = DatabaseUtil.guessObjectLabelRepresentation(result.getRelation().getDatabase());
+ for(DBIDIter id = result.getIDs().iter(); id.valid(); id.advance()) {
+ String label = lrep.get(id);
+ if(label != null) {
+ // Label on horizontal axis
+ final double hlx = margin * 0.75 + hlsize * (i + .8);
+ final double hly = margin * 0.7;
+ Element lbl = svgp.svgText(hlx, hly, label);
+ SVGUtil.setAtt(lbl, SVGConstants.SVG_TRANSFORM_ATTRIBUTE, "rotate(-90," + hlx + "," + hly + ")");
+ SVGUtil.setAtt(lbl, SVGConstants.SVG_STYLE_ATTRIBUTE, "font-size: " + hlsize * 0.8);
+ layer.appendChild(lbl);
+ // Label on vertical axis
+ Element lbl2 = svgp.svgText(margin * 0.7, margin * 0.75 + vlsize * (i + .8), label);
+ SVGUtil.setAtt(lbl2, SVGConstants.SVG_TEXT_ANCHOR_ATTRIBUTE, SVGConstants.SVG_END_VALUE);
+ SVGUtil.setAtt(lbl2, SVGConstants.SVG_STYLE_ATTRIBUTE, "font-size: " + vlsize * 0.8);
+ layer.appendChild(lbl2);
+ }
+ i++;
+ }
}
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/XYCurveVisFactory.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/XYCurveVisualization.java
index 507de376..76ce7c09 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/XYCurveVisFactory.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/XYCurveVisualization.java
@@ -48,7 +48,7 @@ import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGSimpleLinearAxis;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
-import de.lmu.ifi.dbs.elki.visualization.visualizers.StaticVisualization;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.StaticVisualizationInstance;
import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
/**
@@ -57,10 +57,10 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
* @author Erich Schubert
*
* @apiviz.stereotype factory
- * @apiviz.uses StaticVisualization oneway - - «create»
+ * @apiviz.uses StaticVisualizationInstance oneway - - «create»
* @apiviz.has XYCurve oneway - - visualizes
*/
-public class XYCurveVisFactory extends AbstractVisFactory {
+public class XYCurveVisualization extends AbstractVisFactory {
/**
* Name for this visualizer.
*/
@@ -79,7 +79,7 @@ public class XYCurveVisFactory extends AbstractVisFactory {
/**
* Constructor, Parameterizable style - does nothing.
*/
- public XYCurveVisFactory() {
+ public XYCurveVisualization() {
super();
}
@@ -90,10 +90,10 @@ public class XYCurveVisFactory extends AbstractVisFactory {
XYCurve curve = task.getResult();
setupCSS(context, svgp);
- double scale = StyleLibrary.SCALE;
- final double sizex = scale;
- final double sizey = scale * task.getHeight() / task.getWidth();
- final double margin = context.getStyleLibrary().getSize(StyleLibrary.MARGIN);
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ final double sizex = StyleLibrary.SCALE;
+ final double sizey = StyleLibrary.SCALE * task.getHeight() / task.getWidth();
+ final double margin = style.getSize(StyleLibrary.MARGIN);
Element layer = SVGUtil.svgElement(svgp.getDocument(), SVGConstants.SVG_G_TAG);
final String transform = SVGUtil.makeMarginTransform(task.getWidth(), task.getHeight(), sizex, sizey, margin);
SVGUtil.setAtt(layer, SVGConstants.SVG_TRANSFORM_ATTRIBUTE, transform);
@@ -113,15 +113,15 @@ public class XYCurveVisFactory extends AbstractVisFactory {
// add axes
try {
- SVGSimpleLinearAxis.drawAxis(svgp, layer, scaley, 0, sizey, 0, 0, SVGSimpleLinearAxis.LabelStyle.LEFTHAND, context.getStyleLibrary());
- SVGSimpleLinearAxis.drawAxis(svgp, layer, scalex, 0, sizey, sizex, sizey, SVGSimpleLinearAxis.LabelStyle.RIGHTHAND, context.getStyleLibrary());
+ SVGSimpleLinearAxis.drawAxis(svgp, layer, scaley, 0, sizey, 0, 0, SVGSimpleLinearAxis.LabelStyle.LEFTHAND, style);
+ SVGSimpleLinearAxis.drawAxis(svgp, layer, scalex, 0, sizey, sizex, sizey, SVGSimpleLinearAxis.LabelStyle.RIGHTHAND, style);
}
catch(CSSNamingConflict e) {
LoggingUtil.exception(e);
}
// Add axis labels
{
- Element labelx = svgp.svgText(sizex / 2, sizey + margin * .9, curve.getLabelx());
+ Element labelx = svgp.svgText(sizex * .5, sizey + margin * .9, curve.getLabelx());
SVGUtil.setCSSClass(labelx, CSS_AXIS_LABEL);
layer.appendChild(labelx);
Element labely = svgp.svgText(margin * -.8, sizey * .5, curve.getLabely());
@@ -161,7 +161,7 @@ public class XYCurveVisFactory extends AbstractVisFactory {
}
layer.appendChild(line);
- return new StaticVisualization(task, layer);
+ return new StaticVisualizationInstance(task, layer);
}
/**
@@ -170,7 +170,7 @@ public class XYCurveVisFactory extends AbstractVisFactory {
* @param svgp Plot
*/
private void setupCSS(VisualizerContext context, SVGPlot svgp) {
- StyleLibrary style = context.getStyleLibrary();
+ StyleLibrary style = context.getStyleResult().getStyleLibrary();
CSSClass csscls = new CSSClass(this, SERIESID);
// csscls.setStatement(SVGConstants.SVG_STROKE_WIDTH_ATTRIBUTE, "0.2%");
csscls.setStatement(SVGConstants.SVG_FILL_ATTRIBUTE, SVGConstants.SVG_NONE_VALUE);
@@ -193,7 +193,7 @@ public class XYCurveVisFactory extends AbstractVisFactory {
final VisualizationTask task = new VisualizationTask(NAME, curve, null, this);
task.width = 1.0;
task.height = 1.0;
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_STATIC);
+ task.level = VisualizationTask.LEVEL_STATIC;
baseResult.getHierarchy().add(curve, task);
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/workflow/AlgorithmStep.java b/src/de/lmu/ifi/dbs/elki/workflow/AlgorithmStep.java
index e6788568..cdaf702d 100644
--- a/src/de/lmu/ifi/dbs/elki/workflow/AlgorithmStep.java
+++ b/src/de/lmu/ifi/dbs/elki/workflow/AlgorithmStep.java
@@ -55,7 +55,7 @@ public class AlgorithmStep implements WorkflowStep {
/**
* Logger
*/
- private static final Logging logger = Logging.getLogger(AlgorithmStep.class);
+ private static final Logging LOG = Logging.getLogger(AlgorithmStep.class);
/**
* Holds the algorithm to run.
@@ -86,29 +86,29 @@ public class AlgorithmStep implements WorkflowStep {
public HierarchicalResult runAlgorithms(Database database) {
result = new BasicResult("Algorithm Step", "main");
result.addChildResult(database);
- if(logger.isVerbose() && database.getIndexes().size() > 0) {
- StringBuffer buf = new StringBuffer();
+ if(LOG.isVerbose() && database.getIndexes().size() > 0) {
+ StringBuilder buf = new StringBuilder();
buf.append("Index statistics before running algorithms:").append(FormatUtil.NEWLINE);
for(Index idx : database.getIndexes()) {
PageFileStatistics stat = idx.getPageFileStatistics();
PageFileUtil.appendPageFileStatistics(buf, stat);
}
- logger.verbose(buf.toString());
+ LOG.verbose(buf.toString());
}
for(Algorithm algorithm : algorithms) {
long start = System.currentTimeMillis();
Result res = algorithm.run(database);
long end = System.currentTimeMillis();
- if(logger.isVerbose()) {
+ if(LOG.isVerbose()) {
long elapsedTime = end - start;
- StringBuffer buf = new StringBuffer();
+ StringBuilder buf = new StringBuilder();
buf.append(algorithm.getClass().getName()).append(" runtime : ");
buf.append(elapsedTime).append(" milliseconds.").append(FormatUtil.NEWLINE);
for(Index idx : database.getIndexes()) {
PageFileStatistics stat = idx.getPageFileStatistics();
PageFileUtil.appendPageFileStatistics(buf, stat);
}
- logger.verbose(buf.toString());
+ LOG.verbose(buf.toString());
}
if(res != null) {
result.addChildResult(res);
diff --git a/src/de/lmu/ifi/dbs/elki/workflow/LoggingStep.java b/src/de/lmu/ifi/dbs/elki/workflow/LoggingStep.java
index bfe9dba2..b4f6070b 100644
--- a/src/de/lmu/ifi/dbs/elki/workflow/LoggingStep.java
+++ b/src/de/lmu/ifi/dbs/elki/workflow/LoggingStep.java
@@ -46,7 +46,7 @@ public class LoggingStep implements WorkflowStep {
/**
* Logger
*/
- private final static Logging logger = Logging.getLogger(LoggingStep.class);
+ private static final Logging LOG = Logging.getLogger(LoggingStep.class);
/**
* Constructor.
@@ -78,7 +78,7 @@ public class LoggingStep implements WorkflowStep {
}
}
catch(IllegalArgumentException e) {
- logger.warning("Invalid logging statement for package " + pair[0] + ": " + e.getMessage());
+ LOG.warning("Invalid logging statement for package " + pair[0] + ": " + e.getMessage());
}
}
}
@@ -109,7 +109,8 @@ public class LoggingStep implements WorkflowStep {
if(config.grab(verboseF)) {
verbose = verboseF.getValue();
}
- final StringParameter debugP = new StringParameter(OptionID.DEBUG, true);
+ final StringParameter debugP = new StringParameter(OptionID.DEBUG);
+ debugP.setOptional(true);
if(config.grab(debugP)) {
String[] opts = debugP.getValue().split(",");
levels = new String[opts.length][];
diff --git a/src/tutorial/clustering/SameSizeKMeansAlgorithm.java b/src/tutorial/clustering/SameSizeKMeansAlgorithm.java
new file mode 100644
index 00000000..c85af2e9
--- /dev/null
+++ b/src/tutorial/clustering/SameSizeKMeansAlgorithm.java
@@ -0,0 +1,519 @@
+package tutorial.clustering;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+
+import de.lmu.ifi.dbs.elki.algorithm.clustering.kmeans.AbstractKMeans;
+import de.lmu.ifi.dbs.elki.algorithm.clustering.kmeans.KMeansInitialization;
+import de.lmu.ifi.dbs.elki.algorithm.clustering.kmeans.KMeansPlusPlusInitialMeans;
+import de.lmu.ifi.dbs.elki.data.Cluster;
+import de.lmu.ifi.dbs.elki.data.Clustering;
+import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.data.model.MeanModel;
+import de.lmu.ifi.dbs.elki.database.Database;
+import de.lmu.ifi.dbs.elki.database.datastore.DataStoreFactory;
+import de.lmu.ifi.dbs.elki.database.datastore.DataStoreUtil;
+import de.lmu.ifi.dbs.elki.database.datastore.WritableDataStore;
+import de.lmu.ifi.dbs.elki.database.ids.ArrayModifiableDBIDs;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDArrayIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDMIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
+import de.lmu.ifi.dbs.elki.database.ids.ModifiableDBIDs;
+import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
+import de.lmu.ifi.dbs.elki.distance.distancefunction.EuclideanDistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distancefunction.PrimitiveDoubleDistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distancefunction.SquaredEuclideanDistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
+import de.lmu.ifi.dbs.elki.logging.Logging;
+import de.lmu.ifi.dbs.elki.utilities.datastructures.arrays.IntegerArrayQuickSort;
+import de.lmu.ifi.dbs.elki.utilities.datastructures.arrays.IntegerComparator;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterEqualConstraint;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ObjectParameter;
+
+/**
+ * K-means variation that produces equally sized clusters.
+ *
+ * Note that this is a rather obvious variation, and one cannot expect very good
+ * results from this algorithm. K-means already is quite primitive, and putting
+ * in the size constraint will likely not make the results much better (in
+ * particular, it will even less be able to make sense of outliers!)
+ *
+ * There is no reference for this algorithm. If you want to cite it, please cite
+ * the latest ELKI release as given on the ELKI web page:
+ * http://elki.dbs.ifi.lmu.de/wiki/Releases
+ *
+ * @author Erich Schubert
+ *
+ * @param <V> Vector type
+ */
+public class SameSizeKMeansAlgorithm<V extends NumberVector<?>> extends AbstractKMeans<V, DoubleDistance, MeanModel<V>> {
+ /**
+ * Class logger
+ */
+ private static final Logging LOG = Logging.getLogger(SameSizeKMeansAlgorithm.class);
+
+ /**
+ * Constructor.
+ *
+ * @param distanceFunction Distance function
+ * @param k K parameter
+ * @param maxiter Maximum number of iterations
+ * @param initializer
+ */
+ public SameSizeKMeansAlgorithm(PrimitiveDoubleDistanceFunction<? super NumberVector<?>> distanceFunction, int k, int maxiter, KMeansInitialization<V> initializer) {
+ super(distanceFunction, k, maxiter, initializer);
+ }
+
+ /**
+ * Run k-means with cluster size constraints.
+ *
+ * @param database Database
+ * @param relation relation to use
+ * @return result
+ */
+ public Clustering<MeanModel<V>> run(Database database, Relation<V> relation) {
+ // Database objects to process
+ final DBIDs ids = relation.getDBIDs();
+ // Choose initial means
+ List<? extends NumberVector<?>> means = initializer.chooseInitialMeans(relation, k, getDistanceFunction());
+ // Setup cluster assignment store
+ List<ModifiableDBIDs> clusters = new ArrayList<ModifiableDBIDs>();
+ for (int i = 0; i < k; i++) {
+ clusters.add(DBIDUtil.newHashSet(relation.size() / k + 2));
+ }
+
+ // Meta data storage
+ final WritableDataStore<Meta> metas = initializeMeta(relation, means);
+ // Perform the initial assignment
+ ArrayModifiableDBIDs tids = initialAssignment(clusters, metas, ids);
+ // Recompute the means after the initial assignment
+ means = means(clusters, means, relation);
+ // Refine the result via k-means like iterations
+ means = refineResult(relation, means, clusters, metas, tids);
+
+ // Wrap result
+ Clustering<MeanModel<V>> result = new Clustering<MeanModel<V>>("k-Means Samesize Clustering", "kmeans-samesize-clustering");
+ final NumberVector.Factory<V, ?> factory = RelationUtil.getNumberVectorFactory(relation);
+ for (int i = 0; i < clusters.size(); i++) {
+ V mean = factory.newNumberVector(means.get(i).getColumnVector().getArrayRef());
+ MeanModel<V> model = new MeanModel<V>(mean);
+ result.addCluster(new Cluster<MeanModel<V>>(clusters.get(i), model));
+ }
+ return result;
+ }
+
+ /**
+ * Initialize the metadata storage.
+ *
+ * @param relation Relation to process
+ * @param means Mean vectors
+ * @return Initialized storage
+ */
+ protected WritableDataStore<Meta> initializeMeta(Relation<V> relation, List<? extends NumberVector<?>> means) {
+ // This is a safe cast - see constructor.
+ @SuppressWarnings("unchecked")
+ PrimitiveDoubleDistanceFunction<NumberVector<?>> df = (PrimitiveDoubleDistanceFunction<NumberVector<?>>) getDistanceFunction();
+ // The actual storage
+ final WritableDataStore<Meta> metas = DataStoreUtil.makeStorage(relation.getDBIDs(), DataStoreFactory.HINT_HOT | DataStoreFactory.HINT_TEMP, Meta.class);
+ // Build the metadata, track the two nearest cluster centers.
+ for (DBIDIter id = relation.iterDBIDs(); id.valid(); id.advance()) {
+ Meta c = new Meta(k);
+ V fv = relation.get(id);
+ for (int i = 0; i < k; i++) {
+ c.dists[i] = df.doubleDistance(fv, means.get(i));
+ if (i > 0) {
+ if (c.dists[i] < c.dists[c.primary]) {
+ c.primary = i;
+ } else if (c.dists[i] > c.dists[c.secondary]) {
+ c.secondary = i;
+ }
+ }
+ }
+ metas.put(id, c);
+ }
+ return metas;
+ }
+
+ protected ArrayModifiableDBIDs initialAssignment(List<ModifiableDBIDs> clusters, final WritableDataStore<Meta> metas, DBIDs ids) {
+ // Build a sorted list of objects, by descending distance delta
+ ArrayModifiableDBIDs tids = DBIDUtil.newArray(ids);
+ // Our desired cluster size:
+ final int maxsize = (tids.size() + k - 1) / k; // rounded up
+ // Comparator: sort by largest benefit of assigning to preferred cluster.
+ final Comparator<DBIDRef> comp = new Comparator<DBIDRef>() {
+ @Override
+ public int compare(DBIDRef o1, DBIDRef o2) {
+ Meta c1 = metas.get(o1), c2 = metas.get(o2);
+ return -Double.compare(c1.priority(), c2.priority());
+ }
+ };
+ // We will use this iterator below. It support seeking!
+ DBIDArrayIter id = tids.iter();
+
+ // Initialization phase:
+ for (int start = 0; start < tids.size();) {
+ tids.sort(start, tids.size(), comp);
+ for (id.seek(start); id.valid();) {
+ Meta c = metas.get(id);
+ // Assigning to best cluster - which cannot be full yet!
+ ModifiableDBIDs cluster = clusters.get(c.primary);
+ cluster.add(id);
+ start++;
+ id.advance();
+ // Now the cluster may have become completely filled:
+ if (cluster.size() == maxsize) {
+ final int full = c.primary;
+ // Refresh the not yet assigned objects where necessary:
+ for (; id.valid(); id.advance()) {
+ Meta ca = metas.get(id);
+ if (ca.primary == full) {
+ // Update the best index:
+ for (int i = 0; i < k; i++) {
+ if (i == full || clusters.get(i).size() >= maxsize) {
+ continue;
+ }
+ if (ca.primary == full || ca.dists[i] < ca.dists[ca.primary]) {
+ ca.primary = i;
+ }
+ }
+ metas.put(id, ca); // Changed.
+ }
+ }
+ // The next iteration will perform the sorting!
+ break; // not really necessary - iterator is at end anyway.
+ }
+ }
+ // Note: we expect Candidate.a == cluster the object is assigned to!
+ }
+ return tids;
+ }
+
+ /**
+ * Compute the distances of each object to all means. Update
+ * {@link Meta#secondary} to point to the best cluster number except the
+ * current cluster assignment
+ *
+ * @param relation Data relation
+ * @param means Means
+ * @param metas Metadata storage
+ * @param df Distance function
+ */
+ protected void updateDistances(Relation<V> relation, List<? extends NumberVector<?>> means, final WritableDataStore<Meta> metas, PrimitiveDoubleDistanceFunction<NumberVector<?>> df) {
+ for (DBIDIter id = relation.iterDBIDs(); id.valid(); id.advance()) {
+ Meta c = metas.get(id);
+ V fv = relation.get(id);
+ // Update distances to means.
+ c.secondary = -1;
+ for (int i = 0; i < k; i++) {
+ c.dists[i] = df.doubleDistance(fv, means.get(i));
+ if (c.primary != i) {
+ if (c.secondary < 0 || c.dists[i] < c.dists[c.secondary]) {
+ c.secondary = i;
+ }
+ }
+ }
+ metas.put(id, c); // Changed.
+ }
+ }
+
+ /**
+ * Perform k-means style iterations to improve the clustering result.
+ *
+ * @param relation Data relation
+ * @param means Means list
+ * @param clusters Cluster list
+ * @param metas Metadata storage
+ * @param tids DBIDs array
+ * @return final means
+ */
+ protected List<? extends NumberVector<?>> refineResult(Relation<V> relation, List<? extends NumberVector<?>> means, List<ModifiableDBIDs> clusters, final WritableDataStore<Meta> metas, ArrayModifiableDBIDs tids) {
+ // This is a safe cast - see constructor.
+ @SuppressWarnings("unchecked")
+ PrimitiveDoubleDistanceFunction<NumberVector<?>> df = (PrimitiveDoubleDistanceFunction<NumberVector<?>>) getDistanceFunction();
+ // Our desired cluster size:
+ final int minsize = tids.size() / k; // rounded down
+ final int maxsize = (tids.size() + k - 1) / k; // rounded up
+
+ // Comparator: sort by largest gain by transfer
+ final Comparator<DBIDRef> comp = new Comparator<DBIDRef>() {
+ @Override
+ public int compare(DBIDRef o1, DBIDRef o2) {
+ Meta c1 = metas.get(o1), c2 = metas.get(o2);
+ return Double.compare(c1.priority(), c2.priority());
+ }
+ };
+ // List for sorting cluster preferences
+ int[] preferences = new int[k];
+ for (int i = 0; i < k; i++) {
+ preferences[i] = i;
+ }
+ // Comparator for this list.
+ final PreferenceComparator pcomp = new PreferenceComparator();
+
+ // Initialize transfer lists:
+ ArrayModifiableDBIDs[] transfers = new ArrayModifiableDBIDs[k];
+ for (int i = 0; i < k; i++) {
+ transfers[i] = DBIDUtil.newArray();
+ }
+
+ for (int iter = 0; maxiter < 0 || iter < maxiter; iter++) {
+ updateDistances(relation, means, metas, df);
+ tids.sort(comp);
+ int active = 0; // Track if anything has changed
+ for (DBIDIter id = tids.iter(); id.valid(); id.advance()) {
+ Meta c = metas.get(id);
+ ModifiableDBIDs source = clusters.get(c.primary);
+ IntegerArrayQuickSort.sort(preferences, pcomp.select(c));
+ boolean transferred = false;
+ for (int i : preferences) {
+ if (i == c.primary) {
+ continue; // Cannot transfer to the same cluster!
+ }
+ ModifiableDBIDs dest = clusters.get(i);
+ // Can we pair this transfer?
+ for (DBIDMIter other = transfers[i].iter(); other.valid(); other.advance()) {
+ Meta c2 = metas.get(other);
+ if (c.gain(i) + c2.gain(c.primary) > 0) {
+ transfer(metas, c2, dest, source, other, c.primary);
+ transfer(metas, c, source, dest, id, i);
+ active += 2;
+ transferred = true;
+ other.remove(); // last, as this invalides the reference!
+ break;
+ }
+ }
+ // If cluster sizes allow, move a single object.
+ if (c.gain(i) > 0 && (dest.size() < maxsize && source.size() > minsize)) {
+ transfer(metas, c, source, dest, id, i);
+ active += 1;
+ transferred = true;
+ break;
+ }
+ }
+ // If the object would prefer a different cluster, put in outgoing
+ // transfer list.
+ if (!transferred && (c.dists[c.primary] > c.dists[c.secondary])) {
+ transfers[c.primary].add(id);
+ }
+ }
+ // TODO: try to get more transfers out of the transfer lists done by
+ // considering more than one object?
+ int pending = 0;
+ // Clear transfer lists for next iteration.
+ for (int i = 0; i < k; i++) {
+ pending += transfers[i].size();
+ transfers[i].clear();
+ }
+ if (LOG.isDebuggingFine()) {
+ LOG.debugFine("Performed " + active + " transfers in iteration " + iter + " skipped " + pending);
+ }
+ if (active <= 0) {
+ break;
+ }
+ // Recompute means after reassignment
+ means = means(clusters, means, relation);
+ }
+ return means;
+ }
+
+ /**
+ * Transfer a single element from one cluster to another.
+ *
+ * @param metas Meta storage
+ * @param meta Meta of current object
+ * @param src Source cluster
+ * @param dst Destination cluster
+ * @param id Object ID
+ * @param dstnum Destination cluster number
+ */
+ protected void transfer(final WritableDataStore<Meta> metas, Meta meta, ModifiableDBIDs src, ModifiableDBIDs dst, DBIDRef id, Integer dstnum) {
+ src.remove(id);
+ dst.add(id);
+ meta.primary = dstnum;
+ metas.put(id, meta); // Make sure the storage is up to date.
+ }
+
+ /**
+ * Object metadata.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ private class Meta {
+ /**
+ * Distance to the cluster centers.
+ */
+ double[] dists;
+
+ /**
+ * Indexes: primary assignment (current or best), secondary assignment
+ * (second best or worst). The actual meanin changes from initialization to
+ * iteration phase!
+ */
+ int primary, secondary;
+
+ /**
+ * Constructor.
+ *
+ * @param k
+ */
+ protected Meta(int k) {
+ dists = new double[k];
+ Arrays.fill(dists, Double.POSITIVE_INFINITY);
+ primary = 0;
+ secondary = 0;
+ }
+
+ /**
+ * Priority / badness: difference between best and worst. (Assuming that
+ * "secondary" is the worst).
+ *
+ * @return Priority
+ */
+ protected double priority() {
+ return dists[secondary] - dists[primary];
+ }
+
+ /**
+ * Gain from switching to cluster i.
+ *
+ * @param i Target cluster
+ * @return Gain
+ */
+ protected double gain(int i) {
+ return dists[primary] - dists[i];
+ }
+ }
+
+ /**
+ * Sort a list of integers (= cluster numbers) by the distances.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public class PreferenceComparator implements IntegerComparator {
+ /**
+ * Meta to use for comparison.
+ */
+ Meta c = null;
+
+ @Override
+ public int compare(int o1, int o2) {
+ return Double.compare(c.dists[o1], c.dists[o2]);
+ }
+
+ /**
+ * Set the meta to sort by
+ *
+ * @param c Meta to sort by
+ * @return The comparator
+ */
+ public IntegerComparator select(Meta c) {
+ this.c = c;
+ return this;
+ }
+ }
+
+ @Override
+ protected Logging getLogger() {
+ return LOG;
+ }
+
+ /**
+ * Parameterization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public static class Parameterizer<V extends NumberVector<?>> extends AbstractParameterizer {
+ /**
+ * k Parameter.
+ */
+ protected int k;
+
+ /**
+ * Number of iterations.
+ */
+ protected int maxiter = -1;
+
+ /**
+ * Initialization method.
+ */
+ protected KMeansInitialization<V> initializer;
+
+ /**
+ * Distance function
+ */
+ protected PrimitiveDoubleDistanceFunction<? super NumberVector<?>> distanceFunction;
+
+ @Override
+ protected void makeOptions(Parameterization config) {
+ super.makeOptions(config);
+ ObjectParameter<PrimitiveDoubleDistanceFunction<? super NumberVector<?>>> distanceFunctionP = makeParameterDistanceFunction(SquaredEuclideanDistanceFunction.class, PrimitiveDoubleDistanceFunction.class);
+ if (config.grab(distanceFunctionP)) {
+ distanceFunction = distanceFunctionP.instantiateClass(config);
+ if (!(distanceFunction instanceof EuclideanDistanceFunction) && !(distanceFunction instanceof SquaredEuclideanDistanceFunction)) {
+ LOG.warning("k-means optimizes the sum of squares - it should be used with squared euclidean distance and may stop converging otherwise!");
+ }
+ }
+
+ IntParameter kP = new IntParameter(K_ID);
+ kP.addConstraint(new GreaterConstraint(1));
+ if (config.grab(kP)) {
+ k = kP.getValue();
+ }
+
+ ObjectParameter<KMeansInitialization<V>> initialP = new ObjectParameter<KMeansInitialization<V>>(INIT_ID, KMeansInitialization.class, KMeansPlusPlusInitialMeans.class);
+ if (config.grab(initialP)) {
+ initializer = initialP.instantiateClass(config);
+ }
+
+ IntParameter maxiterP = new IntParameter(MAXITER_ID, -1);
+ maxiterP.addConstraint(new GreaterEqualConstraint(-1));
+ if (config.grab(maxiterP)) {
+ maxiter = maxiterP.intValue();
+ }
+ }
+
+ @Override
+ protected SameSizeKMeansAlgorithm<V> makeInstance() {
+ return new SameSizeKMeansAlgorithm<V>(distanceFunction, k, maxiter, initializer);
+ }
+ }
+}
diff --git a/src/tutorial/clustering/package-info.java b/src/tutorial/clustering/package-info.java
new file mode 100644
index 00000000..2dd893ab
--- /dev/null
+++ b/src/tutorial/clustering/package-info.java
@@ -0,0 +1,26 @@
+/**
+ * <p>Classes from the tutorial on implementing a custom k-means variation.</p>
+ */
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package tutorial.clustering; \ No newline at end of file
diff --git a/src/tutorial/distancefunction/MultiLPNorm.java b/src/tutorial/distancefunction/MultiLPNorm.java
index 6909d2ff..4dca1a93 100644
--- a/src/tutorial/distancefunction/MultiLPNorm.java
+++ b/src/tutorial/distancefunction/MultiLPNorm.java
@@ -72,14 +72,14 @@ public class MultiLPNorm extends AbstractVectorDoubleDistanceFunction {
}
@Override
- public double doubleDistance(NumberVector<?, ?> o1, NumberVector<?, ?> o2) {
+ public double doubleDistance(NumberVector<?> o1, NumberVector<?> o2) {
assert o1.getDimensionality() == ps.length : "Inappropriate dimensionality!";
assert o2.getDimensionality() == ps.length : "Inappropriate dimensionality!";
double sum = 0.0;
for(int dim = 0; dim < ps.length; dim++) {
if(ps[dim] > 0) {
- final double delta = Math.abs(o1.doubleValue(dim + 1) - o2.doubleValue(dim + 1));
+ final double delta = Math.abs(o1.doubleValue(dim) - o2.doubleValue(dim));
sum += Math.pow(delta, ps[dim]);
}
}
@@ -87,14 +87,16 @@ public class MultiLPNorm extends AbstractVectorDoubleDistanceFunction {
}
@Override
- public SimpleTypeInformation<? super NumberVector<?, ?>> getInputTypeRestriction() {
- return new VectorFieldTypeInformation<NumberVector<?, ?>>(NumberVector.class, ps.length);
+ public SimpleTypeInformation<? super NumberVector<?>> getInputTypeRestriction() {
+ return new VectorFieldTypeInformation<NumberVector<?>>(NumberVector.class, ps.length);
}
/**
* Parameterization class example
*
* @author Erich Schubert
+ *
+ * @apiviz.exclude
*/
public static class Parameterizer extends AbstractParameterizer {
/**
@@ -102,7 +104,7 @@ public class MultiLPNorm extends AbstractVectorDoubleDistanceFunction {
* -multinorm.ps
* </code>
*/
- public static final OptionID EXPONENTS_ID = OptionID.getOrCreateOptionID("multinorm.ps", "The exponents to use for this distance function");
+ public static final OptionID EXPONENTS_ID = new OptionID("multinorm.ps", "The exponents to use for this distance function");
/**
* P exponents
@@ -123,4 +125,4 @@ public class MultiLPNorm extends AbstractVectorDoubleDistanceFunction {
return new MultiLPNorm(ps);
}
}
-} \ No newline at end of file
+}
diff --git a/src/tutorial/distancefunction/TutorialDistanceFunction.java b/src/tutorial/distancefunction/TutorialDistanceFunction.java
index 0b225c61..e41f8093 100644
--- a/src/tutorial/distancefunction/TutorialDistanceFunction.java
+++ b/src/tutorial/distancefunction/TutorialDistanceFunction.java
@@ -37,14 +37,14 @@ import de.lmu.ifi.dbs.elki.distance.distancefunction.AbstractVectorDoubleDistanc
*/
public class TutorialDistanceFunction extends AbstractVectorDoubleDistanceFunction {
@Override
- public double doubleDistance(NumberVector<?, ?> o1, NumberVector<?, ?> o2) {
- double dx = (o1.doubleValue(1) - o2.doubleValue(1));
- double dy = (o1.doubleValue(2) - o2.doubleValue(2));
+ public double doubleDistance(NumberVector<?> o1, NumberVector<?> o2) {
+ double dx = (o1.doubleValue(0) - o2.doubleValue(0));
+ double dy = (o1.doubleValue(1) - o2.doubleValue(1));
return dx * dx + Math.abs(dy);
}
@Override
- public SimpleTypeInformation<? super NumberVector<?, ?>> getInputTypeRestriction() {
- return new VectorFieldTypeInformation<NumberVector<?, ?>>(NumberVector.class, 2);
+ public SimpleTypeInformation<? super NumberVector<?>> getInputTypeRestriction() {
+ return new VectorFieldTypeInformation<NumberVector<?>>(NumberVector.class, 2);
}
} \ No newline at end of file
diff --git a/src/tutorial/outlier/DistanceStddevOutlier.java b/src/tutorial/outlier/DistanceStddevOutlier.java
index 56c9e002..97b9898c 100644
--- a/src/tutorial/outlier/DistanceStddevOutlier.java
+++ b/src/tutorial/outlier/DistanceStddevOutlier.java
@@ -9,14 +9,14 @@ import de.lmu.ifi.dbs.elki.database.QueryUtil;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreFactory;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreUtil;
import de.lmu.ifi.dbs.elki.database.datastore.WritableDoubleDataStore;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.query.knn.KNNQuery;
-import de.lmu.ifi.dbs.elki.database.query.knn.KNNResult;
import de.lmu.ifi.dbs.elki.database.relation.MaterializedRelation;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResultIter;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.math.DoubleMinMax;
@@ -42,7 +42,7 @@ public class DistanceStddevOutlier<O, D extends NumberDistance<D, ?>> extends Ab
/**
* Class logger
*/
- private static final Logging logger = Logging.getLogger(DistanceStddevOutlier.class);
+ private static final Logging LOG = Logging.getLogger(DistanceStddevOutlier.class);
/**
* Number of neighbors to get.
@@ -77,19 +77,18 @@ public class DistanceStddevOutlier<O, D extends NumberDistance<D, ?>> extends Ab
// Iterate over all objects
for(DBIDIter iter = relation.iterDBIDs(); iter.valid(); iter.advance()) {
- DBID id = iter.getDBID();
- KNNResult<D> neighbors = knnq.getKNNForDBID(id, k);
+ KNNResult<D> neighbors = knnq.getKNNForDBID(iter, k);
// Aggregate distances
MeanVariance mv = new MeanVariance();
- for(DistanceResultPair<D> neighbor : neighbors) {
+ for(DistanceDBIDResultIter<D> neighbor = neighbors.iter(); neighbor.valid(); neighbor.advance()) {
// Skip the object itself. The 0 is not very informative.
- if(id.sameDBID(neighbor.getDBID())) {
+ if(DBIDUtil.equal(iter, neighbor)) {
continue;
}
mv.put(neighbor.getDistance().doubleValue());
}
// Store score
- scores.putDouble(id, mv.getSampleStddev());
+ scores.putDouble(iter, mv.getSampleStddev());
}
// Wrap the result in the standard containers
@@ -106,7 +105,7 @@ public class DistanceStddevOutlier<O, D extends NumberDistance<D, ?>> extends Ab
@Override
protected Logging getLogger() {
- return logger;
+ return LOG;
}
/**
@@ -123,7 +122,7 @@ public class DistanceStddevOutlier<O, D extends NumberDistance<D, ?>> extends Ab
/**
* Option ID for parameterization.
*/
- public static final OptionID K_ID = OptionID.getOrCreateOptionID("stddevout.k", "Number of neighbors to get for stddev based outlier detection.");
+ public static final OptionID K_ID = new OptionID("stddevout.k", "Number of neighbors to get for stddev based outlier detection.");
/**
* Number of neighbors to get
@@ -134,7 +133,8 @@ public class DistanceStddevOutlier<O, D extends NumberDistance<D, ?>> extends Ab
protected void makeOptions(Parameterization config) {
// The super class has the distance function parameter!
super.makeOptions(config);
- IntParameter kParam = new IntParameter(K_ID, new GreaterConstraint(1));
+ IntParameter kParam = new IntParameter(K_ID);
+ kParam.addConstraint(new GreaterConstraint(1));
if(config.grab(kParam)) {
k = kParam.getValue();
}
diff --git a/test/de/lmu/ifi/dbs/elki/algorithm/AbstractSimpleAlgorithmTest.java b/test/de/lmu/ifi/dbs/elki/algorithm/AbstractSimpleAlgorithmTest.java
index 3f9171aa..b33a8432 100644
--- a/test/de/lmu/ifi/dbs/elki/algorithm/AbstractSimpleAlgorithmTest.java
+++ b/test/de/lmu/ifi/dbs/elki/algorithm/AbstractSimpleAlgorithmTest.java
@@ -179,7 +179,7 @@ public abstract class AbstractSimpleAlgorithmTest {
Arrays.sort(expected);
// Report
// if(logger.isVerbose()) {
- StringBuffer buf = new StringBuffer();
+ StringBuilder buf = new StringBuilder();
buf.append("Cluster sizes: [");
for(int i = 0; i < sizes.size(); i++) {
if(i > 0) {
diff --git a/test/de/lmu/ifi/dbs/elki/algorithm/TestKNNJoin.java b/test/de/lmu/ifi/dbs/elki/algorithm/TestKNNJoin.java
index 6c3ddc1a..f38f3dad 100644
--- a/test/de/lmu/ifi/dbs/elki/algorithm/TestKNNJoin.java
+++ b/test/de/lmu/ifi/dbs/elki/algorithm/TestKNNJoin.java
@@ -36,16 +36,15 @@ import de.lmu.ifi.dbs.elki.database.Database;
import de.lmu.ifi.dbs.elki.database.QueryUtil;
import de.lmu.ifi.dbs.elki.database.StaticArrayDatabase;
import de.lmu.ifi.dbs.elki.database.datastore.DataStore;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.database.query.knn.KNNQuery;
-import de.lmu.ifi.dbs.elki.database.query.knn.KNNResult;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.datasource.FileBasedDatabaseConnection;
import de.lmu.ifi.dbs.elki.datasource.filter.FixedDBIDsFilter;
import de.lmu.ifi.dbs.elki.distance.distancefunction.EuclideanDistanceFunction;
import de.lmu.ifi.dbs.elki.distance.distancefunction.ManhattanDistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
import de.lmu.ifi.dbs.elki.index.tree.TreeIndexFactory;
import de.lmu.ifi.dbs.elki.index.tree.spatial.SpatialEntry;
@@ -56,7 +55,6 @@ import de.lmu.ifi.dbs.elki.index.tree.spatial.rstarvariants.rstar.RStarTreeFacto
import de.lmu.ifi.dbs.elki.index.tree.spatial.rstarvariants.rstar.RStarTreeNode;
import de.lmu.ifi.dbs.elki.math.MeanVariance;
import de.lmu.ifi.dbs.elki.utilities.ClassGenericsUtil;
-import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.KNNList;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.ParameterException;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.ListParameterization;
@@ -92,14 +90,14 @@ public class TestKNNJoin implements JUnit4Test {
inputparams.failOnErrors();
db.initialize();
- Relation<NumberVector<?, ?>> relation = db.getRelation(TypeUtil.NUMBER_VECTOR_FIELD);
+ Relation<NumberVector<?>> relation = db.getRelation(TypeUtil.NUMBER_VECTOR_FIELD);
// verify data set size.
org.junit.Assert.assertEquals("Database size does not match.", shoulds, relation.size());
// Euclidean
{
- DistanceQuery<NumberVector<?, ?>, DoubleDistance> dq = db.getDistanceQuery(relation, EuclideanDistanceFunction.STATIC);
- KNNQuery<NumberVector<?, ?>, DoubleDistance> knnq = QueryUtil.getLinearScanKNNQuery(dq);
+ DistanceQuery<NumberVector<?>, DoubleDistance> dq = db.getDistanceQuery(relation, EuclideanDistanceFunction.STATIC);
+ KNNQuery<NumberVector<?>, DoubleDistance> knnq = QueryUtil.getLinearScanKNNQuery(dq);
MeanVariance meansize = new MeanVariance();
for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
@@ -111,8 +109,8 @@ public class TestKNNJoin implements JUnit4Test {
}
// Manhattan
{
- DistanceQuery<NumberVector<?, ?>, DoubleDistance> dq = db.getDistanceQuery(relation, ManhattanDistanceFunction.STATIC);
- KNNQuery<NumberVector<?, ?>, DoubleDistance> knnq = QueryUtil.getLinearScanKNNQuery(dq);
+ DistanceQuery<NumberVector<?>, DoubleDistance> dq = db.getDistanceQuery(relation, ManhattanDistanceFunction.STATIC);
+ KNNQuery<NumberVector<?>, DoubleDistance> knnq = QueryUtil.getLinearScanKNNQuery(dq);
MeanVariance meansize = new MeanVariance();
for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
@@ -183,18 +181,18 @@ public class TestKNNJoin implements JUnit4Test {
inputparams.failOnErrors();
db.initialize();
- Relation<NumberVector<?, ?>> relation = db.getRelation(TypeUtil.NUMBER_VECTOR_FIELD);
+ Relation<NumberVector<?>> relation = db.getRelation(TypeUtil.NUMBER_VECTOR_FIELD);
// verify data set size.
org.junit.Assert.assertEquals("Database size does not match.", shoulds, relation.size());
// Euclidean
{
KNNJoin<DoubleVector, DoubleDistance, ?, ?> knnjoin = new KNNJoin<DoubleVector, DoubleDistance, RStarTreeNode, SpatialEntry>(EuclideanDistanceFunction.STATIC, 2);
- DataStore<KNNList<DoubleDistance>> result = knnjoin.run(db);
+ DataStore<KNNResult<DoubleDistance>> result = knnjoin.run(db);
MeanVariance meansize = new MeanVariance();
- for(DBID id : relation.getDBIDs()) {
- KNNList<DoubleDistance> knnlist = result.get(id);
+ for(DBIDIter id = relation.getDBIDs().iter(); id.valid(); id.advance()) {
+ KNNResult<DoubleDistance> knnlist = result.get(id);
meansize.put(knnlist.size());
}
org.junit.Assert.assertEquals("Euclidean mean 2NN", mean2nnEuclid, meansize.getMean(), 0.00001);
@@ -203,11 +201,11 @@ public class TestKNNJoin implements JUnit4Test {
// Manhattan
{
KNNJoin<DoubleVector, DoubleDistance, ?, ?> knnjoin = new KNNJoin<DoubleVector, DoubleDistance, RStarTreeNode, SpatialEntry>(ManhattanDistanceFunction.STATIC, 2);
- DataStore<KNNList<DoubleDistance>> result = knnjoin.run(db);
+ DataStore<KNNResult<DoubleDistance>> result = knnjoin.run(db);
MeanVariance meansize = new MeanVariance();
- for(DBID id : relation.getDBIDs()) {
- KNNList<DoubleDistance> knnlist = result.get(id);
+ for(DBIDIter id = relation.getDBIDs().iter(); id.valid(); id.advance()) {
+ KNNResult<DoubleDistance> knnlist = result.get(id);
meansize.put(knnlist.size());
}
org.junit.Assert.assertEquals("Manhattan mean 2NN", mean2nnManhattan, meansize.getMean(), 0.00001);
diff --git a/test/de/lmu/ifi/dbs/elki/algorithm/clustering/TestSLINKResults.java b/test/de/lmu/ifi/dbs/elki/algorithm/clustering/TestSLINKResults.java
index 18a16e0e..44160dd0 100644
--- a/test/de/lmu/ifi/dbs/elki/algorithm/clustering/TestSLINKResults.java
+++ b/test/de/lmu/ifi/dbs/elki/algorithm/clustering/TestSLINKResults.java
@@ -60,7 +60,7 @@ public class TestSLINKResults extends AbstractSimpleAlgorithmTest implements JUn
// Setup algorithm
ListParameterization params = new ListParameterization();
- params.addParameter(SLINK.SLINK_MINCLUSTERS_ID, 3);
+ params.addParameter(SLINK.Parameterizer.SLINK_MINCLUSTERS_ID, 3);
SLINK<DoubleVector, DoubleDistance> slink = ClassGenericsUtil.parameterizeOrAbort(SLINK.class, params);
testParameterizationOk(params);
diff --git a/test/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/TestCASHResults.java b/test/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/TestCASHResults.java
index 2757ab36..ad11e6d2 100644
--- a/test/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/TestCASHResults.java
+++ b/test/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/TestCASHResults.java
@@ -28,12 +28,10 @@ import org.junit.Test;
import de.lmu.ifi.dbs.elki.JUnit4Test;
import de.lmu.ifi.dbs.elki.algorithm.AbstractSimpleAlgorithmTest;
import de.lmu.ifi.dbs.elki.data.Clustering;
-import de.lmu.ifi.dbs.elki.data.ParameterizationFunction;
+import de.lmu.ifi.dbs.elki.data.DoubleVector;
import de.lmu.ifi.dbs.elki.data.model.Model;
import de.lmu.ifi.dbs.elki.database.Database;
-import de.lmu.ifi.dbs.elki.datasource.parser.NumberVectorLabelParser;
import de.lmu.ifi.dbs.elki.utilities.ClassGenericsUtil;
-import de.lmu.ifi.dbs.elki.utilities.optionhandling.ParameterException;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.ListParameterization;
/**
@@ -48,16 +46,11 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.ListParamet
public class TestCASHResults extends AbstractSimpleAlgorithmTest implements JUnit4Test {
/**
* Run CASH with fixed parameters and compare the result to a golden standard.
- *
- * @throws ParameterException on errors.
*/
@Test
public void testCASHResults() {
- ListParameterization inp = new ListParameterization();
- // CASH input
- inp.addParameter(NumberVectorLabelParser.VECTOR_TYPE_ID, ParameterizationFunction.class);
// Input
- Database db = makeSimpleDatabase(UNITTEST + "hierarchical-3d2d1d.csv", 600, inp, null);
+ Database db = makeSimpleDatabase(UNITTEST + "hierarchical-3d2d1d.csv", 600, new ListParameterization(), null);
// CASH parameters
ListParameterization params = new ListParameterization();
@@ -67,7 +60,7 @@ public class TestCASHResults extends AbstractSimpleAlgorithmTest implements JUni
params.addFlag(CASH.ADJUST_ID);
// setup algorithm
- CASH cash = ClassGenericsUtil.parameterizeOrAbort(CASH.class, params);
+ CASH<DoubleVector> cash = ClassGenericsUtil.parameterizeOrAbort(CASH.class, params);
testParameterizationOk(params);
// run CASH on database
@@ -79,15 +72,11 @@ public class TestCASHResults extends AbstractSimpleAlgorithmTest implements JUni
/**
* Run CASH with fixed parameters and compare the result to a golden standard.
- *
- * @throws ParameterException on errors.
*/
@Test
public void testCASHEmbedded() {
// CASH input
- ListParameterization inp = new ListParameterization();
- inp.addParameter(NumberVectorLabelParser.VECTOR_TYPE_ID, ParameterizationFunction.class);
- Database db = makeSimpleDatabase(UNITTEST + "correlation-embedded-2-4d.ascii", 600, inp, null);
+ Database db = makeSimpleDatabase(UNITTEST + "correlation-embedded-2-4d.ascii", 600, new ListParameterization(), null);
// CASH parameters
ListParameterization params = new ListParameterization();
@@ -96,7 +85,7 @@ public class TestCASHResults extends AbstractSimpleAlgorithmTest implements JUni
params.addParameter(CASH.MAXLEVEL_ID, 40);
// setup algorithm
- CASH cash = ClassGenericsUtil.parameterizeOrAbort(CASH.class, params);
+ CASH<DoubleVector> cash = ClassGenericsUtil.parameterizeOrAbort(CASH.class, params);
testParameterizationOk(params);
// run CASH on database
diff --git a/test/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/TestORCLUSResults.java b/test/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/TestORCLUSResults.java
index ef9ad063..5cdb09c9 100644
--- a/test/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/TestORCLUSResults.java
+++ b/test/de/lmu/ifi/dbs/elki/algorithm/clustering/correlation/TestORCLUSResults.java
@@ -56,9 +56,9 @@ public class TestORCLUSResults extends AbstractSimpleAlgorithmTest implements JU
Database db = makeSimpleDatabase(UNITTEST + "correlation-hierarchy.csv", 450);
ListParameterization params = new ListParameterization();
- params.addParameter(ORCLUS.K_ID, 3);
- params.addParameter(ORCLUS.L_ID, 1);
- params.addParameter(ORCLUS.SEED_ID, 2);
+ params.addParameter(ORCLUS.Parameterizer.K_ID, 3);
+ params.addParameter(ORCLUS.Parameterizer.L_ID, 1);
+ params.addParameter(ORCLUS.Parameterizer.SEED_ID, 2);
// setup algorithm
ORCLUS<DoubleVector> orclus = ClassGenericsUtil.parameterizeOrAbort(ORCLUS.class, params);
@@ -83,9 +83,9 @@ public class TestORCLUSResults extends AbstractSimpleAlgorithmTest implements JU
// Setup algorithm
ListParameterization params = new ListParameterization();
- params.addParameter(ORCLUS.K_ID, 3);
- params.addParameter(ORCLUS.L_ID, 4);
- params.addParameter(ORCLUS.SEED_ID, 9);
+ params.addParameter(ORCLUS.Parameterizer.K_ID, 3);
+ params.addParameter(ORCLUS.Parameterizer.L_ID, 4);
+ params.addParameter(ORCLUS.Parameterizer.SEED_ID, 9);
ORCLUS<DoubleVector> orclus = ClassGenericsUtil.parameterizeOrAbort(ORCLUS.class, params);
testParameterizationOk(params);
diff --git a/test/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/TestKMeansResults.java b/test/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/TestKMeansResults.java
index 589be8ad..bfe57052 100644
--- a/test/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/TestKMeansResults.java
+++ b/test/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/TestKMeansResults.java
@@ -62,11 +62,11 @@ public class TestKMeansResults extends AbstractSimpleAlgorithmTest implements JU
ListParameterization params = new ListParameterization();
params.addParameter(KMeans.K_ID, 5);
params.addParameter(KMeans.SEED_ID, 3);
- AbstractKMeans<DoubleVector, DoubleDistance> kmeans = ClassGenericsUtil.parameterizeOrAbort(KMeansLloyd.class, params);
+ AbstractKMeans<DoubleVector, DoubleDistance, ?> kmeans = ClassGenericsUtil.parameterizeOrAbort(KMeansLloyd.class, params);
testParameterizationOk(params);
// run KMeans on database
- Clustering<MeanModel<DoubleVector>> result = kmeans.run(db);
+ Clustering<? extends MeanModel<DoubleVector>> result = kmeans.run(db);
testFMeasure(db, result, 0.998005);
testClusterSizes(result, new int[] { 199, 200, 200, 200, 201 });
}
@@ -85,11 +85,11 @@ public class TestKMeansResults extends AbstractSimpleAlgorithmTest implements JU
ListParameterization params = new ListParameterization();
params.addParameter(KMeans.K_ID, 5);
params.addParameter(KMeans.SEED_ID, 3);
- AbstractKMeans<DoubleVector, DoubleDistance> kmeans = ClassGenericsUtil.parameterizeOrAbort(KMeansMacQueen.class, params);
+ AbstractKMeans<DoubleVector, DoubleDistance, ?> kmeans = ClassGenericsUtil.parameterizeOrAbort(KMeansMacQueen.class, params);
testParameterizationOk(params);
// run KMeans on database
- Clustering<MeanModel<DoubleVector>> result = kmeans.run(db);
+ Clustering<? extends MeanModel<DoubleVector>> result = kmeans.run(db);
testFMeasure(db, result, 0.998005);
testClusterSizes(result, new int[] { 199, 200, 200, 200, 201 });
}
@@ -108,11 +108,11 @@ public class TestKMeansResults extends AbstractSimpleAlgorithmTest implements JU
ListParameterization params = new ListParameterization();
params.addParameter(KMeans.K_ID, 5);
params.addParameter(KMeans.SEED_ID, 3);
- AbstractKMeans<DoubleVector, DoubleDistance> kmedians = ClassGenericsUtil.parameterizeOrAbort(KMediansLloyd.class, params);
+ AbstractKMeans<DoubleVector, DoubleDistance, ?> kmedians = ClassGenericsUtil.parameterizeOrAbort(KMediansLloyd.class, params);
testParameterizationOk(params);
// run KMedians on database
- Clustering<MeanModel<DoubleVector>> result = kmedians.run(db);
+ Clustering<? extends MeanModel<DoubleVector>> result = kmedians.run(db);
testFMeasure(db, result, 0.998005);
testClusterSizes(result, new int[] { 199, 200, 200, 200, 201 });
}
diff --git a/test/de/lmu/ifi/dbs/elki/algorithm/clustering/subspace/TestPROCLUSResults.java b/test/de/lmu/ifi/dbs/elki/algorithm/clustering/subspace/TestPROCLUSResults.java
index 82038b8b..b7dde28e 100644
--- a/test/de/lmu/ifi/dbs/elki/algorithm/clustering/subspace/TestPROCLUSResults.java
+++ b/test/de/lmu/ifi/dbs/elki/algorithm/clustering/subspace/TestPROCLUSResults.java
@@ -56,9 +56,9 @@ public class TestPROCLUSResults extends AbstractSimpleAlgorithmTest implements J
Database db = makeSimpleDatabase(UNITTEST + "subspace-simple.csv", 600);
ListParameterization params = new ListParameterization();
- params.addParameter(PROCLUS.L_ID, 1);
- params.addParameter(PROCLUS.K_ID, 4);
- params.addParameter(PROCLUS.SEED_ID, 1);
+ params.addParameter(PROCLUS.Parameterizer.L_ID, 1);
+ params.addParameter(PROCLUS.Parameterizer.K_ID, 4);
+ params.addParameter(PROCLUS.Parameterizer.SEED_ID, 2);
// setup algorithm
PROCLUS<DoubleVector> proclus = ClassGenericsUtil.parameterizeOrAbort(PROCLUS.class, params);
@@ -67,8 +67,8 @@ public class TestPROCLUSResults extends AbstractSimpleAlgorithmTest implements J
// run PROCLUS on database
Clustering<?> result = proclus.run(db);
- testFMeasure(db, result, 0.7541038);
- testClusterSizes(result, new int[] { 292, 308 });
+ testFMeasure(db, result, 0.900947932);
+ testClusterSizes(result, new int[] { 15, 35, 200, 350 });
}
/**
@@ -83,15 +83,15 @@ public class TestPROCLUSResults extends AbstractSimpleAlgorithmTest implements J
// Setup algorithm
ListParameterization params = new ListParameterization();
- params.addParameter(PROCLUS.L_ID, 2);
- params.addParameter(PROCLUS.K_ID, 3);
- params.addParameter(PROCLUS.SEED_ID, 4);
+ params.addParameter(PROCLUS.Parameterizer.L_ID, 2);
+ params.addParameter(PROCLUS.Parameterizer.K_ID, 3);
+ params.addParameter(PROCLUS.Parameterizer.SEED_ID, 0);
PROCLUS<DoubleVector> proclus = ClassGenericsUtil.parameterizeOrAbort(PROCLUS.class, params);
testParameterizationOk(params);
// run PROCLUS on database
Clustering<?> result = proclus.run(db);
- testFMeasure(db, result, 0.6329819);
- testClusterSizes(result, new int[] { 282, 568 });
+ testFMeasure(db, result, 0.739931511);
+ testClusterSizes(result, new int[] { 146, 259, 445 });
}
} \ No newline at end of file
diff --git a/test/de/lmu/ifi/dbs/elki/algorithm/clustering/subspace/TestPreDeConResults.java b/test/de/lmu/ifi/dbs/elki/algorithm/clustering/subspace/TestPreDeConResults.java
index 4d2bf7d0..a07c71a1 100644
--- a/test/de/lmu/ifi/dbs/elki/algorithm/clustering/subspace/TestPreDeConResults.java
+++ b/test/de/lmu/ifi/dbs/elki/algorithm/clustering/subspace/TestPreDeConResults.java
@@ -60,8 +60,6 @@ public class TestPreDeConResults extends AbstractSimpleAlgorithmTest implements
ListParameterization inp = new ListParameterization();
inp.addParameter(ClassLabelFilter.CLASS_LABEL_INDEX_ID, 1);
Class<?>[] filters = new Class<?>[] { ClassLabelFilter.class };
- // FIXME: makeSimpleDatabase currently does also add FILTERS, this doesn't
- // work.
Database db = makeSimpleDatabase(UNITTEST + "axis-parallel-subspace-clusters-6d.csv.gz", 2500, inp, filters);
ListParameterization params = new ListParameterization();
@@ -108,4 +106,4 @@ public class TestPreDeConResults extends AbstractSimpleAlgorithmTest implements
testFMeasure(db, result, 0.6470817);
testClusterSizes(result, new int[] { 7, 10, 10, 13, 15, 16, 16, 18, 28, 131, 586 });
}
-} \ No newline at end of file
+}
diff --git a/test/de/lmu/ifi/dbs/elki/algorithm/outlier/TestAggarwalYuEvolutionary.java b/test/de/lmu/ifi/dbs/elki/algorithm/outlier/TestAggarwalYuEvolutionary.java
index 1eb0c713..cabd315a 100644
--- a/test/de/lmu/ifi/dbs/elki/algorithm/outlier/TestAggarwalYuEvolutionary.java
+++ b/test/de/lmu/ifi/dbs/elki/algorithm/outlier/TestAggarwalYuEvolutionary.java
@@ -45,10 +45,10 @@ public class TestAggarwalYuEvolutionary extends AbstractSimpleAlgorithmTest impl
// Parameterization
ListParameterization params = new ListParameterization();
- params.addParameter(AggarwalYuEvolutionary.K_ID, 2);
- params.addParameter(AggarwalYuEvolutionary.PHI_ID, 8);
- params.addParameter(AggarwalYuEvolutionary.M_ID, 5);
- params.addParameter(AggarwalYuEvolutionary.SEED_ID, 0);
+ params.addParameter(AggarwalYuEvolutionary.Parameterizer.K_ID, 2);
+ params.addParameter(AggarwalYuEvolutionary.Parameterizer.PHI_ID, 8);
+ params.addParameter(AggarwalYuEvolutionary.Parameterizer.M_ID, 5);
+ params.addParameter(AggarwalYuEvolutionary.Parameterizer.SEED_ID, 0);
// setup Algorithm
AggarwalYuEvolutionary<DoubleVector> aggarwalYuEvolutionary = ClassGenericsUtil.parameterizeOrAbort(AggarwalYuEvolutionary.class, params);
diff --git a/test/de/lmu/ifi/dbs/elki/algorithm/outlier/TestAggarwalYuNaive.java b/test/de/lmu/ifi/dbs/elki/algorithm/outlier/TestAggarwalYuNaive.java
index 937977f2..016fe5e5 100644
--- a/test/de/lmu/ifi/dbs/elki/algorithm/outlier/TestAggarwalYuNaive.java
+++ b/test/de/lmu/ifi/dbs/elki/algorithm/outlier/TestAggarwalYuNaive.java
@@ -45,8 +45,8 @@ public class TestAggarwalYuNaive extends AbstractSimpleAlgorithmTest implements
// Parameterization
ListParameterization params = new ListParameterization();
- params.addParameter(AggarwalYuNaive.K_ID, 2);
- params.addParameter(AggarwalYuNaive.PHI_ID, 8);
+ params.addParameter(AggarwalYuNaive.Parameterizer.K_ID, 2);
+ params.addParameter(AggarwalYuNaive.Parameterizer.PHI_ID, 8);
// setup Algorithm
AggarwalYuNaive<DoubleVector> aggarwalYuNaive = ClassGenericsUtil.parameterizeOrAbort(AggarwalYuNaive.class, params);
diff --git a/test/de/lmu/ifi/dbs/elki/algorithm/outlier/TestOnlineLOF.java b/test/de/lmu/ifi/dbs/elki/algorithm/outlier/TestOnlineLOF.java
index d9b46aea..5b9881e3 100644
--- a/test/de/lmu/ifi/dbs/elki/algorithm/outlier/TestOnlineLOF.java
+++ b/test/de/lmu/ifi/dbs/elki/algorithm/outlier/TestOnlineLOF.java
@@ -33,13 +33,16 @@ import org.junit.Test;
import de.lmu.ifi.dbs.elki.JUnit4Test;
import de.lmu.ifi.dbs.elki.data.DoubleVector;
+import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.data.VectorUtil;
import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
import de.lmu.ifi.dbs.elki.database.HashmapDatabase;
import de.lmu.ifi.dbs.elki.database.UpdatableDatabase;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.datasource.FileBasedDatabaseConnection;
import de.lmu.ifi.dbs.elki.datasource.bundle.MultipleObjectsBundle;
import de.lmu.ifi.dbs.elki.distance.distancefunction.CosineDistanceFunction;
@@ -48,7 +51,6 @@ import de.lmu.ifi.dbs.elki.distance.distancefunction.EuclideanDistanceFunction;
import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
import de.lmu.ifi.dbs.elki.result.outlier.OutlierResult;
import de.lmu.ifi.dbs.elki.utilities.ClassGenericsUtil;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.utilities.exceptions.UnableToComplyException;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.ListParameterization;
@@ -108,7 +110,7 @@ public class TestOnlineLOF implements JUnit4Test {
for(DBIDIter id = scores1.getDBIDs().iter(); id.valid(); id.advance()) {
Double lof1 = scores1.get(id);
Double lof2 = scores2.get(id);
- assertTrue("lof(" + id.getDBID() + ") != lof(" + id.getDBID() + "): " + lof1 + " != " + lof2, lof1.equals(lof2));
+ assertTrue("lof(" + DBIDUtil.toString(id) + ") != lof(" + DBIDUtil.toString(id) + "): " + lof1 + " != " + lof2, lof1.equals(lof2));
}
}
@@ -127,10 +129,11 @@ public class TestOnlineLOF implements JUnit4Test {
// insert new objects
ArrayList<DoubleVector> insertions = new ArrayList<DoubleVector>();
- DoubleVector o = DatabaseUtil.assumeVectorField(rep).getFactory();
+ NumberVector.Factory<DoubleVector, ?> o = RelationUtil.getNumberVectorFactory(rep);
+ int dim = RelationUtil.dimensionality(rep);
Random random = new Random(seed);
for(int i = 0; i < size; i++) {
- DoubleVector obj = VectorUtil.randomVector(o, random);
+ DoubleVector obj = VectorUtil.randomVector(o, dim, random);
insertions.add(obj);
}
DBIDs deletions = db.insert(MultipleObjectsBundle.makeSimple(rep.getDataTypeInformation(), insertions));
diff --git a/test/de/lmu/ifi/dbs/elki/database/TestRelationSorting.java b/test/de/lmu/ifi/dbs/elki/database/TestRelationSorting.java
new file mode 100644
index 00000000..01ec174c
--- /dev/null
+++ b/test/de/lmu/ifi/dbs/elki/database/TestRelationSorting.java
@@ -0,0 +1,91 @@
+package de.lmu.ifi.dbs.elki.database;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+
+import de.lmu.ifi.dbs.elki.JUnit4Test;
+import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.data.VectorUtil;
+import de.lmu.ifi.dbs.elki.data.VectorUtil.SortDBIDsBySingleDimension;
+import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
+import de.lmu.ifi.dbs.elki.database.ids.ArrayModifiableDBIDs;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDArrayIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
+import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
+import de.lmu.ifi.dbs.elki.datasource.FileBasedDatabaseConnection;
+import de.lmu.ifi.dbs.elki.utilities.ClassGenericsUtil;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.ListParameterization;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * Unit test that loads a data file and sorts it. This tests some key parts of
+ * the database and ID layers.
+ *
+ * @author Erich Schubert
+ *
+ */
+public class TestRelationSorting implements JUnit4Test {
+ public static final String filename = "data/testdata/unittests/hierarchical-3d2d1d.csv";
+
+ @Test
+ public void testSorting() {
+ ListParameterization params = new ListParameterization();
+ params.addParameter(FileBasedDatabaseConnection.INPUT_ID, filename);
+ Database db = ClassGenericsUtil.parameterizeOrAbort(StaticArrayDatabase.class, params);
+ if (params.hasUnusedParameters()) {
+ fail("Unused parameters: " + params.getRemainingParameters());
+ }
+ if (params.hasErrors()) {
+ params.logAndClearReportedErrors();
+ fail("Parameterization errors.");
+ }
+ db.initialize();
+ Relation<? extends NumberVector<?>> rel = db.getRelation(TypeUtil.NUMBER_VECTOR_FIELD);
+
+ ArrayModifiableDBIDs ids = DBIDUtil.newArray(rel.getDBIDs());
+ final int size = rel.size();
+
+ int dims = RelationUtil.dimensionality(rel);
+ SortDBIDsBySingleDimension sorter = new VectorUtil.SortDBIDsBySingleDimension(rel);
+
+ for (int d = 0; d < dims; d++) {
+ sorter.setDimension(d);
+ ids.sort(sorter);
+ assertEquals("Lost some DBID during sorting?!?", size, DBIDUtil.newHashSet(ids).size());
+
+ DBIDArrayIter it = ids.iter();
+ double prev = rel.get(it).doubleValue(d);
+ for (it.advance(); it.valid(); it.advance()) {
+ double next = rel.get(it).doubleValue(d);
+ assertTrue("Not correctly sorted: " + prev + " > " + next + " at pos " + it.getOffset(), prev <= next);
+ prev = next;
+ }
+ }
+ }
+}
diff --git a/test/de/lmu/ifi/dbs/elki/distance/distancefunction/SpatialPrimitiveDistanceFunctionTest.java b/test/de/lmu/ifi/dbs/elki/distance/distancefunction/SpatialPrimitiveDistanceFunctionTest.java
index 1c459717..7309cbbf 100644
--- a/test/de/lmu/ifi/dbs/elki/distance/distancefunction/SpatialPrimitiveDistanceFunctionTest.java
+++ b/test/de/lmu/ifi/dbs/elki/distance/distancefunction/SpatialPrimitiveDistanceFunctionTest.java
@@ -53,7 +53,7 @@ public class SpatialPrimitiveDistanceFunctionTest implements JUnit4Test {
final int dim = 7;
final int iters = 10000;
- List<SpatialPrimitiveDistanceFunction<? super NumberVector<?, ?>, ?>> dists = new ArrayList<SpatialPrimitiveDistanceFunction<? super NumberVector<?, ?>, ?>>();
+ List<SpatialPrimitiveDistanceFunction<? super NumberVector<?>, ?>> dists = new ArrayList<SpatialPrimitiveDistanceFunction<? super NumberVector<?>, ?>>();
dists.add(EuclideanDistanceFunction.STATIC);
dists.add(ManhattanDistanceFunction.STATIC);
dists.add(MaximumDistanceFunction.STATIC);
@@ -87,7 +87,7 @@ public class SpatialPrimitiveDistanceFunctionTest implements JUnit4Test {
double m = rnd.nextDouble();
d4[d] = m * d2[d] + (1 - m) * d3[d];
}
- for(SpatialPrimitiveDistanceFunction<? super NumberVector<?, ?>, ?> dis : dists) {
+ for(SpatialPrimitiveDistanceFunction<? super NumberVector<?>, ?> dis : dists) {
compareDistances(v1, mbr, v2, dis);
}
}
@@ -99,7 +99,7 @@ public class SpatialPrimitiveDistanceFunctionTest implements JUnit4Test {
final int dim = 7;
final int iters = 10000;
- List<SpatialPrimitiveDistanceFunction<? super NumberVector<?, ?>, ?>> dists = new ArrayList<SpatialPrimitiveDistanceFunction<? super NumberVector<?, ?>, ?>>();
+ List<SpatialPrimitiveDistanceFunction<? super NumberVector<?>, ?>> dists = new ArrayList<SpatialPrimitiveDistanceFunction<? super NumberVector<?>, ?>>();
dists.add(EuclideanDistanceFunction.STATIC);
dists.add(ManhattanDistanceFunction.STATIC);
dists.add(MaximumDistanceFunction.STATIC);
@@ -132,13 +132,13 @@ public class SpatialPrimitiveDistanceFunctionTest implements JUnit4Test {
double m = rnd.nextDouble();
d4[d] = m * d2[d] + (1 - m) * d3[d];
}
- for(SpatialPrimitiveDistanceFunction<? super NumberVector<?, ?>, ?> dis : dists) {
+ for(SpatialPrimitiveDistanceFunction<? super NumberVector<?>, ?> dis : dists) {
compareDistances(v1, mbr, v2, dis);
}
}
}
- protected <D extends Distance<D>> void compareDistances(Vector v1, ModifiableHyperBoundingBox mbr, Vector v2, SpatialPrimitiveDistanceFunction<? super NumberVector<?, ?>, D> dist) {
+ protected <D extends Distance<D>> void compareDistances(Vector v1, ModifiableHyperBoundingBox mbr, Vector v2, SpatialPrimitiveDistanceFunction<? super NumberVector<?>, D> dist) {
D exact = dist.distance(v1, v2);
D mind = dist.minDist(v1, v2);
D mbrd = dist.minDist(v1, mbr);
diff --git a/test/de/lmu/ifi/dbs/elki/index/TestIndexStructures.java b/test/de/lmu/ifi/dbs/elki/index/TestIndexStructures.java
index e0b3a479..0b0b12d5 100644
--- a/test/de/lmu/ifi/dbs/elki/index/TestIndexStructures.java
+++ b/test/de/lmu/ifi/dbs/elki/index/TestIndexStructures.java
@@ -26,8 +26,6 @@ package de.lmu.ifi.dbs.elki.index;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;
-import java.util.List;
-
import org.junit.Test;
import de.lmu.ifi.dbs.elki.JUnit4Test;
@@ -35,22 +33,23 @@ import de.lmu.ifi.dbs.elki.data.DoubleVector;
import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
import de.lmu.ifi.dbs.elki.database.Database;
import de.lmu.ifi.dbs.elki.database.StaticArrayDatabase;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.database.query.knn.KNNQuery;
-import de.lmu.ifi.dbs.elki.database.query.knn.KNNResult;
import de.lmu.ifi.dbs.elki.database.query.knn.LinearScanKNNQuery;
import de.lmu.ifi.dbs.elki.database.query.range.LinearScanRangeQuery;
import de.lmu.ifi.dbs.elki.database.query.range.RangeQuery;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.datasource.FileBasedDatabaseConnection;
import de.lmu.ifi.dbs.elki.distance.distancefunction.EuclideanDistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResultIter;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
import de.lmu.ifi.dbs.elki.index.tree.TreeIndexFactory;
import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mtree.MTree;
import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mtree.MTreeFactory;
-import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.query.MetricalIndexKNNQuery;
-import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.query.MetricalIndexRangeQuery;
+import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.query.DoubleDistanceMetricalIndexKNNQuery;
+import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.query.DoubleDistanceMetricalIndexRangeQuery;
import de.lmu.ifi.dbs.elki.index.tree.spatial.rstarvariants.AbstractRStarTreeFactory;
import de.lmu.ifi.dbs.elki.index.tree.spatial.rstarvariants.query.DoubleDistanceRStarTreeKNNQuery;
import de.lmu.ifi.dbs.elki.index.tree.spatial.rstarvariants.query.DoubleDistanceRStarTreeRangeQuery;
@@ -96,8 +95,6 @@ public class TestIndexStructures implements JUnit4Test {
/**
* Test exact query, also to validate the test is correct.
- *
- * @throws ParameterException on errors.
*/
@Test
public void testExact() {
@@ -107,21 +104,17 @@ public class TestIndexStructures implements JUnit4Test {
/**
* Test {@link MTree} using a file based database connection.
- *
- * @throws ParameterException on errors.
*/
@Test
public void testMetrical() {
ListParameterization metparams = new ListParameterization();
metparams.addParameter(StaticArrayDatabase.INDEX_ID, MTreeFactory.class);
metparams.addParameter(TreeIndexFactory.PAGE_SIZE_ID, 100);
- testFileBasedDatabaseConnection(metparams, MetricalIndexKNNQuery.class, MetricalIndexRangeQuery.class);
+ testFileBasedDatabaseConnection(metparams, DoubleDistanceMetricalIndexKNNQuery.class, DoubleDistanceMetricalIndexRangeQuery.class);
}
/**
* Test {@link RStarTree} using a file based database connection.
- *
- * @throws ParameterException on errors.
*/
@Test
public void testRStarTree() {
@@ -133,8 +126,6 @@ public class TestIndexStructures implements JUnit4Test {
/**
* Test {@link VAFile} using a file based database connection.
- *
- * @throws ParameterException on errors.
*/
@Test
public void testVAFile() {
@@ -143,11 +134,9 @@ public class TestIndexStructures implements JUnit4Test {
spatparams.addParameter(VAFile.Factory.PARTITIONS_ID, 4);
testFileBasedDatabaseConnection(spatparams, VAFile.VAFileKNNQuery.class, VAFile.VAFileRangeQuery.class);
}
-
+
/**
* Test {@link PartialVAFile} using a file based database connection.
- *
- * @throws ParameterException on errors.
*/
@Test
public void testPartialVAFile() {
@@ -156,13 +145,11 @@ public class TestIndexStructures implements JUnit4Test {
spatparams.addParameter(PartialVAFile.Factory.PARTITIONS_ID, 4);
testFileBasedDatabaseConnection(spatparams, PartialVAFile.PartialVAFileKNNQuery.class, PartialVAFile.PartialVAFileRangeQuery.class);
}
-
+
/**
* Test {@link RStarTree} using a file based database connection. With "fast"
* mode enabled on an extreme level (since this should only reduce
- * performance, not accuracy!)
- *
- * @throws ParameterException on errors.
+ * performance, not correctness!)
*/
@Test
public void testRStarTreeFast() {
@@ -176,8 +163,6 @@ public class TestIndexStructures implements JUnit4Test {
/**
* Test {@link XTree} using a file based database connection.
- *
- * @throws ParameterException
*/
// @Test
// public void testXTree() {
@@ -194,7 +179,6 @@ public class TestIndexStructures implements JUnit4Test {
* Actual test routine.
*
* @param inputparams
- * @throws ParameterException
*/
void testFileBasedDatabaseConnection(ListParameterization inputparams, Class<?> expectKNNQuery, Class<?> expectRangeQuery) {
inputparams.addParameter(FileBasedDatabaseConnection.INPUT_ID, dataset);
@@ -218,15 +202,13 @@ public class TestIndexStructures implements JUnit4Test {
// verify that the neighbors match.
int i = 0;
- for(DistanceResultPair<DoubleDistance> res : ids) {
+ for(DistanceDBIDResultIter<DoubleDistance> res = ids.iter(); res.valid(); res.advance(), i++) {
// Verify distance
assertEquals("Expected distance doesn't match.", shouldd[i], res.getDistance().doubleValue());
// verify vector
DoubleVector c = rep.get(res);
DoubleVector c2 = new DoubleVector(shouldc[i]);
assertEquals("Expected vector doesn't match: " + c.toString(), 0.0, dist.distance(c, c2).doubleValue(), 0.00001);
-
- i++;
}
}
{
@@ -234,20 +216,18 @@ public class TestIndexStructures implements JUnit4Test {
DoubleVector dv = new DoubleVector(querypoint);
RangeQuery<DoubleVector, DoubleDistance> rangeq = db.getRangeQuery(dist, eps);
assertTrue("Returned range query is not of expected class.", expectRangeQuery.isAssignableFrom(rangeq.getClass()));
- List<DistanceResultPair<DoubleDistance>> ids = rangeq.getRangeForObject(dv, eps);
+ DistanceDBIDResult<DoubleDistance> ids = rangeq.getRangeForObject(dv, eps);
assertEquals("Result size does not match expectation!", shouldd.length, ids.size());
// verify that the neighbors match.
int i = 0;
- for(DistanceResultPair<DoubleDistance> res : ids) {
+ for(DistanceDBIDResultIter<DoubleDistance> res = ids.iter(); res.valid(); res.advance(), i++) {
// Verify distance
assertEquals("Expected distance doesn't match.", shouldd[i], res.getDistance().doubleValue());
// verify vector
DoubleVector c = rep.get(res);
DoubleVector c2 = new DoubleVector(shouldc[i]);
assertEquals("Expected vector doesn't match: " + c.toString(), 0.0, dist.distance(c, c2).doubleValue(), 0.00001);
-
- i++;
}
}
}
diff --git a/test/de/lmu/ifi/dbs/elki/index/preprocessed/TestMaterializedKNNAndRKNNPreprocessor.java b/test/de/lmu/ifi/dbs/elki/index/preprocessed/TestMaterializedKNNAndRKNNPreprocessor.java
index 0ef07a69..3d6e1ba7 100644
--- a/test/de/lmu/ifi/dbs/elki/index/preprocessed/TestMaterializedKNNAndRKNNPreprocessor.java
+++ b/test/de/lmu/ifi/dbs/elki/index/preprocessed/TestMaterializedKNNAndRKNNPreprocessor.java
@@ -34,6 +34,7 @@ import org.junit.Test;
import de.lmu.ifi.dbs.elki.JUnit4Test;
import de.lmu.ifi.dbs.elki.data.DoubleVector;
+import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.data.VectorUtil;
import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
import de.lmu.ifi.dbs.elki.database.HashmapDatabase;
@@ -41,22 +42,23 @@ import de.lmu.ifi.dbs.elki.database.UpdatableDatabase;
import de.lmu.ifi.dbs.elki.database.ids.ArrayDBIDs;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
-import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair;
import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery;
import de.lmu.ifi.dbs.elki.database.query.knn.KNNQuery;
-import de.lmu.ifi.dbs.elki.database.query.knn.KNNResult;
import de.lmu.ifi.dbs.elki.database.query.knn.LinearScanKNNQuery;
import de.lmu.ifi.dbs.elki.database.query.rknn.LinearScanRKNNQuery;
import de.lmu.ifi.dbs.elki.database.query.rknn.RKNNQuery;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.datasource.FileBasedDatabaseConnection;
import de.lmu.ifi.dbs.elki.datasource.bundle.MultipleObjectsBundle;
import de.lmu.ifi.dbs.elki.distance.distancefunction.EuclideanDistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResultIter;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
import de.lmu.ifi.dbs.elki.index.preprocessed.knn.MaterializeKNNAndRKNNPreprocessor;
import de.lmu.ifi.dbs.elki.index.preprocessed.knn.MaterializeKNNPreprocessor;
import de.lmu.ifi.dbs.elki.utilities.ClassGenericsUtil;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.utilities.exceptions.UnableToComplyException;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.ParameterException;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.ListParameterization;
@@ -131,10 +133,11 @@ public class TestMaterializedKNNAndRKNNPreprocessor implements JUnit4Test {
// insert new objects
List<DoubleVector> insertions = new ArrayList<DoubleVector>();
- DoubleVector o = DatabaseUtil.assumeVectorField(rep).getFactory();
+ NumberVector.Factory<DoubleVector, ?> o = RelationUtil.getNumberVectorFactory(rep);
+ int dim = RelationUtil.dimensionality(rep);
Random random = new Random(seed);
for(int i = 0; i < updatesize; i++) {
- DoubleVector obj = VectorUtil.randomVector(o, random);
+ DoubleVector obj = VectorUtil.randomVector(o, dim, random);
insertions.add(obj);
}
System.out.println("Insert " + insertions);
@@ -156,18 +159,24 @@ public class TestMaterializedKNNAndRKNNPreprocessor implements JUnit4Test {
private void testKNNQueries(Relation<DoubleVector> rep, KNNQuery<DoubleVector, DoubleDistance> lin_knn_query, KNNQuery<DoubleVector, DoubleDistance> preproc_knn_query, int k) {
ArrayDBIDs sample = DBIDUtil.ensureArray(rep.getDBIDs());
- List<KNNResult<DoubleDistance>> lin_knn_ids = lin_knn_query.getKNNForBulkDBIDs(sample, k);
- List<KNNResult<DoubleDistance>> preproc_knn_ids = preproc_knn_query.getKNNForBulkDBIDs(sample, k);
+ List<? extends KNNResult<DoubleDistance>> lin_knn_ids = lin_knn_query.getKNNForBulkDBIDs(sample, k);
+ List<? extends KNNResult<DoubleDistance>> preproc_knn_ids = preproc_knn_query.getKNNForBulkDBIDs(sample, k);
for(int i = 0; i < rep.size(); i++) {
KNNResult<DoubleDistance> lin_knn = lin_knn_ids.get(i);
KNNResult<DoubleDistance> pre_knn = preproc_knn_ids.get(i);
- if(!lin_knn.equals(pre_knn)) {
- System.out.println("LIN kNN " + lin_knn);
- System.out.println("PRE kNN " + pre_knn);
+ DistanceDBIDResultIter<DoubleDistance> lin = lin_knn.iter(), pre = pre_knn.iter();
+ for(; lin.valid() && pre.valid(); lin.advance(), pre.advance(), i++) {
+ if(!DBIDUtil.equal(lin, pre) && lin.getDistance().compareTo(pre.getDistance()) != 0) {
+ System.out.print("LIN kNN #" + i + " " + lin.getDistancePair());
+ System.out.print(" <=> ");
+ System.out.print("PRE kNN #" + i + " " + pre.getDistancePair());
+ System.out.println();
+ break;
+ }
}
assertEquals("kNN sizes do not agree.", lin_knn.size(), pre_knn.size());
for(int j = 0; j < lin_knn.size(); j++) {
- assertTrue("kNNs of linear scan and preprocessor do not match!", lin_knn.get(j).sameDBID(pre_knn.get(j)));
+ assertTrue("kNNs of linear scan and preprocessor do not match!", DBIDUtil.equal(lin_knn.get(j), pre_knn.get(j)));
assertTrue("kNNs of linear scan and preprocessor do not match!", lin_knn.get(j).getDistance().equals(pre_knn.get(j).getDistance()));
}
}
@@ -176,19 +185,25 @@ public class TestMaterializedKNNAndRKNNPreprocessor implements JUnit4Test {
private void testRKNNQueries(Relation<DoubleVector> rep, RKNNQuery<DoubleVector, DoubleDistance> lin_rknn_query, RKNNQuery<DoubleVector, DoubleDistance> preproc_rknn_query, int k) {
ArrayDBIDs sample = DBIDUtil.ensureArray(rep.getDBIDs());
- List<List<DistanceResultPair<DoubleDistance>>> lin_rknn_ids = lin_rknn_query.getRKNNForBulkDBIDs(sample, k);
- List<List<DistanceResultPair<DoubleDistance>>> preproc_rknn_ids = preproc_rknn_query.getRKNNForBulkDBIDs(sample, k);
+ List<? extends DistanceDBIDResult<DoubleDistance>> lin_rknn_ids = lin_rknn_query.getRKNNForBulkDBIDs(sample, k);
+ List<? extends DistanceDBIDResult<DoubleDistance>> preproc_rknn_ids = preproc_rknn_query.getRKNNForBulkDBIDs(sample, k);
for(int i = 0; i < rep.size(); i++) {
- List<DistanceResultPair<DoubleDistance>> lin_rknn = lin_rknn_ids.get(i);
- List<DistanceResultPair<DoubleDistance>> pre_rknn = preproc_rknn_ids.get(i);
- if(!lin_rknn.equals(pre_rknn)) {
- System.out.println("LIN RkNN " + lin_rknn);
- System.out.println("PRE RkNN " + pre_rknn);
- System.out.println();
+ DistanceDBIDResult<DoubleDistance> lin_rknn = lin_rknn_ids.get(i);
+ DistanceDBIDResult<DoubleDistance> pre_rknn = preproc_rknn_ids.get(i);
+
+ DistanceDBIDResultIter<DoubleDistance> lin = lin_rknn.iter(), pre = pre_rknn.iter();
+ for(; lin.valid() && pre.valid(); lin.advance(), pre.advance(), i++) {
+ if(!DBIDUtil.equal(lin, pre) || lin.getDistance().compareTo(pre.getDistance()) != 0) {
+ System.out.print("LIN RkNN #" + i + " " + lin);
+ System.out.print(" <=> ");
+ System.out.print("PRE RkNN #" + i + " " + pre);
+ System.out.println();
+ break;
+ }
}
assertEquals("rkNN sizes do not agree for k=" + k, lin_rknn.size(), pre_rknn.size());
for(int j = 0; j < lin_rknn.size(); j++) {
- assertTrue("rkNNs of linear scan and preprocessor do not match!", lin_rknn.get(j).sameDBID(pre_rknn.get(j)));
+ assertTrue("rkNNs of linear scan and preprocessor do not match!", DBIDUtil.equal(lin_rknn.get(j), pre_rknn.get(j)));
assertTrue("rkNNs of linear scan and preprocessor do not match!", lin_rknn.get(j).getDistance().equals(pre_rknn.get(j).getDistance()));
}
}
diff --git a/test/de/lmu/ifi/dbs/elki/math/TestKernelDensityFitting.java b/test/de/lmu/ifi/dbs/elki/math/TestKernelDensityFitting.java
index 56d4549d..86058abe 100644
--- a/test/de/lmu/ifi/dbs/elki/math/TestKernelDensityFitting.java
+++ b/test/de/lmu/ifi/dbs/elki/math/TestKernelDensityFitting.java
@@ -34,7 +34,6 @@ import de.lmu.ifi.dbs.elki.data.DoubleVector;
import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
import de.lmu.ifi.dbs.elki.database.Database;
import de.lmu.ifi.dbs.elki.database.StaticArrayDatabase;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.datasource.FileBasedDatabaseConnection;
@@ -94,8 +93,7 @@ public class TestKernelDensityFitting implements JUnit4Test {
{
int i = 0;
for(DBIDIter iditer = rep.iterDBIDs(); iditer.valid(); iditer.advance()) {
- DBID id = iditer.getDBID();
- fulldata[i] = rep.get(id).doubleValue(1);
+ fulldata[i] = rep.get(iditer).doubleValue(0);
i++;
}
}
diff --git a/test/de/lmu/ifi/dbs/elki/math/TestMathUtil.java b/test/de/lmu/ifi/dbs/elki/math/TestMathUtil.java
index d8211741..58441623 100644
--- a/test/de/lmu/ifi/dbs/elki/math/TestMathUtil.java
+++ b/test/de/lmu/ifi/dbs/elki/math/TestMathUtil.java
@@ -44,7 +44,7 @@ public class TestMathUtil implements JUnit4Test {
double[] weight2 = new double[size];
Random r = new Random(seed);
- for(int i = 0; i < size; i++) {
+ for (int i = 0; i < size; i++) {
data1[i] = r.nextDouble();
data2[i] = r.nextDouble();
weight1[i] = 1.0;
@@ -84,7 +84,7 @@ public class TestMathUtil implements JUnit4Test {
@Test
public void testFloatToDouble() {
Random r = new Random(1l);
- for(int i = 0; i < 10000; i++) {
+ for (int i = 0; i < 10000; i++) {
final double dbl = Double.longBitsToDouble(r.nextLong());
final float flt = (float) dbl;
final double uppd = MathUtil.floatToDoubleUpper(flt);
@@ -97,4 +97,4 @@ public class TestMathUtil implements JUnit4Test {
assertTrue("Expected value to round to the same float.", flt == lowf || Double.isNaN(flt));
}
}
-} \ No newline at end of file
+}
diff --git a/test/de/lmu/ifi/dbs/elki/math/TestSinCosTable.java b/test/de/lmu/ifi/dbs/elki/math/TestSinCosTable.java
new file mode 100644
index 00000000..8e91a404
--- /dev/null
+++ b/test/de/lmu/ifi/dbs/elki/math/TestSinCosTable.java
@@ -0,0 +1,50 @@
+package de.lmu.ifi.dbs.elki.math;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+import de.lmu.ifi.dbs.elki.JUnit4Test;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+public class TestSinCosTable implements JUnit4Test {
+
+ @Test
+ public void testSinCosTable() {
+ doTestSinCosTable(360);
+ doTestSinCosTable(142); // Divisible by two
+ doTestSinCosTable(17);
+ doTestSinCosTable(131); // Prime.
+ }
+
+ protected void doTestSinCosTable(int steps) {
+ SinCosTable table = SinCosTable.make(steps);
+ for (int i = -steps; i < 2 * steps; i++) {
+ double angle = Math.toRadians(360. * i / steps);
+ assertEquals("Cosine does not match at i=" + i + " a=" + angle, Math.cos(angle), table.cos(i), 1E-10);
+ assertEquals("Sine does not match at i=" + i + " a=" + angle, Math.sin(angle), table.sin(i), 1E-10);
+ }
+ }
+}
diff --git a/test/de/lmu/ifi/dbs/elki/math/geometry/TestPrimsMinimumSpanningTree.java b/test/de/lmu/ifi/dbs/elki/math/geometry/TestPrimsMinimumSpanningTree.java
new file mode 100644
index 00000000..c8252a26
--- /dev/null
+++ b/test/de/lmu/ifi/dbs/elki/math/geometry/TestPrimsMinimumSpanningTree.java
@@ -0,0 +1,76 @@
+package de.lmu.ifi.dbs.elki.math.geometry;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import de.lmu.ifi.dbs.elki.JUnit4Test;
+
+/**
+ * Test class for Prim's minmum spanning tree algorithm.
+ *
+ * @author Erich Schubert
+ */
+public class TestPrimsMinimumSpanningTree implements JUnit4Test {
+ @Test
+ public void testSimple() {
+ // A simple test.
+ final double inf = Double.POSITIVE_INFINITY;
+ double[][] mat = new double[][] {//
+ { 0.0, 7.0, inf, 5.0, inf, inf, inf }, //
+ { 7.0, 0.0, 8.0, 9.0, 7.0, inf, inf }, //
+ { inf, 8.0, 0.0, inf, 5.0, inf, inf }, //
+ { 5.0, 9.0, inf, 0.0, 15., 6.0, inf }, //
+ { inf, 7.0, 5.0, 15., 0.0, 8.0, 9.0 }, //
+ { inf, inf, inf, 6.0, 8.0, 0.0, 11. }, //
+ { inf, inf, inf, inf, 9.0, 11., 0.0 }, //
+ };
+ int[] ret = PrimsMinimumSpanningTree.processDense(mat);
+ // "correct" edges (ignore order and direction!)
+ int[] correct = new int[] { 0, 1, 0, 3, 1, 4, 2, 4, 4, 6, 3, 5 };
+ assertEquals("Graph size does not match expected size.", correct.length, ret.length);
+
+ // Flags so we find edges only once.
+ int[] flags = new int[correct.length];
+ for (int i = 0; i < ret.length; i += 2) {
+ boolean found = false;
+ for (int j = 0; j < correct.length; j += 2) {
+ if (flags[j] == 1) {
+ continue;
+ }
+ if ((correct[j] == ret[i] && correct[j + 1] == ret[i + 1]) || (correct[j] == ret[i + 1] && correct[j + 1] == ret[i])) {
+ found = true;
+ flags[j] = 1;
+ break;
+ }
+ }
+ assertTrue("Edge not found: " + (char) ('A' + ret[i]) + " -> " + (char) ('A' + ret[i + 1]), found);
+ }
+ }
+ // We could also check that every even flag is set. But as we checked the
+ // length and found all edges, all must have been used...
+}
diff --git a/test/de/lmu/ifi/dbs/elki/math/histograms/TestFlexiHistogram.java b/test/de/lmu/ifi/dbs/elki/math/histograms/TestFlexiHistogram.java
deleted file mode 100644
index 7eafa270..00000000
--- a/test/de/lmu/ifi/dbs/elki/math/histograms/TestFlexiHistogram.java
+++ /dev/null
@@ -1,90 +0,0 @@
-package de.lmu.ifi.dbs.elki.math.histograms;
-
-/*
- This file is part of ELKI:
- Environment for Developing KDD-Applications Supported by Index-Structures
-
- Copyright (C) 2012
- Ludwig-Maximilians-Universität München
- Lehr- und Forschungseinheit für Datenbanksysteme
- ELKI Development Team
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
-
-import java.util.Iterator;
-
-import org.junit.Test;
-
-import de.lmu.ifi.dbs.elki.JUnit4Test;
-import de.lmu.ifi.dbs.elki.utilities.pairs.DoubleObjPair;
-
-/**
- * JUnit test to test the {@link ReplacingHistogram} class.
- *
- * @author Erich Schubert
- */
-public class TestFlexiHistogram implements JUnit4Test {
- FlexiHistogram<Double, Double> hist;
-
- /**
- * Test that adds some data to the histogram and compares results.
- */
- @Test
- public final void testHistogram() {
- Double[] filled = { 0.0, 1.23, 4.56, 7.89, 0.0 };
- Double[] changed = { 0.0, 1.35, 8.01, 14.67, 9.01, 2.34 };
- Double[] resized = { -1.23, 1.35, 22.68, 11.35, 0.0, 0.0, -4.56 };
- Double[] expanded = { 1., 0.0, 0.0, 0.0, 0.0, 0.0, 29.59 };
- hist = FlexiHistogram.DoubleSumHistogram(5);
- hist.aggregate(0.0, 0.0);
- hist.aggregate(0.15, 1.23);
- hist.aggregate(0.25, 4.56);
- hist.aggregate(0.35, 7.89);
- hist.aggregate(0.5, 0.0);
- assertArrayEquals("Filled histogram doesn't match", filled, hist.getData().toArray(new Double[0]));
- hist.aggregate(0.15, 0.12);
- hist.aggregate(0.25, 3.45);
- hist.aggregate(0.35, 6.78);
- hist.aggregate(0.45, 9.01);
- hist.aggregate(0.55, 2.34);
- assertArrayEquals("Changed histogram doesn't match", changed, hist.getData().toArray(new Double[0]));
- hist.aggregate(-.13, -1.23);
- hist.aggregate(1.13, -4.56);
- assertArrayEquals("Resized histogram doesn't match", resized, hist.getData().toArray(new Double[0]));
-
- // compare results via Iterator.
- int off = 0;
- for(DoubleObjPair<Double> pair : hist) {
- assertEquals("Array iterator bin position", -0.1 + 0.2 * off, pair.first, 0.00001);
- assertEquals("Array iterator bin contents", resized[off], pair.getSecond(), 0.00001);
- off++;
- }
- // backwards...
- off--;
- for(Iterator<DoubleObjPair<Double>> iter = hist.reverseIterator(); iter.hasNext(); ) {
- DoubleObjPair<Double> pair = iter.next();
- assertEquals("Array iterator bin position", -0.1 + 0.2 * off, pair.first, 0.00001);
- assertEquals("Array iterator bin contents", resized[off], pair.getSecond(), 0.00001);
- off--;
- }
-
- // totally break out of the data range
- hist.aggregate(-10., 1.);
- assertArrayEquals("Expanded histogram doesn't match", expanded, hist.getData().toArray(new Double[0]));
- }
-}
diff --git a/test/de/lmu/ifi/dbs/elki/math/statistics/distribution/TestExponentialDistribution.java b/test/de/lmu/ifi/dbs/elki/math/statistics/distribution/TestExponentialDistribution.java
new file mode 100644
index 00000000..8aa5a04a
--- /dev/null
+++ b/test/de/lmu/ifi/dbs/elki/math/statistics/distribution/TestExponentialDistribution.java
@@ -0,0 +1,826 @@
+package de.lmu.ifi.dbs.elki.math.statistics.distribution;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+import org.junit.Test;
+
+import de.lmu.ifi.dbs.elki.JUnit4Test;
+
+/**
+ * Unit test for the exponential distribution in ELKI.
+ *
+ * The reference values were computed using GNU R and SciPy.
+ *
+ * @author Erich Schubert
+ */
+public class TestExponentialDistribution extends AbstractDistributionTest implements JUnit4Test {
+ public static final double[] P_CDFPDF = { //
+ 1e-10, 1e-05, 0.1, 0.1234567, 0.2, 0.271828182846, 0.3, 0.314159265359, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 2.71828182846, 3.14159265359 //
+ };
+
+ public static final double[] SCIPY_EXP_CDF_01 = { //
+ 9.99999999995000020434998363876935860601602445996150e-12, // 0.000000
+ 9.99999500000166912315989475867894498151144944131374e-07, // 0.000010
+ 9.95016625083194709844303105228391359560191631317139e-03, // 0.100000
+ 1.22697748626232703422767045253749529365450143814087e-02, // 0.123457
+ 1.98013266932446987955707129458460258319973945617676e-02, // 0.200000
+ 2.68166904430214188270742425856951740570366382598877e-02, // 0.271828
+ 2.95544664514918210829197420252967276610434055328369e-02, // 0.300000
+ 3.09275736951893608084329656549016362987458705902100e-02, // 0.314159
+ 3.92105608476767883430191830029798438772559165954590e-02, // 0.400000
+ 4.87705754992859910612601481716410489752888679504395e-02, // 0.500000
+ 5.82354664157512871835997714242694200947880744934082e-02, // 0.600000
+ 6.76061800940517682034780477806634735316038131713867e-02, // 0.700000
+ 7.68836536133642167722967997178784571588039398193359e-02, // 0.800000
+ 8.60688147287718141598134025116451084613800048828125e-02, // 0.900000
+ 9.51625819640404269073030718573136255145072937011719e-02, // 1.000000
+ 1.04165864703471763852427045549120521172881126403809e-01, // 1.100000
+ 1.13079563282842479599032969872496323660016059875488e-01, // 1.200000
+ 1.21904569079438682144278516261692857369780540466309e-01, // 1.300000
+ 1.30641764601194171335762916896783281117677688598633e-01, // 1.400000
+ 1.39292023574942191999070928432047367095947265625000e-01, // 1.500000
+ 1.47856211033788653708143101539462804794311523437500e-01, // 1.600000
+ 1.56335183403616295727545093541266396641731262207031e-01, // 1.700000
+ 1.64729788588727971143654826846614014357328414916992e-01, // 1.800000
+ 1.73040866056637682168783953784441109746694564819336e-01, // 1.900000
+ 1.81269246922018151257915974383649881929159164428711e-01, // 2.000000
+ 2.38014828033141334628908225568011403083801269531250e-01, // 2.718282
+ 2.69597308951354364126018481329083442687988281250000e-01, // 3.141593
+ };
+
+ public static final double[] SCIPY_EXP_PDF_01 = { //
+ 9.99999999989999999172596290009096264839172363281250e-02, // 0.000000
+ 9.99999000000500043450202269923465792089700698852539e-02, // 0.000010
+ 9.90049833749168106677984724228736013174057006835938e-02, // 0.100000
+ 9.87730225137376793842491906616487540304660797119141e-02, // 0.123457
+ 9.80198673306755252632171959703555330634117126464844e-02, // 0.200000
+ 9.73183309556978626275736132811289280652999877929688e-02, // 0.271828
+ 9.70445533548508210142102825557230971753597259521484e-02, // 0.300000
+ 9.69072426304810607966544466762570664286613464355469e-02, // 0.314159
+ 9.60789439152323204718086913089791778475046157836914e-02, // 0.400000
+ 9.51229424500713988122058140106673818081617355346680e-02, // 0.500000
+ 9.41764533584248664244142901225131936371326446533203e-02, // 0.600000
+ 9.32393819905948245674309760033793281763792037963867e-02, // 0.700000
+ 9.23116346386635699960976353395381011068820953369141e-02, // 0.800000
+ 9.13931185271228130329035366230527870357036590576172e-02, // 0.900000
+ 9.04837418035959462070394465627032332122325897216797e-02, // 1.000000
+ 8.95834135296528222269785146636422723531723022460938e-02, // 1.100000
+ 8.86920436717157506523179222313046921044588088989258e-02, // 1.200000
+ 8.78095430920561303977933675923850387334823608398438e-02, // 1.300000
+ 8.69358235398805800908661467474303208291530609130859e-02, // 1.400000
+ 8.60707976425057752489777840310125611722469329833984e-02, // 1.500000
+ 8.52143788966211318536281282831623684614896774291992e-02, // 1.600000
+ 8.43664816596383648761303675200906582176685333251953e-02, // 1.700000
+ 8.35270211411272001100769557524472475051879882812500e-02, // 1.800000
+ 8.26959133943362262320064814957731869071722030639648e-02, // 1.900000
+ 8.18730753077981793230932794358523096889257431030273e-02, // 2.000000
+ 7.61985171966858693126667390060902107506990432739258e-02, // 2.718282
+ 7.30402691048645663629557134299830067902803421020508e-02, // 3.141593
+ };
+
+ public static final double[] GNUR_EXP_CDF_01 = { //
+ 9.99999999995000020434998363876935860601602445996150e-12, // 0.000000
+ 9.99999500000166912315989475867894498151144944131374e-07, // 0.000010
+ 9.95016625083194709844303105228391359560191631317139e-03, // 0.100000
+ 1.22697748626232703422767045253749529365450143814087e-02, // 0.123457
+ 1.98013266932446987955707129458460258319973945617676e-02, // 0.200000
+ 2.68166904430307134754585263181070331484079360961914e-02, // 0.271828
+ 2.95544664514918210829197420252967276610434055328369e-02, // 0.300000
+ 3.09275736951913661487711948439027764834463596343994e-02, // 0.314159
+ 3.92105608476767883430191830029798438772559165954590e-02, // 0.400000
+ 4.87705754992859910612601481716410489752888679504395e-02, // 0.500000
+ 5.82354664157512871835997714242694200947880744934082e-02, // 0.600000
+ 6.76061800940517682034780477806634735316038131713867e-02, // 0.700000
+ 7.68836536133642167722967997178784571588039398193359e-02, // 0.800000
+ 8.60688147287718141598134025116451084613800048828125e-02, // 0.900000
+ 9.51625819640404269073030718573136255145072937011719e-02, // 1.000000
+ 1.04165864703471763852427045549120521172881126403809e-01, // 1.100000
+ 1.13079563282842479599032969872496323660016059875488e-01, // 1.200000
+ 1.21904569079438682144278516261692857369780540466309e-01, // 1.300000
+ 1.30641764601194171335762916896783281117677688598633e-01, // 1.400000
+ 1.39292023574942191999070928432047367095947265625000e-01, // 1.500000
+ 1.47856211033788653708143101539462804794311523437500e-01, // 1.600000
+ 1.56335183403616295727545093541266396641731262207031e-01, // 1.700000
+ 1.64729788588727971143654826846614014357328414916992e-01, // 1.800000
+ 1.73040866056637682168783953784441109746694564819336e-01, // 1.900000
+ 1.81269246922018151257915974383649881929159164428711e-01, // 2.000000
+ 2.38014828033214109748172404579236172139644622802734e-01, // 2.718282
+ 2.69597308951369518670304614715860225260257720947266e-01, // 3.141593
+ };
+
+ public static final double[] GNUR_EXP_PDF_01 = { //
+ 9.99999999989999999172596290009096264839172363281250e-02, // 0.000000
+ 9.99999000000500043450202269923465792089700698852539e-02, // 0.000010
+ 9.90049833749168106677984724228736013174057006835938e-02, // 0.100000
+ 9.87730225137376793842491906616487540304660797119141e-02, // 0.123457
+ 9.80198673306755252632171959703555330634117126464844e-02, // 0.200000
+ 9.73183309556969328157904897125263232737779617309570e-02, // 0.271828
+ 9.70445533548508210142102825557230971753597259521484e-02, // 0.300000
+ 9.69072426304808665076251372738624922931194305419922e-02, // 0.314159
+ 9.60789439152323204718086913089791778475046157836914e-02, // 0.400000
+ 9.51229424500713988122058140106673818081617355346680e-02, // 0.500000
+ 9.41764533584248664244142901225131936371326446533203e-02, // 0.600000
+ 9.32393819905948245674309760033793281763792037963867e-02, // 0.700000
+ 9.23116346386635699960976353395381011068820953369141e-02, // 0.800000
+ 9.13931185271228130329035366230527870357036590576172e-02, // 0.900000
+ 9.04837418035959462070394465627032332122325897216797e-02, // 1.000000
+ 8.95834135296528222269785146636422723531723022460938e-02, // 1.100000
+ 8.86920436717157506523179222313046921044588088989258e-02, // 1.200000
+ 8.78095430920561303977933675923850387334823608398438e-02, // 1.300000
+ 8.69358235398805800908661467474303208291530609130859e-02, // 1.400000
+ 8.60707976425057752489777840310125611722469329833984e-02, // 1.500000
+ 8.52143788966211318536281282831623684614896774291992e-02, // 1.600000
+ 8.43664816596383648761303675200906582176685333251953e-02, // 1.700000
+ 8.35270211411272001100769557524472475051879882812500e-02, // 1.800000
+ 8.26959133943362262320064814957731869071722030639648e-02, // 1.900000
+ 8.18730753077981793230932794358523096889257431030273e-02, // 2.000000
+ 7.61985171966785973518554442307504359632730484008789e-02, // 2.718282
+ 7.30402691048630536840846616541966795921325683593750e-02, // 3.141593
+ };
+
+ public static final double[] SCIPY_EXP_CDF_05 = { //
+ 4.99999999987500026690715295475821817669515034765482e-11, // 0.000000
+ 4.99998750002083400397704521234132357676571700721979e-06, // 0.000010
+ 4.87705754992859910612601481716410489752888679504395e-02, // 0.100000
+ 5.98617593408454357062353778928809333592653274536133e-02, // 0.123457
+ 9.51625819640404269073030718573136255145072937011719e-02, // 0.200000
+ 1.27082379621731633712045095307985320687294006347656e-01, // 0.271828
+ 1.39292023574942191999070928432047367095947265625000e-01, // 0.300000
+ 1.45364000846766566743539783601590897887945175170898e-01, // 0.314159
+ 1.81269246922018151257915974383649881929159164428711e-01, // 0.400000
+ 2.21199216928595121522960198490181937813758850097656e-01, // 0.500000
+ 2.59181779318282123902861258102348074316978454589844e-01, // 0.600000
+ 2.95311910281286560397973062208620831370353698730469e-01, // 0.700000
+ 3.29679953964360727969307163220946677029132843017578e-01, // 0.800000
+ 3.62371848378226724118889023884548805654048919677734e-01, // 0.900000
+ 3.93469340287366575736882623459678143262863159179688e-01, // 1.000000
+ 4.23050189619513350436363907647319138050079345703125e-01, // 1.100000
+ 4.51188363905973555123551932410919107496738433837891e-01, // 1.200000
+ 4.77954223238983955113212687138002365827560424804688e-01, // 1.300000
+ 5.03414696208590473069932613725541159510612487792969e-01, // 1.400000
+ 5.27633447258985310845957883429946377873420715332031e-01, // 1.500000
+ 5.50671035882778436842954761232249438762664794921875e-01, // 1.600000
+ 5.72585068051273293754377391451271250844001770019531e-01, // 1.700000
+ 5.93430340259400890268182138242991641163825988769531e-01, // 1.800000
+ 6.13258976545498768473407835699617862701416015625000e-01, // 1.900000
+ 6.32120558828557665975722557050175964832305908203125e-01, // 2.000000
+ 7.43118634686529722088721428008284419775009155273438e-01, // 2.718282
+ 7.92120423649238070140654599526897072792053222656250e-01, // 3.141593
+ };
+
+ public static final double[] SCIPY_EXP_PDF_05 = { //
+ 4.99999999974999997931490725022740662097930908203125e-01, // 0.000000
+ 4.99997500006249984139117259474005550146102905273438e-01, // 0.000010
+ 4.75614712250357007938816877867793664336204528808594e-01, // 0.100000
+ 4.70069120329577261330200599331874400377273559570312e-01, // 0.123457
+ 4.52418709017979758790772848442429676651954650878906e-01, // 0.200000
+ 4.36458810189134183143977452346007339656352996826172e-01, // 0.271828
+ 4.30353988212528904000464535783976316452026367187500e-01, // 0.300000
+ 4.27317999576616702750442300384747795760631561279297e-01, // 0.314159
+ 4.09365376538990910493254204993718303740024566650391e-01, // 0.400000
+ 3.89400391535702439238519900754909031093120574951172e-01, // 0.500000
+ 3.70409110340858938048569370948825962841510772705078e-01, // 0.600000
+ 3.52344044859356719801013468895689584314823150634766e-01, // 0.700000
+ 3.35160023017819663770922034018440172076225280761719e-01, // 0.800000
+ 3.18814075810886665696131103686639107763767242431641e-01, // 0.900000
+ 3.03265329856316712131558688270160928368568420410156e-01, // 1.000000
+ 2.88474905190243324781818046176340430974960327148438e-01, // 1.100000
+ 2.74405818047013194682648418165626935660839080810547e-01, // 1.200000
+ 2.61022888380508022443393656430998817086219787597656e-01, // 1.300000
+ 2.48292651895704763465033693137229420244693756103516e-01, // 1.400000
+ 2.36183276370507344577021058285026811063289642333984e-01, // 1.500000
+ 2.24664482058610781578522619383875280618667602539062e-01, // 1.600000
+ 2.13707465974363353122811304274364374577999114990234e-01, // 1.700000
+ 2.03284829870299554865908930878504179418087005615234e-01, // 1.800000
+ 1.93370511727250615763296082150191068649291992187500e-01, // 1.900000
+ 1.83939720585721167012138721474912017583847045898438e-01, // 2.000000
+ 1.28440682656735111200063670366944279521703720092773e-01, // 2.718282
+ 1.03939788175380964929672700236551463603973388671875e-01, // 3.141593
+ };
+
+ public static final double[] GNUR_EXP_CDF_05 = { //
+ 4.99999999987500026690715295475821817669515034765482e-11, // 0.000000
+ 4.99998750002083400397704521234132357676571700721979e-06, // 0.000010
+ 4.87705754992859910612601481716410489752888679504395e-02, // 0.100000
+ 5.98617593408454357062353778928809333592653274536133e-02, // 0.123457
+ 9.51625819640404269073030718573136255145072937011719e-02, // 0.200000
+ 1.27082379621773322586619769936078228056430816650391e-01, // 0.271828
+ 1.39292023574942191999070928432047367095947265625000e-01, // 0.300000
+ 1.45364000846775420772161169225000776350498199462891e-01, // 0.314159
+ 1.81269246922018151257915974383649881929159164428711e-01, // 0.400000
+ 2.21199216928595121522960198490181937813758850097656e-01, // 0.500000
+ 2.59181779318282123902861258102348074316978454589844e-01, // 0.600000
+ 2.95311910281286560397973062208620831370353698730469e-01, // 0.700000
+ 3.29679953964360727969307163220946677029132843017578e-01, // 0.800000
+ 3.62371848378226724118889023884548805654048919677734e-01, // 0.900000
+ 3.93469340287366575736882623459678143262863159179688e-01, // 1.000000
+ 4.23050189619513350436363907647319138050079345703125e-01, // 1.100000
+ 4.51188363905973555123551932410919107496738433837891e-01, // 1.200000
+ 4.77954223238983955113212687138002365827560424804688e-01, // 1.300000
+ 5.03414696208590473069932613725541159510612487792969e-01, // 1.400000
+ 5.27633447258985310845957883429946377873420715332031e-01, // 1.500000
+ 5.50671035882778436842954761232249438762664794921875e-01, // 1.600000
+ 5.72585068051273293754377391451271250844001770019531e-01, // 1.700000
+ 5.93430340259400890268182138242991641163825988769531e-01, // 1.800000
+ 6.13258976545498768473407835699617862701416015625000e-01, // 1.900000
+ 6.32120558828557665975722557050175964832305908203125e-01, // 2.000000
+ 7.43118634686652401732942507806001231074333190917969e-01, // 2.718282
+ 7.92120423649259608467332327563781291246414184570312e-01, // 3.141593
+ };
+
+ public static final double[] GNUR_EXP_PDF_05 = { //
+ 4.99999999974999997931490725022740662097930908203125e-01, // 0.000000
+ 4.99997500006249984139117259474005550146102905273438e-01, // 0.000010
+ 4.75614712250357007938816877867793664336204528808594e-01, // 0.100000
+ 4.70069120329577261330200599331874400377273559570312e-01, // 0.123457
+ 4.52418709017979758790772848442429676651954650878906e-01, // 0.200000
+ 4.36458810189113310951114499403047375380992889404297e-01, // 0.271828
+ 4.30353988212528904000464535783976316452026367187500e-01, // 0.300000
+ 4.27317999576612317369495031016413122415542602539062e-01, // 0.314159
+ 4.09365376538990910493254204993718303740024566650391e-01, // 0.400000
+ 3.89400391535702439238519900754909031093120574951172e-01, // 0.500000
+ 3.70409110340858938048569370948825962841510772705078e-01, // 0.600000
+ 3.52344044859356719801013468895689584314823150634766e-01, // 0.700000
+ 3.35160023017819663770922034018440172076225280761719e-01, // 0.800000
+ 3.18814075810886665696131103686639107763767242431641e-01, // 0.900000
+ 3.03265329856316712131558688270160928368568420410156e-01, // 1.000000
+ 2.88474905190243324781818046176340430974960327148438e-01, // 1.100000
+ 2.74405818047013194682648418165626935660839080810547e-01, // 1.200000
+ 2.61022888380508022443393656430998817086219787597656e-01, // 1.300000
+ 2.48292651895704763465033693137229420244693756103516e-01, // 1.400000
+ 2.36183276370507344577021058285026811063289642333984e-01, // 1.500000
+ 2.24664482058610781578522619383875280618667602539062e-01, // 1.600000
+ 2.13707465974363353122811304274364374577999114990234e-01, // 1.700000
+ 2.03284829870299554865908930878504179418087005615234e-01, // 1.800000
+ 1.93370511727250615763296082150191068649291992187500e-01, // 1.900000
+ 1.83939720585721167012138721474912017583847045898438e-01, // 2.000000
+ 1.28440682656673799133528746096999384462833404541016e-01, // 2.718282
+ 1.03939788175370209644121644032566109672188758850098e-01, // 3.141593
+ };
+
+ public static final double[] SCIPY_EXP_CDF_1 = { //
+ 9.99999999950000070330663866405545691512513073462287e-11, // 0.000000
+ 9.99995000016666629922851977640974041605659294873476e-06, // 0.000010
+ 9.51625819640404269073030718573136255145072937011719e-02, // 0.100000
+ 1.16140088450309583723019102308171568438410758972168e-01, // 0.123457
+ 1.81269246922018151257915974383649881929159164428711e-01, // 0.200000
+ 2.38014828033141334628908225568011403083801269531250e-01, // 0.271828
+ 2.59181779318282123902861258102348074316978454589844e-01, // 0.300000
+ 2.69597308951354364126018481329083442687988281250000e-01, // 0.314159
+ 3.29679953964360727969307163220946677029132843017578e-01, // 0.400000
+ 3.93469340287366575736882623459678143262863159179688e-01, // 0.500000
+ 4.51188363905973555123551932410919107496738433837891e-01, // 0.600000
+ 5.03414696208590473069932613725541159510612487792969e-01, // 0.700000
+ 5.50671035882778436842954761232249438762664794921875e-01, // 0.800000
+ 5.93430340259400890268182138242991641163825988769531e-01, // 0.900000
+ 6.32120558828557665975722557050175964832305908203125e-01, // 1.000000
+ 6.67128916301920504849931603530421853065490722656250e-01, // 1.100000
+ 6.98805788087797918883836700842948630452156066894531e-01, // 1.200000
+ 7.27468206965987462808698182925581932067871093750000e-01, // 1.300000
+ 7.53403036058393538176858328370144590735435485839844e-01, // 1.400000
+ 7.76869839851570209710018843907164409756660461425781e-01, // 1.500000
+ 7.98103482005344644356625849468400701880455017089844e-01, // 1.600000
+ 8.17316475947265308477085454796906560659408569335938e-01, // 1.700000
+ 8.34701111778413440411839019361650571227073669433594e-01, // 1.800000
+ 8.50431380777364909206994525447953492403030395507812e-01, // 1.900000
+ 8.64664716763387297682186272140825167298316955566406e-01, // 2.000000
+ 9.34011964154687457373427150741918012499809265136719e-01, // 2.718282
+ 9.56786081736227700389463279861956834793090820312500e-01, // 3.141593
+ };
+
+ public static final double[] SCIPY_EXP_PDF_1 = { //
+ 9.99999999899999991725962900090962648391723632812500e-01, // 0.000000
+ 9.99990000049999827602675850357627496123313903808594e-01, // 0.000010
+ 9.04837418035959517581545696884859353303909301757812e-01, // 0.100000
+ 8.83859911549690457910344321135198697447776794433594e-01, // 0.123457
+ 8.18730753077981820986508409987436607480049133300781e-01, // 0.200000
+ 7.61985171966858665371091774431988596916198730468750e-01, // 0.271828
+ 7.40818220681717876097138741897651925683021545410156e-01, // 0.300000
+ 7.30402691048645635873981518670916557312011718750000e-01, // 0.314159
+ 6.70320046035639327541844068036880344152450561523438e-01, // 0.400000
+ 6.06530659712633424263117376540321856737136840820312e-01, // 0.500000
+ 5.48811636094026389365296836331253871321678161621094e-01, // 0.600000
+ 4.96585303791409526930067386274458840489387512207031e-01, // 0.700000
+ 4.49328964117221563157045238767750561237335205078125e-01, // 0.800000
+ 4.06569659740599109731817861757008358836174011230469e-01, // 0.900000
+ 3.67879441171442334024277442949824035167694091796875e-01, // 1.000000
+ 3.32871083698079550661219627727405168116092681884766e-01, // 1.100000
+ 3.01194211912202136627314530414878390729427337646484e-01, // 1.200000
+ 2.72531793034012592702453048332245089113712310791016e-01, // 1.300000
+ 2.46596963941606489578717287258768919855356216430664e-01, // 1.400000
+ 2.23130160148429818045556771721749100834131240844727e-01, // 1.500000
+ 2.01896517994655383398949766160512808710336685180664e-01, // 1.600000
+ 1.82683524052734663767338929574179928749799728393555e-01, // 1.700000
+ 1.65298888221586531832585365009435918182134628295898e-01, // 1.800000
+ 1.49568619222635063037429858923132997006177902221680e-01, // 1.900000
+ 1.35335283236612702317813727859174832701683044433594e-01, // 2.000000
+ 6.59880358453125426265728492580819875001907348632812e-02, // 2.718282
+ 4.32139182637722579771732966946728993207216262817383e-02, // 3.141593
+ };
+
+ public static final double[] GNUR_EXP_CDF_1 = { //
+ 9.99999999950000070330663866405545691512513073462287e-11, // 0.000000
+ 9.99995000016666629922851977640974041605659294873476e-06, // 0.000010
+ 9.51625819640404269073030718573136255145072937011719e-02, // 0.100000
+ 1.16140088450309583723019102308171568438410758972168e-01, // 0.123457
+ 1.81269246922018151257915974383649881929159164428711e-01, // 0.200000
+ 2.38014828033214137503748020208149682730436325073242e-01, // 0.271828
+ 2.59181779318282123902861258102348074316978454589844e-01, // 0.300000
+ 2.69597308951369518670304614715860225260257720947266e-01, // 0.314159
+ 3.29679953964360727969307163220946677029132843017578e-01, // 0.400000
+ 3.93469340287366575736882623459678143262863159179688e-01, // 0.500000
+ 4.51188363905973555123551932410919107496738433837891e-01, // 0.600000
+ 5.03414696208590473069932613725541159510612487792969e-01, // 0.700000
+ 5.50671035882778436842954761232249438762664794921875e-01, // 0.800000
+ 5.93430340259400890268182138242991641163825988769531e-01, // 0.900000
+ 6.32120558828557665975722557050175964832305908203125e-01, // 1.000000
+ 6.67128916301920504849931603530421853065490722656250e-01, // 1.100000
+ 6.98805788087797918883836700842948630452156066894531e-01, // 1.200000
+ 7.27468206965987462808698182925581932067871093750000e-01, // 1.300000
+ 7.53403036058393538176858328370144590735435485839844e-01, // 1.400000
+ 7.76869839851570209710018843907164409756660461425781e-01, // 1.500000
+ 7.98103482005344644356625849468400701880455017089844e-01, // 1.600000
+ 8.17316475947265308477085454796906560659408569335938e-01, // 1.700000
+ 8.34701111778413440411839019361650571227073669433594e-01, // 1.800000
+ 8.50431380777364909206994525447953492403030395507812e-01, // 1.900000
+ 8.64664716763387297682186272140825167298316955566406e-01, // 2.000000
+ 9.34011964154750407018923397117760032415390014648438e-01, // 2.718282
+ 9.56786081736236693195962743629934266209602355957031e-01, // 3.141593
+ };
+
+ public static final double[] GNUR_EXP_PDF_1 = { //
+ 9.99999999899999991725962900090962648391723632812500e-01, // 0.000000
+ 9.99990000049999827602675850357627496123313903808594e-01, // 0.000010
+ 9.04837418035959517581545696884859353303909301757812e-01, // 0.100000
+ 8.83859911549690457910344321135198697447776794433594e-01, // 0.123457
+ 8.18730753077981820986508409987436607480049133300781e-01, // 0.200000
+ 7.61985171966785834740676364162936806678771972656250e-01, // 0.271828
+ 7.40818220681717876097138741897651925683021545410156e-01, // 0.300000
+ 7.30402691048630536840846616541966795921325683593750e-01, // 0.314159
+ 6.70320046035639327541844068036880344152450561523438e-01, // 0.400000
+ 6.06530659712633424263117376540321856737136840820312e-01, // 0.500000
+ 5.48811636094026389365296836331253871321678161621094e-01, // 0.600000
+ 4.96585303791409526930067386274458840489387512207031e-01, // 0.700000
+ 4.49328964117221563157045238767750561237335205078125e-01, // 0.800000
+ 4.06569659740599109731817861757008358836174011230469e-01, // 0.900000
+ 3.67879441171442334024277442949824035167694091796875e-01, // 1.000000
+ 3.32871083698079550661219627727405168116092681884766e-01, // 1.100000
+ 3.01194211912202136627314530414878390729427337646484e-01, // 1.200000
+ 2.72531793034012592702453048332245089113712310791016e-01, // 1.300000
+ 2.46596963941606489578717287258768919855356216430664e-01, // 1.400000
+ 2.23130160148429818045556771721749100834131240844727e-01, // 1.500000
+ 2.01896517994655383398949766160512808710336685180664e-01, // 1.600000
+ 1.82683524052734663767338929574179928749799728393555e-01, // 1.700000
+ 1.65298888221586531832585365009435918182134628295898e-01, // 1.800000
+ 1.49568619222635063037429858923132997006177902221680e-01, // 1.900000
+ 1.35335283236612702317813727859174832701683044433594e-01, // 2.000000
+ 6.59880358452495374699253716244129464030265808105469e-02, // 2.718282
+ 4.32139182637633137429311602772941114380955696105957e-02, // 3.141593
+ };
+
+ public static final double[] SCIPY_EXP_CDF_2 = { //
+ 1.99999999979999994996431941180555121040374189078648e-10, // 0.000000
+ 1.99998000013333293123189704498798846543650142848492e-05, // 0.000010
+ 1.81269246922018151257915974383649881929159164428711e-01, // 0.100000
+ 2.18791656755373431497702085835044272243976593017578e-01, // 0.123457
+ 3.29679953964360727969307163220946677029132843017578e-01, // 0.200000
+ 4.19378597702636857214031351759331300854682922363281e-01, // 0.271828
+ 4.51188363905973555123551932410919107496738433837891e-01, // 0.300000
+ 4.66511908908896710812541641644202172756195068359375e-01, // 0.314159
+ 5.50671035882778436842954761232249438762664794921875e-01, // 0.400000
+ 6.32120558828557665975722557050175964832305908203125e-01, // 0.500000
+ 6.98805788087797918883836700842948630452156066894531e-01, // 0.600000
+ 7.53403036058393538176858328370144590735435485839844e-01, // 0.700000
+ 7.98103482005344644356625849468400701880455017089844e-01, // 0.800000
+ 8.34701111778413440411839019361650571227073669433594e-01, // 0.900000
+ 8.64664716763387297682186272140825167298316955566406e-01, // 1.000000
+ 8.89196841637666102187154137936886399984359741210938e-01, // 1.100000
+ 9.09282046710587543714154890039935708045959472656250e-01, // 1.200000
+ 9.25726421785666109265378054260509088635444641113281e-01, // 1.300000
+ 9.39189937374782068957301817135885357856750488281250e-01, // 1.400000
+ 9.50212931632136048598624711303273215889930725097656e-01, // 1.500000
+ 9.59237796021633837284525725408457219600677490234375e-01, // 1.600000
+ 9.66626730039673920380494109849678352475166320800781e-01, // 1.700000
+ 9.72676277552707468920800693013006821274757385253906e-01, // 1.800000
+ 9.77629228143834350106544661684893071651458740234375e-01, // 1.900000
+ 9.81684361111265779697987454710528254508972167968750e-01, // 2.000000
+ 9.95645579125277735421661873260745778679847717285156e-01, // 2.718282
+ 9.98132557268291975560714490711688995361328125000000e-01, // 3.141593
+ };
+
+ public static final double[] SCIPY_EXP_PDF_2 = { //
+ 1.99999999959999996690385160036385059356689453125000e+00, // 0.000000
+ 1.99996000039999732855733327596681192517280578613281e+00, // 0.000010
+ 1.63746150615596364197301681997487321496009826660156e+00, // 0.100000
+ 1.56241668648925324802689829084556549787521362304688e+00, // 0.123457
+ 1.34064009207127865508368813607376068830490112304688e+00, // 0.200000
+ 1.16124280459472628557193729648133739829063415527344e+00, // 0.271828
+ 1.09762327218805277873059367266250774264335632324219e+00, // 0.300000
+ 1.06697618218220657837491671671159565448760986328125e+00, // 0.314159
+ 8.98657928234443126314090477535501122474670410156250e-01, // 0.400000
+ 7.35758882342884668048554885899648070335388183593750e-01, // 0.500000
+ 6.02388423824404273254629060829756781458854675292969e-01, // 0.600000
+ 4.93193927883212979157434574517537839710712432861328e-01, // 0.700000
+ 4.03793035989310766797899532321025617420673370361328e-01, // 0.800000
+ 3.30597776443173063665170730018871836364269256591797e-01, // 0.900000
+ 2.70670566473225404635627455718349665403366088867188e-01, // 1.000000
+ 2.21606316724667740114540492868400178849697113037109e-01, // 1.100000
+ 1.81435906578825023593992682435782626271247863769531e-01, // 1.200000
+ 1.48547156428667753713668275850068312138319015502930e-01, // 1.300000
+ 1.21620125250435945352123212614969816058874130249023e-01, // 1.400000
+ 9.95741367357278889249627695789968129247426986694336e-02, // 1.500000
+ 8.15244079567324225754632038842828478664159774780273e-02, // 1.600000
+ 6.67465399206521592390117803006432950496673583984375e-02, // 1.700000
+ 5.46474448945851176695498452318133786320686340332031e-02, // 1.800000
+ 4.47415437123312026423960219290165696293115615844727e-02, // 1.900000
+ 3.66312777774683573372982436922029592096805572509766e-02, // 2.000000
+ 8.70884174944450660527106578001621528528630733489990e-03, // 2.718282
+ 3.73488546341597862227024151593468559440225362777710e-03, // 3.141593
+ };
+
+ public static final double[] GNUR_EXP_CDF_2 = { //
+ 1.99999999979999994996431941180555121040374189078648e-10, // 0.000000
+ 1.99998000013333293123189704498798846543650142848492e-05, // 0.000010
+ 1.81269246922018151257915974383649881929159164428711e-01, // 0.100000
+ 2.18791656755373431497702085835044272243976593017578e-01, // 0.123457
+ 3.29679953964360727969307163220946677029132843017578e-01, // 0.200000
+ 4.19378597702747768494191404897719621658325195312500e-01, // 0.271828
+ 4.51188363905973555123551932410919107496738433837891e-01, // 0.300000
+ 4.66511908908918804250731682259356603026390075683594e-01, // 0.314159
+ 5.50671035882778436842954761232249438762664794921875e-01, // 0.400000
+ 6.32120558828557665975722557050175964832305908203125e-01, // 0.500000
+ 6.98805788087797918883836700842948630452156066894531e-01, // 0.600000
+ 7.53403036058393538176858328370144590735435485839844e-01, // 0.700000
+ 7.98103482005344644356625849468400701880455017089844e-01, // 0.800000
+ 8.34701111778413440411839019361650571227073669433594e-01, // 0.900000
+ 8.64664716763387297682186272140825167298316955566406e-01, // 1.000000
+ 8.89196841637666102187154137936886399984359741210938e-01, // 1.100000
+ 9.09282046710587543714154890039935708045959472656250e-01, // 1.200000
+ 9.25726421785666109265378054260509088635444641113281e-01, // 1.300000
+ 9.39189937374782068957301817135885357856750488281250e-01, // 1.400000
+ 9.50212931632136048598624711303273215889930725097656e-01, // 1.500000
+ 9.59237796021633837284525725408457219600677490234375e-01, // 1.600000
+ 9.66626730039673920380494109849678352475166320800781e-01, // 1.700000
+ 9.72676277552707468920800693013006821274757385253906e-01, // 1.800000
+ 9.77629228143834350106544661684893071651458740234375e-01, // 1.900000
+ 9.81684361111265779697987454710528254508972167968750e-01, // 2.000000
+ 9.95645579125286062094346561934798955917358398437500e-01, // 2.718282
+ 9.98132557268292752716831728321267291903495788574219e-01, // 3.141593
+ };
+
+ public static final double[] GNUR_EXP_PDF_2 = { //
+ 1.99999999959999996690385160036385059356689453125000e+00, // 0.000000
+ 1.99996000039999732855733327596681192517280578613281e+00, // 0.000010
+ 1.63746150615596364197301681997487321496009826660156e+00, // 0.100000
+ 1.56241668648925324802689829084556549787521362304688e+00, // 0.123457
+ 1.34064009207127865508368813607376068830490112304688e+00, // 0.200000
+ 1.16124280459450446301161719020456075668334960937500e+00, // 0.271828
+ 1.09762327218805277873059367266250774264335632324219e+00, // 0.300000
+ 1.06697618218216239149853663548128679394721984863281e+00, // 0.314159
+ 8.98657928234443126314090477535501122474670410156250e-01, // 0.400000
+ 7.35758882342884668048554885899648070335388183593750e-01, // 0.500000
+ 6.02388423824404273254629060829756781458854675292969e-01, // 0.600000
+ 4.93193927883212979157434574517537839710712432861328e-01, // 0.700000
+ 4.03793035989310766797899532321025617420673370361328e-01, // 0.800000
+ 3.30597776443173063665170730018871836364269256591797e-01, // 0.900000
+ 2.70670566473225404635627455718349665403366088867188e-01, // 1.000000
+ 2.21606316724667740114540492868400178849697113037109e-01, // 1.100000
+ 1.81435906578825023593992682435782626271247863769531e-01, // 1.200000
+ 1.48547156428667753713668275850068312138319015502930e-01, // 1.300000
+ 1.21620125250435945352123212614969816058874130249023e-01, // 1.400000
+ 9.95741367357278889249627695789968129247426986694336e-02, // 1.500000
+ 8.15244079567324225754632038842828478664159774780273e-02, // 1.600000
+ 6.67465399206521592390117803006432950496673583984375e-02, // 1.700000
+ 5.46474448945851176695498452318133786320686340332031e-02, // 1.800000
+ 4.47415437123312026423960219290165696293115615844727e-02, // 1.900000
+ 3.66312777774683573372982436922029592096805572509766e-02, // 2.000000
+ 8.70884174942787754603035210720918257720768451690674e-03, // 2.718282
+ 3.73488546341443254997227718661179096670821309089661e-03, // 3.141593
+ };
+
+ public static final double[] SCIPY_EXP_CDF_4 = { //
+ 3.99999999920000017111637123087353531958321184447414e-10, // 0.000000
+ 3.99992000106665654499821238587742300296667963266373e-05, // 0.000010
+ 3.29679953964360727969307163220946677029132843017578e-01, // 0.100000
+ 3.89713524444985692785081710098893381655216217041016e-01, // 0.123457
+ 5.50671035882778436842954761232249438762664794921875e-01, // 0.200000
+ 6.62878787194243646041513784439302980899810791015625e-01, // 0.271828
+ 6.98805788087797918883836700842948630452156066894531e-01, // 0.300000
+ 7.15390456663970719652922980458242818713188171386719e-01, // 0.314159
+ 7.98103482005344644356625849468400701880455017089844e-01, // 0.400000
+ 8.64664716763387297682186272140825167298316955566406e-01, // 0.500000
+ 9.09282046710587543714154890039935708045959472656250e-01, // 0.600000
+ 9.39189937374782068957301817135885357856750488281250e-01, // 0.700000
+ 9.59237796021633837284525725408457219600677490234375e-01, // 0.800000
+ 9.72676277552707468920800693013006821274757385253906e-01, // 0.900000
+ 9.81684361111265779697987454710528254508972167968750e-01, // 1.000000
+ 9.87722660096931548423526692204177379608154296875000e-01, // 1.100000
+ 9.91770252950979980255397094879299402236938476562500e-01, // 1.200000
+ 9.94483435579239238855109306314261630177497863769531e-01, // 1.300000
+ 9.96302136283517048020996753621147945523262023925781e-01, // 1.400000
+ 9.97521247823333623294672634074231609702110290527344e-01, // 1.500000
+ 9.98338442726826036377474338223692029714584350585938e-01, // 1.600000
+ 9.98886224852155168996148404403356835246086120605469e-01, // 1.700000
+ 9.99253414191623279272391755512217059731483459472656e-01, // 1.800000
+ 9.99499548566559425921695947181433439254760742187500e-01, // 1.900000
+ 9.99664537372097483647337412548949941992759704589844e-01, // 2.000000
+ 9.99981039018845829779991163377417251467704772949219e-01, // 2.718282
+ 9.99996512657643776833538140635937452316284179687500e-01, // 3.141593
+ };
+
+ public static final double[] SCIPY_EXP_PDF_4 = { //
+ 3.99999999839999986761540640145540237426757812500000e+00, // 0.000000
+ 3.99984000319995747219081749790348112583160400390625e+00, // 0.000010
+ 2.68128018414255731016737627214752137660980224609375e+00, // 0.100000
+ 2.44114590222005700681506823457311838865280151367188e+00, // 0.123457
+ 1.79731585646888625262818095507100224494934082031250e+00, // 0.200000
+ 1.34848485122302563787854978727409616112709045410156e+00, // 0.271828
+ 1.20477684764880854650925812165951356291770935058594e+00, // 0.300000
+ 1.13843817334411712138830807816702872514724731445312e+00, // 0.314159
+ 8.07586071978621533595799064642051234841346740722656e-01, // 0.400000
+ 5.41341132946450809271254911436699330806732177734375e-01, // 0.500000
+ 3.62871813157650047187985364871565252542495727539062e-01, // 0.600000
+ 2.43240250500871890704246425229939632117748260498047e-01, // 0.700000
+ 1.63048815913464845150926407768565695732831954956055e-01, // 0.800000
+ 1.09294889789170235339099690463626757264137268066406e-01, // 0.900000
+ 7.32625555549367146745964873844059184193611145019531e-02, // 1.000000
+ 4.91093596122737438558480960182350827381014823913574e-02, // 1.100000
+ 3.29189881960801206117750439261726569384336471557617e-02, // 1.200000
+ 2.20662576830430862129261981863237451761960983276367e-02, // 1.300000
+ 1.47914548659317281187330905822818749584257602691650e-02, // 1.400000
+ 9.91500870666543396292347267717559589073061943054199e-03, // 1.500000
+ 6.64622909269573566154454269394591392483562231063843e-03, // 1.600000
+ 4.45510059137921299310391987091861665248870849609375e-03, // 1.700000
+ 2.98634323350671681066015317185247113229706883430481e-03, // 1.800000
+ 2.00180573376244333103080030866749439155682921409607e-03, // 1.900000
+ 1.34185051161004741289428654482662750524468719959259e-03, // 2.000000
+ 7.58439246168676609646114306073627631121780723333359e-05, // 2.718282
+ 1.39493694248359891788707765059029952681157737970352e-05, // 3.141593
+ };
+
+ public static final double[] GNUR_EXP_CDF_4 = { //
+ 3.99999999920000017111637123087353531958321184447414e-10, // 0.000000
+ 3.99992000106665654499821238587742300296667963266373e-05, // 0.000010
+ 3.29679953964360727969307163220946677029132843017578e-01, // 0.100000
+ 3.89713524444985692785081710098893381655216217041016e-01, // 0.123457
+ 5.50671035882778436842954761232249438762664794921875e-01, // 0.200000
+ 6.62878787194372431912370302597992122173309326171875e-01, // 0.271828
+ 6.98805788087797918883836700842948630452156066894531e-01, // 0.300000
+ 7.15390456663994256381045033776899799704551696777344e-01, // 0.314159
+ 7.98103482005344644356625849468400701880455017089844e-01, // 0.400000
+ 8.64664716763387297682186272140825167298316955566406e-01, // 0.500000
+ 9.09282046710587543714154890039935708045959472656250e-01, // 0.600000
+ 9.39189937374782068957301817135885357856750488281250e-01, // 0.700000
+ 9.59237796021633837284525725408457219600677490234375e-01, // 0.800000
+ 9.72676277552707468920800693013006821274757385253906e-01, // 0.900000
+ 9.81684361111265779697987454710528254508972167968750e-01, // 1.000000
+ 9.87722660096931548423526692204177379608154296875000e-01, // 1.100000
+ 9.91770252950979980255397094879299402236938476562500e-01, // 1.200000
+ 9.94483435579239238855109306314261630177497863769531e-01, // 1.300000
+ 9.96302136283517048020996753621147945523262023925781e-01, // 1.400000
+ 9.97521247823333623294672634074231609702110290527344e-01, // 1.500000
+ 9.98338442726826036377474338223692029714584350585938e-01, // 1.600000
+ 9.98886224852155168996148404403356835246086120605469e-01, // 1.700000
+ 9.99253414191623279272391755512217059731483459472656e-01, // 1.800000
+ 9.99499548566559425921695947181433439254760742187500e-01, // 1.900000
+ 9.99664537372097483647337412548949941992759704589844e-01, // 2.000000
+ 9.99981039018845829779991163377417251467704772949219e-01, // 2.718282
+ 9.99996512657643776833538140635937452316284179687500e-01, // 3.141593
+ };
+
+ public static final double[] GNUR_EXP_PDF_4 = { //
+ 3.99999999839999986761540640145540237426757812500000e+00, // 0.000000
+ 3.99984000319995747219081749790348112583160400390625e+00, // 0.000010
+ 2.68128018414255731016737627214752137660980224609375e+00, // 0.100000
+ 2.44114590222005700681506823457311838865280151367188e+00, // 0.123457
+ 1.79731585646888625262818095507100224494934082031250e+00, // 0.200000
+ 1.34848485122251049439512371463933959603309631347656e+00, // 0.271828
+ 1.20477684764880854650925812165951356291770935058594e+00, // 0.300000
+ 1.13843817334402297447581986489240080118179321289062e+00, // 0.314159
+ 8.07586071978621533595799064642051234841346740722656e-01, // 0.400000
+ 5.41341132946450809271254911436699330806732177734375e-01, // 0.500000
+ 3.62871813157650047187985364871565252542495727539062e-01, // 0.600000
+ 2.43240250500871890704246425229939632117748260498047e-01, // 0.700000
+ 1.63048815913464845150926407768565695732831954956055e-01, // 0.800000
+ 1.09294889789170235339099690463626757264137268066406e-01, // 0.900000
+ 7.32625555549367146745964873844059184193611145019531e-02, // 1.000000
+ 4.91093596122737438558480960182350827381014823913574e-02, // 1.100000
+ 3.29189881960801206117750439261726569384336471557617e-02, // 1.200000
+ 2.20662576830430862129261981863237451761960983276367e-02, // 1.300000
+ 1.47914548659317281187330905822818749584257602691650e-02, // 1.400000
+ 9.91500870666543396292347267717559589073061943054199e-03, // 1.500000
+ 6.64622909269573566154454269394591392483562231063843e-03, // 1.600000
+ 4.45510059137921299310391987091861665248870849609375e-03, // 1.700000
+ 2.98634323350671681066015317185247113229706883430481e-03, // 1.800000
+ 2.00180573376244333103080030866749439155682921409607e-03, // 1.900000
+ 1.34185051161004741289428654482662750524468719959259e-03, // 2.000000
+ 7.58439246165780028017047720290122470032656565308571e-05, // 2.718282
+ 1.39493694248244424257338058836808158957865089178085e-05, // 3.141593
+ };
+
+ public static final double[] P_QUANT = { //
+ 0.0001, 0.001, 0.01, 0.1, 0.25, 0.5, 0.75, 0.9, 0.99, 0.999, 0.9999 //
+ };
+
+ public static final double[] SCIPY_EXP_QUANT_01 = { //
+ 1.00005000333358335003197669976771067013032734394073e-03, // 0.000100
+ 1.00050033358353353957204134871972200926393270492554e-02, // 0.001000
+ 1.00503358535014422625586405501962872222065925598145e-01, // 0.010000
+ 1.05360515657826314672718126530526205897331237792969e+00, // 0.100000
+ 2.87682072451780879163152349065057933330535888671875e+00, // 0.250000
+ 6.93147180559945308431224475498311221599578857421875e+00, // 0.500000
+ 1.38629436111989061686244895099662244319915771484375e+01, // 0.750000
+ 2.30258509299404607872929773293435573577880859375000e+01, // 0.900000
+ 4.60517018598809002583038818556815385818481445312500e+01, // 0.990000
+ 6.90775527898213539401695015840232372283935546875000e+01, // 0.999000
+ 9.21034037197629373849849798716604709625244140625000e+01, // 0.999900
+ };
+
+ public static final double[] GNUR_EXP_QUANT_01 = { //
+ 1.00005000333358335003197669976771067013032734394073e-03, // 0.000100
+ 1.00050033358353353957204134871972200926393270492554e-02, // 0.001000
+ 1.00503358535014422625586405501962872222065925598145e-01, // 0.010000
+ 1.05360515657826314672718126530526205897331237792969e+00, // 0.100000
+ 2.87682072451780879163152349065057933330535888671875e+00, // 0.250000
+ 6.93147180559945308431224475498311221599578857421875e+00, // 0.500000
+ 1.38629436111989061686244895099662244319915771484375e+01, // 0.750000
+ 2.30258509299404607872929773293435573577880859375000e+01, // 0.900000
+ 4.60517018598809002583038818556815385818481445312500e+01, // 0.990000
+ 6.90775527898213539401695015840232372283935546875000e+01, // 0.999000
+ 9.21034037197629373849849798716604709625244140625000e+01, // 0.999900
+ };
+
+ public static final double[] SCIPY_EXP_QUANT_05 = { //
+ 2.00010000666716686269427927236108644137857481837273e-04, // 0.000100
+ 2.00100066716706699240790889859908929793164134025574e-03, // 0.001000
+ 2.01006717070028838312278907096697366796433925628662e-02, // 0.010000
+ 2.10721031315652618243206006809487007558345794677734e-01, // 0.100000
+ 5.75364144903561802735225683136377483606338500976562e-01, // 0.250000
+ 1.38629436111989057245352796599036082625389099121094e+00, // 0.500000
+ 2.77258872223978114490705593198072165250778198242188e+00, // 0.750000
+ 4.60517018598809180218722758581861853599548339843750e+00, // 0.900000
+ 9.21034037197618005166077637113630771636962890625000e+00, // 0.990000
+ 1.38155105579642718538480039569549262523651123046875e+01, // 0.999000
+ 1.84206807439525874769969959743320941925048828125000e+01, // 0.999900
+ };
+
+ public static final double[] GNUR_EXP_QUANT_05 = { //
+ 2.00010000666716686269427927236108644137857481837273e-04, // 0.000100
+ 2.00100066716706699240790889859908929793164134025574e-03, // 0.001000
+ 2.01006717070028838312278907096697366796433925628662e-02, // 0.010000
+ 2.10721031315652618243206006809487007558345794677734e-01, // 0.100000
+ 5.75364144903561802735225683136377483606338500976562e-01, // 0.250000
+ 1.38629436111989057245352796599036082625389099121094e+00, // 0.500000
+ 2.77258872223978114490705593198072165250778198242188e+00, // 0.750000
+ 4.60517018598809180218722758581861853599548339843750e+00, // 0.900000
+ 9.21034037197618005166077637113630771636962890625000e+00, // 0.990000
+ 1.38155105579642718538480039569549262523651123046875e+01, // 0.999000
+ 1.84206807439525874769969959743320941925048828125000e+01, // 0.999900
+ };
+
+ public static final double[] SCIPY_EXP_QUANT_1 = { //
+ 1.00005000333358343134713963618054322068928740918636e-04, // 0.000100
+ 1.00050033358353349620395444929954464896582067012787e-03, // 0.001000
+ 1.00503358535014419156139453548348683398216962814331e-02, // 0.010000
+ 1.05360515657826309121603003404743503779172897338867e-01, // 0.100000
+ 2.87682072451780901367612841568188741803169250488281e-01, // 0.250000
+ 6.93147180559945286226763982995180413126945495605469e-01, // 0.500000
+ 1.38629436111989057245352796599036082625389099121094e+00, // 0.750000
+ 2.30258509299404590109361379290930926799774169921875e+00, // 0.900000
+ 4.60517018598809002583038818556815385818481445312500e+00, // 0.990000
+ 6.90775527898213592692400197847746312618255615234375e+00, // 0.999000
+ 9.21034037197629373849849798716604709625244140625000e+00, // 0.999900
+ };
+
+ public static final double[] GNUR_EXP_QUANT_1 = { //
+ 1.00005000333358343134713963618054322068928740918636e-04, // 0.000100
+ 1.00050033358353349620395444929954464896582067012787e-03, // 0.001000
+ 1.00503358535014419156139453548348683398216962814331e-02, // 0.010000
+ 1.05360515657826309121603003404743503779172897338867e-01, // 0.100000
+ 2.87682072451780901367612841568188741803169250488281e-01, // 0.250000
+ 6.93147180559945286226763982995180413126945495605469e-01, // 0.500000
+ 1.38629436111989057245352796599036082625389099121094e+00, // 0.750000
+ 2.30258509299404590109361379290930926799774169921875e+00, // 0.900000
+ 4.60517018598809002583038818556815385818481445312500e+00, // 0.990000
+ 6.90775527898213592692400197847746312618255615234375e+00, // 0.999000
+ 9.21034037197629373849849798716604709625244140625000e+00, // 0.999900
+ };
+
+ public static final double[] SCIPY_EXP_QUANT_2 = { //
+ 5.00025001666791715673569818090271610344643704593182e-05, // 0.000100
+ 5.00250166791766748101977224649772324482910335063934e-04, // 0.001000
+ 5.02516792675072095780697267741743416991084814071655e-03, // 0.010000
+ 5.26802578289131545608015017023717518895864486694336e-02, // 0.100000
+ 1.43841036225890450683806420784094370901584625244141e-01, // 0.250000
+ 3.46573590279972643113381991497590206563472747802734e-01, // 0.500000
+ 6.93147180559945286226763982995180413126945495605469e-01, // 0.750000
+ 1.15129254649702295054680689645465463399887084960938e+00, // 0.900000
+ 2.30258509299404501291519409278407692909240722656250e+00, // 0.990000
+ 3.45387763949106796346200098923873156309127807617188e+00, // 0.999000
+ 4.60517018598814686924924899358302354812622070312500e+00, // 0.999900
+ };
+
+ public static final double[] GNUR_EXP_QUANT_2 = { //
+ 5.00025001666791715673569818090271610344643704593182e-05, // 0.000100
+ 5.00250166791766748101977224649772324482910335063934e-04, // 0.001000
+ 5.02516792675072095780697267741743416991084814071655e-03, // 0.010000
+ 5.26802578289131545608015017023717518895864486694336e-02, // 0.100000
+ 1.43841036225890450683806420784094370901584625244141e-01, // 0.250000
+ 3.46573590279972643113381991497590206563472747802734e-01, // 0.500000
+ 6.93147180559945286226763982995180413126945495605469e-01, // 0.750000
+ 1.15129254649702295054680689645465463399887084960938e+00, // 0.900000
+ 2.30258509299404501291519409278407692909240722656250e+00, // 0.990000
+ 3.45387763949106796346200098923873156309127807617188e+00, // 0.999000
+ 4.60517018598814686924924899358302354812622070312500e+00, // 0.999900
+ };
+
+ public static final double[] SCIPY_EXP_QUANT_4 = { //
+ 2.50012500833395857836784909045135805172321852296591e-05, // 0.000100
+ 2.50125083395883374050988612324886162241455167531967e-04, // 0.001000
+ 2.51258396337536047890348633870871708495542407035828e-03, // 0.010000
+ 2.63401289144565772804007508511858759447932243347168e-02, // 0.100000
+ 7.19205181129452253419032103920471854507923126220703e-02, // 0.250000
+ 1.73286795139986321556690995748795103281736373901367e-01, // 0.500000
+ 3.46573590279972643113381991497590206563472747802734e-01, // 0.750000
+ 5.75646273248511475273403448227327316999435424804688e-01, // 0.900000
+ 1.15129254649702250645759704639203846454620361328125e+00, // 0.990000
+ 1.72693881974553398173100049461936578154563903808594e+00, // 0.999000
+ 2.30258509299407343462462449679151177406311035156250e+00, // 0.999900
+ };
+
+ public static final double[] GNUR_EXP_QUANT_4 = { //
+ 2.50012500833395857836784909045135805172321852296591e-05, // 0.000100
+ 2.50125083395883374050988612324886162241455167531967e-04, // 0.001000
+ 2.51258396337536047890348633870871708495542407035828e-03, // 0.010000
+ 2.63401289144565772804007508511858759447932243347168e-02, // 0.100000
+ 7.19205181129452253419032103920471854507923126220703e-02, // 0.250000
+ 1.73286795139986321556690995748795103281736373901367e-01, // 0.500000
+ 3.46573590279972643113381991497590206563472747802734e-01, // 0.750000
+ 5.75646273248511475273403448227327316999435424804688e-01, // 0.900000
+ 1.15129254649702250645759704639203846454620361328125e+00, // 0.990000
+ 1.72693881974553398173100049461936578154563903808594e+00, // 0.999000
+ 2.30258509299407343462462449679151177406311035156250e+00, // 0.999900
+ };
+
+ @Test
+ public void testPDF() {
+ checkPDF(new ExponentialDistribution(.1), P_CDFPDF, SCIPY_EXP_PDF_01, 1e-13);
+ checkPDF(new ExponentialDistribution(.5), P_CDFPDF, SCIPY_EXP_PDF_05, 1e-13);
+ checkPDF(new ExponentialDistribution(1.), P_CDFPDF, SCIPY_EXP_PDF_1, 1e-13);
+ checkPDF(new ExponentialDistribution(2.), P_CDFPDF, SCIPY_EXP_PDF_2, 1e-12);
+ checkPDF(new ExponentialDistribution(4.), P_CDFPDF, SCIPY_EXP_PDF_4, 1e-12);
+ checkPDF(new ExponentialDistribution(.1), P_CDFPDF, GNUR_EXP_PDF_01, 1e-15);
+ checkPDF(new ExponentialDistribution(.5), P_CDFPDF, GNUR_EXP_PDF_05, 1e-15);
+ checkPDF(new ExponentialDistribution(1.), P_CDFPDF, GNUR_EXP_PDF_1, 1e-15);
+ checkPDF(new ExponentialDistribution(2.), P_CDFPDF, GNUR_EXP_PDF_2, 1e-15);
+ checkPDF(new ExponentialDistribution(4.), P_CDFPDF, GNUR_EXP_PDF_4, 1e-15);
+ }
+
+ @Test
+ public void testCDF() {
+ checkCDF(new ExponentialDistribution(.1), P_CDFPDF, SCIPY_EXP_CDF_01, 1e-12);
+ checkCDF(new ExponentialDistribution(.5), P_CDFPDF, SCIPY_EXP_CDF_05, 1e-12);
+ checkCDF(new ExponentialDistribution(1.), P_CDFPDF, SCIPY_EXP_CDF_1, 1e-12);
+ checkCDF(new ExponentialDistribution(2.), P_CDFPDF, SCIPY_EXP_CDF_2, 1e-12);
+ checkCDF(new ExponentialDistribution(4.), P_CDFPDF, SCIPY_EXP_CDF_4, 1e-12);
+ checkCDF(new ExponentialDistribution(.1), P_CDFPDF, GNUR_EXP_CDF_01, 1e-15);
+ checkCDF(new ExponentialDistribution(.5), P_CDFPDF, GNUR_EXP_CDF_05, 1e-15);
+ checkCDF(new ExponentialDistribution(1.), P_CDFPDF, GNUR_EXP_CDF_1, 1e-15);
+ checkCDF(new ExponentialDistribution(2.), P_CDFPDF, GNUR_EXP_CDF_2, 1e-15);
+ checkCDF(new ExponentialDistribution(4.), P_CDFPDF, GNUR_EXP_CDF_4, 1e-15);
+ }
+
+ @Test
+ public void testProbit() {
+ checkQuantile(new ExponentialDistribution(.1), P_QUANT, GNUR_EXP_QUANT_01, 1e-15);
+ checkQuantile(new ExponentialDistribution(.5), P_QUANT, GNUR_EXP_QUANT_05, 1e-15);
+ checkQuantile(new ExponentialDistribution(1.), P_QUANT, GNUR_EXP_QUANT_1, 1e-15);
+ checkQuantile(new ExponentialDistribution(2.), P_QUANT, GNUR_EXP_QUANT_2, 1e-15);
+ checkQuantile(new ExponentialDistribution(4.), P_QUANT, GNUR_EXP_QUANT_4, 1e-15);
+ checkQuantile(new ExponentialDistribution(.1), P_QUANT, SCIPY_EXP_QUANT_01, 1e-15);
+ checkQuantile(new ExponentialDistribution(.5), P_QUANT, SCIPY_EXP_QUANT_05, 1e-15);
+ checkQuantile(new ExponentialDistribution(1.), P_QUANT, SCIPY_EXP_QUANT_1, 1e-15);
+ checkQuantile(new ExponentialDistribution(2.), P_QUANT, SCIPY_EXP_QUANT_2, 1e-15);
+ checkQuantile(new ExponentialDistribution(4.), P_QUANT, SCIPY_EXP_QUANT_4, 1e-15);
+ }
+} \ No newline at end of file
diff --git a/test/de/lmu/ifi/dbs/elki/utilities/TestBitsUtil.java b/test/de/lmu/ifi/dbs/elki/utilities/TestBitsUtil.java
index 504e5764..ea1bc757 100644
--- a/test/de/lmu/ifi/dbs/elki/utilities/TestBitsUtil.java
+++ b/test/de/lmu/ifi/dbs/elki/utilities/TestBitsUtil.java
@@ -66,6 +66,7 @@ public class TestBitsUtil implements JUnit4Test {
BitsUtil.cycleRightI(test, 6, 8);
assertEquals(BitsUtil.toString(test), "10000010");
assertEquals(BitsUtil.numberOfTrailingZerosSigned(test), 1);
+ assertEquals(BitsUtil.numberOfTrailingZeros(test), 1);
BitsUtil.zeroI(test);
BitsUtil.setI(test, 125);
@@ -86,41 +87,41 @@ public class TestBitsUtil implements JUnit4Test {
final int cnt = 100;
long[] rnds = new long[cnt];
long[][] bits = new long[cnt][];
- for(int i = 0; i < cnt; i++) {
+ for (int i = 0; i < cnt; i++) {
rnds[i] = Math.abs(r.nextLong());
bits[i] = BitsUtil.make(Long.SIZE, rnds[i]);
}
- for(int i = 0; i < cnt; i++) {
- for(int j = 0; j < cnt; j++) {
+ for (int i = 0; i < cnt; i++) {
+ for (int j = 0; j < cnt; j++) {
assertEquals(compare(rnds[i], rnds[j]), BitsUtil.compare(bits[i], bits[j]));
}
}
- for(int i = 0; i < cnt; i++) {
+ for (int i = 0; i < cnt; i++) {
long[] btmp = BitsUtil.copy(bits[i], 64 + r.nextInt(500));
assertEquals(BitsUtil.compare(btmp, bits[i]), 0);
- for(int j = 0; j < cnt; j++) {
+ for (int j = 0; j < cnt; j++) {
assertEquals(compare(rnds[i], rnds[j]), BitsUtil.compare(btmp, bits[j]));
}
}
- for(int i = 0; i < cnt; i++) {
+ for (int i = 0; i < cnt; i++) {
long[] btmp = BitsUtil.truncateI(BitsUtil.copy(bits[i]), 47);
- for(int j = 0; j < cnt; j++) {
+ for (int j = 0; j < cnt; j++) {
assertEquals(compare(rnds[i] & ((1 << 48) - 1), rnds[j]), BitsUtil.compare(btmp, bits[j]));
}
}
- for(int i = 0; i < cnt; i++) {
+ for (int i = 0; i < cnt; i++) {
long[] btmp = BitsUtil.cycleRightI(BitsUtil.copy(bits[i]), 13, Long.SIZE - 32);
long ltmp = BitsUtil.cycleRightC(rnds[i], 13, Long.SIZE - 32);
- for(int j = 0; j < cnt; j++) {
+ for (int j = 0; j < cnt; j++) {
assertEquals(compare(ltmp, rnds[j]), BitsUtil.compare(btmp, bits[j]));
}
}
}
-
+
/**
* Not jet in Java 6. To come in JDK7 as Long.copmare
*
@@ -141,7 +142,8 @@ public class TestBitsUtil implements JUnit4Test {
i = bitset.nextSetBit(i + 1);
}
// Java 7:
- // assertEquals("Bit strings do not agree.", BitsUtil.toString(bitset.toLongArray()), BitsUtil.toString(bituti));
+ // assertEquals("Bit strings do not agree.",
+ // BitsUtil.toString(bitset.toLongArray()), BitsUtil.toString(bituti));
bitset.set(4);
BitsUtil.setI(bituti, 4);
@@ -150,7 +152,8 @@ public class TestBitsUtil implements JUnit4Test {
i = bitset.nextSetBit(i + 1);
}
// Java 7:
- // assertEquals("Bit strings do not agree.", BitsUtil.toString(bitset.toLongArray()), BitsUtil.toString(bituti));
+ // assertEquals("Bit strings do not agree.",
+ // BitsUtil.toString(bitset.toLongArray()), BitsUtil.toString(bituti));
bitset.set(15);
BitsUtil.setI(bituti, 15);
@@ -159,17 +162,21 @@ public class TestBitsUtil implements JUnit4Test {
i = bitset.nextSetBit(i + 1);
}
// Java 7:
- // assertEquals("Bit strings do not agree.", BitsUtil.toString(bitset.toLongArray()), BitsUtil.toString(bituti));
+ // assertEquals("Bit strings do not agree.",
+ // BitsUtil.toString(bitset.toLongArray()), BitsUtil.toString(bituti));
assertEquals(bitset.nextSetBit(0), BitsUtil.nextSetBit(bituti, 0));
assertEquals(bitset.nextSetBit(4), BitsUtil.nextSetBit(bituti, 4));
assertEquals(bitset.nextSetBit(5), BitsUtil.nextSetBit(bituti, 5));
// previousSetBit is not in JDK6.
- // assertEquals(bitset.previousSetBit(64), BitsUtil.previousSetBit(bituti, 64));
- // assertEquals(bitset.previousSetBit(15), BitsUtil.previousSetBit(bituti, 15));
- // assertEquals(bitset.previousSetBit(14), BitsUtil.previousSetBit(bituti, 14));
+ // assertEquals(bitset.previousSetBit(64), BitsUtil.previousSetBit(bituti,
+ // 64));
+ // assertEquals(bitset.previousSetBit(15), BitsUtil.previousSetBit(bituti,
+ // 15));
+ // assertEquals(bitset.previousSetBit(14), BitsUtil.previousSetBit(bituti,
+ // 14));
}
-
+
@Test
public void testGrayCoding() {
long[] bits = BitsUtil.zero(123);
@@ -183,4 +190,23 @@ public class TestBitsUtil implements JUnit4Test {
assertTrue(BitsUtil.get(bits, 122));
assertEquals(1, BitsUtil.cardinality(bits));
}
-} \ No newline at end of file
+
+ @Test
+ public void testLeadingTrailing() {
+ int[] testi = new int[] { 0x7, 0x12345678, 0x23456789, 0x45678900, 0x89000000, 0xFFFF0000 };
+ int[] truli = new int[] { 29, 3, 2, 1, 0, 0 };
+ int[] truti = new int[] { 0, 3, 0, 8, 24, 16 };
+ for (int i = 0; i < testi.length; i++) {
+ assertEquals("Leading zeros don't agree for " + BitsUtil.toString(testi[i]), truli[i], BitsUtil.numberOfLeadingZeros(testi[i]));
+ assertEquals("Trailing zeros don't agree for " + BitsUtil.toString(testi[i]), truti[i], BitsUtil.numberOfTrailingZeros(testi[i]));
+ }
+
+ long[] testl = new long[] { 0x7L, 0x12345678L, 0x23456789L, 0x45678900L, 0x89000000L, 0x1FFFF0000L, 0x123456789ABCDEFL, 0x0011001188008800L };
+ int[] trull = new int[] { 61, 35, 34, 33, 32, 31, 7, 11 };
+ int[] trutl = new int[] { 0, 3, 0, 8, 24, 16, 0, 11 };
+ for (int i = 0; i < testl.length; i++) {
+ assertEquals("Leading zeros don't agree for " + BitsUtil.toString(testl[i]), trull[i], BitsUtil.numberOfLeadingZeros(testl[i]));
+ assertEquals("Trailing zeros don't agree for " + BitsUtil.toString(testl[i]), trutl[i], BitsUtil.numberOfTrailingZeros(testl[i]));
+ }
+ }
+}
diff --git a/test/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/TestDoubleHeap.java b/test/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/TestDoubleHeap.java
new file mode 100644
index 00000000..51663a9a
--- /dev/null
+++ b/test/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/TestDoubleHeap.java
@@ -0,0 +1,91 @@
+package de.lmu.ifi.dbs.elki.utilities.datastructures.heap;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import static org.junit.Assert.assertTrue;
+
+import java.util.Random;
+
+import org.junit.Test;
+
+import de.lmu.ifi.dbs.elki.JUnit4Test;
+
+/**
+ * Perform standard unit tests on the double-indexed heap structures.
+ *
+ * @author Erich Schubert
+ */
+public class TestDoubleHeap implements JUnit4Test {
+ @Test
+ public void testDoubleMinHeap() {
+ Random r = new Random();
+ DoubleMinHeap heap = new DoubleMinHeap();
+ for (int i = 0; i < 1000; i++) {
+ double key = r.nextDouble();
+ heap.add(key);
+ }
+ double cur = Double.NEGATIVE_INFINITY;
+ for (int i = 0; i < 500; i++) {
+ assertTrue("Heap incorrectly ordered!", cur <= heap.peek());
+ cur = heap.peek();
+ heap.poll();
+ }
+ for (int i = 0; i < 10000; i++) {
+ double key = r.nextDouble();
+ heap.add(key);
+ }
+ cur = Double.NEGATIVE_INFINITY;
+ while (heap.size() > 0) {
+ assertTrue("Heap incorrectly ordered!", cur <= heap.peek());
+ cur = heap.peek();
+ heap.poll();
+ }
+ }
+
+ @Test
+ public void testDoubleMaxHeap() {
+ Random r = new Random();
+ DoubleMaxHeap heap = new DoubleMaxHeap();
+ for (int i = 0; i < 1000; i++) {
+ double key = r.nextDouble();
+ heap.add(key);
+ }
+ double cur = Double.POSITIVE_INFINITY;
+ for (int i = 0; i < 500; i++) {
+ assertTrue("Heap incorrectly ordered! "+cur+" < "+heap.peek(), cur >= heap.peek());
+ cur = heap.peek();
+ heap.poll();
+ }
+ for (int i = 0; i < 10000; i++) {
+ double key = r.nextDouble();
+ heap.add(key);
+ }
+ cur = Double.POSITIVE_INFINITY;
+ while (heap.size() > 0) {
+ assertTrue("Heap incorrectly ordered!", cur >= heap.peek());
+ cur = heap.peek();
+ heap.poll();
+ }
+ }
+}
diff --git a/test/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/TestDoubleObjHeaps.java b/test/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/TestDoubleObjHeaps.java
new file mode 100644
index 00000000..b37fc0b4
--- /dev/null
+++ b/test/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/TestDoubleObjHeaps.java
@@ -0,0 +1,91 @@
+package de.lmu.ifi.dbs.elki.utilities.datastructures.heap;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import static org.junit.Assert.assertTrue;
+
+import java.util.Random;
+
+import org.junit.Test;
+
+import de.lmu.ifi.dbs.elki.JUnit4Test;
+
+/**
+ * Perform standard unit tests on the double-object heap structures.
+ *
+ * @author Erich Schubert
+ */
+public class TestDoubleObjHeaps implements JUnit4Test {
+ @Test
+ public void testDoubleObjMinHeap() {
+ Random r = new Random();
+ DoubleObjMinHeap<Double> heap = new DoubleObjMinHeap<Double>();
+ for(int i = 0; i < 1000; i++) {
+ double key = r.nextDouble();
+ heap.add(key, key);
+ }
+ double cur = Double.NEGATIVE_INFINITY;
+ for(int i = 0; i < 500; i++) {
+ assertTrue("Heap incorrectly ordered!", cur <= heap.peekKey());
+ cur = heap.peekKey();
+ heap.poll();
+ }
+ for(int i = 0; i < 10000; i++) {
+ double key = r.nextDouble();
+ heap.add(key, key);
+ }
+ cur = Double.NEGATIVE_INFINITY;
+ while(heap.size() > 0) {
+ assertTrue("Heap incorrectly ordered!", cur <= heap.peekKey());
+ cur = heap.peekKey();
+ heap.poll();
+ }
+ }
+
+ @Test
+ public void testDoubleObjMaxHeap() {
+ Random r = new Random();
+ DoubleObjMaxHeap<Double> heap = new DoubleObjMaxHeap<Double>();
+ for(int i = 0; i < 1000; i++) {
+ double key = r.nextDouble();
+ heap.add(key, key);
+ }
+ double cur = Double.POSITIVE_INFINITY;
+ for(int i = 0; i < 500; i++) {
+ assertTrue("Heap incorrectly ordered!", cur >= heap.peekKey());
+ cur = heap.peekKey();
+ heap.poll();
+ }
+ for(int i = 0; i < 10000; i++) {
+ double key = r.nextDouble();
+ heap.add(key, key);
+ }
+ cur = Double.POSITIVE_INFINITY;
+ while(heap.size() > 0) {
+ assertTrue("Heap incorrectly ordered!", cur >= heap.peekKey());
+ cur = heap.peekKey();
+ heap.poll();
+ }
+ }
+}
diff --git a/test/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/TestHeapPerformance.java b/test/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/TestHeapPerformance.java
index a10d3706..084a3761 100644
--- a/test/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/TestHeapPerformance.java
+++ b/test/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/TestHeapPerformance.java
@@ -64,7 +64,7 @@ public class TestHeapPerformance {
{
for(int j = 0; j < iterations; j++) {
Heap<Integer> pq = new Heap<Integer>();
- testQueue(elements, pq);
+ testHeap(elements, pq);
}
for(int j = 0; j < iterations; j++) {
PriorityQueue<Integer> pq = new PriorityQueue<Integer>(); // 11,
@@ -76,7 +76,7 @@ public class TestHeapPerformance {
{
for(int j = 0; j < iterations; j++) {
Heap<Integer> pq = new Heap<Integer>();
- testQueue(elements, pq);
+ testHeap(elements, pq);
}
}
long htime = System.nanoTime() - hstart;
@@ -94,6 +94,20 @@ public class TestHeapPerformance {
// 1.05 allows some difference in measuring
}
+ private void testHeap(final List<Integer> elements, Heap<Integer> pq) {
+ // Insert all
+ for(int i = 0; i < elements.size(); i++) {
+ pq.add(elements.get(i));
+ }
+ // Poll first half.
+ for(int i = 0; i < elements.size() >> 1; i++) {
+ assertEquals((int) pq.poll(), i);
+ // assertEquals((int) pq.poll(), queueSize - 1 - i);
+ }
+ assertTrue("Heap not half-empty?", pq.size() == (elements.size() >> 1));
+ pq.clear();
+ }
+
private void testQueue(final List<Integer> elements, Queue<Integer> pq) {
// Insert all
for(int i = 0; i < elements.size(); i++) {
diff --git a/test/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/TestTiedTopBoundedUpdatableHeap.java b/test/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/TestTiedTopBoundedUpdatableHeap.java
index b5ad9b2c..e469bfca 100644
--- a/test/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/TestTiedTopBoundedUpdatableHeap.java
+++ b/test/de/lmu/ifi/dbs/elki/utilities/datastructures/heap/TestTiedTopBoundedUpdatableHeap.java
@@ -51,7 +51,7 @@ public class TestTiedTopBoundedUpdatableHeap implements JUnit4Test {
int score = r.nextInt(10000);
IntegerPriorityObject<Integer> nobj = new IntegerPriorityObject<Integer>(score, id);
// Update heap
- heap.offer(nobj);
+ heap.add(nobj);
// Enabling the followig assertion *hides* certain problems!
// assertTrue("Heap not valid at i="+i, heap.checkHeap());
// Update simulation
diff --git a/test/de/lmu/ifi/dbs/elki/math/histograms/TestReplacingHistogram.java b/test/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/TestDoubleHistogram.java
index 455a880a..51a9836b 100644
--- a/test/de/lmu/ifi/dbs/elki/math/histograms/TestReplacingHistogram.java
+++ b/test/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/TestDoubleHistogram.java
@@ -1,4 +1,4 @@
-package de.lmu.ifi.dbs.elki.math.histograms;
+package de.lmu.ifi.dbs.elki.utilities.datastructures.histogram;
/*
This file is part of ELKI:
@@ -29,46 +29,43 @@ import static org.junit.Assert.assertEquals;
import org.junit.Test;
import de.lmu.ifi.dbs.elki.JUnit4Test;
-import de.lmu.ifi.dbs.elki.utilities.pairs.DoubleObjPair;
/**
* JUnit test to test the {@link ReplacingHistogram} class.
*
* @author Erich Schubert
*/
-public class TestReplacingHistogram implements JUnit4Test {
- ReplacingHistogram<Double> hist;
-
+public class TestDoubleHistogram implements JUnit4Test {
/**
* Test that adds some data to the histogram and compares results.
*/
@Test
public final void testHistogram() {
- Double[] initial = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
- Double[] filled = { 0.0, 1.23, 4.56, 7.89, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
- Double[] changed = { 0.0, 1.35, 8.01, 14.67, 9.01, 2.34, 0.0, 0.0, 0.0, 0.0 };
- Double[] resized = { -1.23, 0.0, 0.0, 1.35, 8.01, 14.67, 9.01, 2.34, 0.0, 0.0, 0.0, 0.0, 0.0, -4.56 };
- hist = ReplacingHistogram.DoubleHistogram(10, 0.0, 1.0);
- assertArrayEquals("Empty histogram doesn't match", initial, hist.getData().toArray(new Double[0]));
- hist.replace(0.15, 1.23);
- hist.replace(0.25, 4.56);
- hist.replace(0.35, 7.89);
- assertArrayEquals("Filled histogram doesn't match", filled, hist.getData().toArray(new Double[0]));
- hist.replace(0.15, 0.12 + hist.get(0.15));
- hist.replace(0.25, 3.45 + hist.get(0.25));
- hist.replace(0.35, 6.78 + hist.get(0.35));
- hist.replace(0.45, 9.01 + hist.get(0.45));
- hist.replace(0.50, 2.34 + hist.get(0.50));
- assertArrayEquals("Changed histogram doesn't match", changed, hist.getData().toArray(new Double[0]));
- hist.replace(-.13, -1.23 + hist.get(-.13));
- hist.replace(1.13, -4.56 + hist.get(1.13));
- assertArrayEquals("Resized histogram doesn't match", resized, hist.getData().toArray(new Double[0]));
+ double[] initial = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
+ double[] filled = { 0.0, 1.23, 4.56, 7.89, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
+ double[] changed = { 0.0, 1.35, 8.01, 14.67, 9.01, 2.34, 0.0, 0.0, 0.0, 0.0 };
+ double[] resized = { -1.23, 0.0, 0.0, 1.35, 8.01, 14.67, 9.01, 2.34, 0.0, 0.0, 0.0, 0.0, 0.0, -4.56 };
+ DoubleStaticHistogram hist = new DoubleStaticHistogram(10, 0.0, 1.0);
+ assertArrayEquals("Empty histogram doesn't match", initial, hist.data, 1E-15);
+ hist.increment(0.15, 1.23);
+ hist.increment(0.25, 4.56);
+ hist.increment(0.35, 7.89);
+ assertArrayEquals("Filled histogram doesn't match", filled, hist.data, 1E-15);
+ hist.increment(0.15, 0.12);
+ hist.increment(0.25, 3.45);
+ hist.increment(0.35, 6.78);
+ hist.increment(0.45, 9.01);
+ hist.increment(0.50, 2.34);
+ assertArrayEquals("Changed histogram doesn't match", changed, hist.data, 1E-15);
+ hist.increment(-.13, -1.23);
+ hist.increment(1.13, -4.56);
+ assertArrayEquals("Resized histogram doesn't match", resized, hist.data, 1E-15);
// compare results via Iterator.
int off = 0;
- for(DoubleObjPair<Double> pair : hist) {
- assertEquals("Array iterator bin position", -0.15 + 0.1 * off, pair.first, 0.00001);
- assertEquals("Array iterator bin contents", resized[off], pair.getSecond(), 0.00001);
+ for (DoubleStaticHistogram.Iter iter = hist.iter(); iter.valid(); iter.advance()) {
+ assertEquals("Array iterator bin position", -0.15 + 0.1 * off, iter.getCenter(), 0.00001);
+ assertEquals("Array iterator bin contents", resized[off], iter.getValue(), 0.00001);
off++;
}
}
diff --git a/test/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/TestFlexiHistogram.java b/test/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/TestFlexiHistogram.java
new file mode 100644
index 00000000..9750977d
--- /dev/null
+++ b/test/de/lmu/ifi/dbs/elki/utilities/datastructures/histogram/TestFlexiHistogram.java
@@ -0,0 +1,159 @@
+package de.lmu.ifi.dbs.elki.utilities.datastructures.histogram;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import java.util.Random;
+
+import org.junit.Test;
+
+import de.lmu.ifi.dbs.elki.JUnit4Test;
+
+/**
+ * JUnit test to test the {@link AbstractObjDynamicHistogram} class.
+ *
+ * @author Erich Schubert
+ */
+public class TestFlexiHistogram implements JUnit4Test {
+ /**
+ * Test that adds some data to the histogram and compares results.
+ */
+ @Test
+ public final void testObjHistogram() {
+ Double[] filled = { 0.0, 1.23, 4.56, 7.89, 0.0, null, null, null, null, null };
+ Double[] changed = { 0.0, 1.35, 8.01, 14.67, 9.01, 2.34, null, null, null, null };
+ Double[] resized = { -1.23, 1.35, 22.68, 11.35, 0.0, 0.0, -4.56, null, null, null };
+ Double[] expanded = { 1., 0.0, 0.0, 0.0, 0.0, 0.0, 29.59, null, null, null };
+ AbstractObjDynamicHistogram<Double> hist = new AbstractObjDynamicHistogram<Double>(5) {
+ @Override
+ public Double aggregate(Double first, Double second) {
+ return Double.valueOf(first.doubleValue() + second.doubleValue());
+ }
+
+ @Override
+ protected Double downsample(Object[] data, int start, int end, int size) {
+ double sum = 0;
+ for (int i = start; i < end; i++) {
+ final Double val = (Double) data[i];
+ if (val != null) {
+ sum += val;
+ }
+ }
+ return Double.valueOf(sum);
+ }
+
+ @Override
+ protected Double makeObject() {
+ return Double.valueOf(0.);
+ }
+
+ @Override
+ protected Double cloneForCache(Double data) {
+ return data;
+ }
+ };
+ hist.putData(0.0, 0.0);
+ hist.putData(0.15, 1.23);
+ hist.putData(0.25, 4.56);
+ hist.putData(0.35, 7.89);
+ hist.putData(0.5, 0.0);
+ hist.materialize();
+ assertArrayEquals("Filled histogram doesn't match", filled, hist.data);
+ hist.putData(0.15, 0.12);
+ hist.putData(0.25, 3.45);
+ hist.putData(0.35, 6.78);
+ hist.putData(0.45, 9.01);
+ hist.putData(0.55, 2.34);
+ assertArrayEquals("Changed histogram doesn't match", changed, hist.data);
+ hist.putData(-.13, -1.23);
+ hist.putData(1.13, -4.56);
+ assertArrayEquals("Resized histogram doesn't match", resized, hist.data);
+
+ // compare results via Iterator.
+ int off = 0;
+ for (ObjHistogram.Iter<Double> iter = hist.iter(); iter.valid(); iter.advance()) {
+ assertEquals("Array iterator bin position", -0.1 + 0.2 * off, iter.getCenter(), 0.00001);
+ assertEquals("Array iterator bin contents", resized[off], iter.getValue(), 0.00001);
+ off++;
+ }
+
+ // totally break out of the data range
+ hist.putData(-10., 1.);
+ assertArrayEquals("Expanded histogram doesn't match", expanded, hist.data);
+
+ // Try some random operations, too
+ Random r = new Random(0);
+ for (int i = 0; i < 1000; i++) {
+ hist.putData((r.nextDouble() - .5) * i * i, i * .1);
+ }
+ }
+
+ /**
+ * Test that adds some data to the histogram and compares results.
+ */
+ @Test
+ public final void testDoubleHistogram() {
+ double[] filled = { 0.0, 1.23, 4.56, 7.89, 0.0, 0, 0, 0, 0, 0 };
+ double[] changed = { 0.0, 1.35, 8.01, 14.67, 9.01, 2.34, 0, 0, 0, 0 };
+ double[] resized = { -1.23, 1.35, 22.68, 11.35, 0.0, 0.0, -4.56, 0, 0, 0 };
+ double[] expanded = { 1., 0.0, 0.0, 0.0, 0.0, 0.0, 29.59, 0, 0, 0 };
+ DoubleDynamicHistogram hist = new DoubleDynamicHistogram(5);
+ hist.increment(0.0, 0.0);
+ hist.increment(0.15, 1.23);
+ hist.increment(0.25, 4.56);
+ hist.increment(0.35, 7.89);
+ hist.increment(0.5, 0.0);
+ hist.materialize();
+ assertArrayEquals("Filled histogram doesn't match", filled, hist.data, 1E-15);
+ hist.increment(0.15, 0.12);
+ hist.increment(0.25, 3.45);
+ hist.increment(0.35, 6.78);
+ hist.increment(0.45, 9.01);
+ hist.increment(0.55, 2.34);
+ assertArrayEquals("Changed histogram doesn't match", changed, hist.data, 1E-15);
+ hist.increment(-.13, -1.23);
+ hist.increment(1.13, -4.56);
+ assertArrayEquals("Resized histogram doesn't match", resized, hist.data, 1E-15);
+
+ // compare results via Iterator.
+ int off = 0;
+ for (DoubleHistogram.Iter iter = hist.iter(); iter.valid(); iter.advance()) {
+ assertEquals("Array iterator bin position", -0.1 + 0.2 * off, iter.getCenter(), 0.00001);
+ assertEquals("Array iterator bin contents", resized[off], iter.getValue(), 0.00001);
+ off++;
+ }
+
+ // totally break out of the data range
+ hist.increment(-10., 1.);
+ assertArrayEquals("Expanded histogram doesn't match", expanded, hist.data, 1E-15);
+
+ // Try some random operations, too
+ Random r = new Random(0);
+ for (int i = 0; i < 1000; i++) {
+ hist.increment((r.nextDouble() - .5) * i * i, i * .1);
+ }
+ }
+}