package de.lmu.ifi.dbs.elki.result.optics; /* 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 . */ import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; 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.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.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.distance.distancevalue.Distance; import de.lmu.ifi.dbs.elki.result.BasicResult; import de.lmu.ifi.dbs.elki.result.IterableResult; import de.lmu.ifi.dbs.elki.result.OrderingResult; import de.lmu.ifi.dbs.elki.result.ResultAdapter; import de.lmu.ifi.dbs.elki.result.ResultHierarchy; import de.lmu.ifi.dbs.elki.utilities.iterator.IterableIterator; import de.lmu.ifi.dbs.elki.utilities.iterator.IterableIteratorAdapter; import de.lmu.ifi.dbs.elki.utilities.iterator.IterableUtil; /** * Class to store the result of an ordering clustering algorithm such as OPTICS. * * @author Erich Schubert * * @apiviz.landmark * * @apiviz.has ClusterOrderEntry oneway - - contains * @apiviz.composedOf ClusterOrderResult.ClusterOrderAdapter * @apiviz.composedOf ClusterOrderResult.ReachabilityDistanceAdapter * @apiviz.composedOf ClusterOrderResult.PredecessorAdapter * * @param distance type. */ public class ClusterOrderResult> extends BasicResult implements IterableResult> { /** * Cluster order storage */ private ArrayList> clusterOrder; /** * Map of object IDs to their cluster order entry */ private HashMap> map; /** * The DBIDs we are defined for */ ModifiableDBIDs dbids; /** * Constructor * * @param name The long name (for pretty printing) * @param shortname the short name (for filenames etc.) */ public ClusterOrderResult(String name, String shortname) { super(name, shortname); clusterOrder = new ArrayList>(); map = new HashMap>(); dbids = DBIDUtil.newHashSet(); addChildResult(new ClusterOrderAdapter(clusterOrder)); addChildResult(new ReachabilityDistanceAdapter(map, dbids)); addChildResult(new PredecessorAdapter(map, dbids)); } /** * Retrieve the complete cluster order. * * @return cluster order */ public List> getClusterOrder() { return clusterOrder; } /** * The cluster order is iterable */ @Override public Iterator> iterator() { return clusterOrder.iterator(); } /** * Add an object to the cluster order. * * @param id Object ID * @param predecessor Predecessor ID * @param reachability Reachability distance */ public void add(DBID id, DBID predecessor, D reachability) { add(new GenericClusterOrderEntry(id, predecessor, reachability)); dbids.add(id); } /** * Add an object to the cluster order. * * @param ce Entry */ public void add(ClusterOrderEntry ce) { clusterOrder.add(ce); map.put(ce.getID(), ce); dbids.add(ce.getID()); } /** * Get the distance class * * @return distance class. Can be {@code null} for an all-undefined result! */ public Class getDistanceClass() { for(ClusterOrderEntry ce : clusterOrder) { D dist = ce.getReachability(); if(dist != null) { return dist.getClass(); } } return null; } /** * Ordering part of the result. * * @author Erich Schubert */ class ClusterOrderAdapter implements OrderingResult, ResultAdapter { /** * Access reference. */ private ArrayList> clusterOrder; /** * Constructor. * * @param clusterOrder order to return */ public ClusterOrderAdapter(final ArrayList> clusterOrder) { super(); this.clusterOrder = clusterOrder; } @Override public DBIDs getDBIDs() { return dbids; } /** * Use the cluster order to sort the given collection ids. * * Implementation of the {@link OrderingResult} interface. */ @Override public IterableIterator iter(DBIDs ids) { ArrayModifiableDBIDs res = DBIDUtil.newArray(ids.size()); for(ClusterOrderEntry e : clusterOrder) { if(ids.contains(e.getID())) { res.add(e.getID()); } } // TODO: elements in ids that are not in clusterOrder are lost! return new IterableIteratorAdapter(res); } @Override public String getLongName() { return "Derived Object Order"; } @Override public String getShortName() { return "clusterobjectorder"; } } /** * Result containing the reachability distances. * * @author Erich Schubert */ class ReachabilityDistanceAdapter implements Relation, ResultAdapter { /** * Access reference. */ private HashMap> map; /** * DBIDs */ private DBIDs dbids; /** * Constructor. * * @param map Map that stores the results. * @param dbids DBIDs we are defined for. */ public ReachabilityDistanceAdapter(HashMap> map, DBIDs dbids) { super(); this.map = map; this.dbids = dbids; } @Override public D get(DBID objID) { return map.get(objID).getReachability(); } @Override public String getLongName() { return "Reachability"; } @Override public String getShortName() { return "reachability"; } @Override public DBIDs getDBIDs() { return DBIDUtil.makeUnmodifiable(dbids); } @Override public IterableIterator iterDBIDs() { return IterableUtil.fromIterator(dbids.iterator()); } @Override public int size() { return dbids.size(); } @Override public Database getDatabase() { return null; // FIXME } @Override public void set(DBID id, D val) { throw new UnsupportedOperationException(); } @Override public void delete(DBID id) { throw new UnsupportedOperationException(); } @Override public SimpleTypeInformation getDataTypeInformation() { return new SimpleTypeInformation(Distance.class); } @Override public ResultHierarchy getHierarchy() { return ClusterOrderResult.this.getHierarchy(); } @Override public void setHierarchy(ResultHierarchy hierarchy) { ClusterOrderResult.this.setHierarchy(hierarchy); } } /** * Result containing the predecessor ID. * * @author Erich Schubert */ class PredecessorAdapter implements Relation, ResultAdapter { /** * Access reference. */ private HashMap> map; /** * Database IDs */ private DBIDs dbids; /** * Constructor. * * @param map Map that stores the results. * @param dbids DBIDs we are defined for */ public PredecessorAdapter(HashMap> map, DBIDs dbids) { super(); this.map = map; this.dbids = dbids; } @Override public DBID get(DBID objID) { return map.get(objID).getPredecessorID(); } @Override public String getLongName() { return "Predecessor"; } @Override public String getShortName() { return "predecessor"; } @Override public DBIDs getDBIDs() { return DBIDUtil.makeUnmodifiable(dbids); } @Override public IterableIterator iterDBIDs() { return IterableUtil.fromIterator(dbids.iterator()); } @Override public int size() { return dbids.size(); } @Override public Database getDatabase() { return null; // FIXME } @Override public void set(DBID id, DBID val) { throw new UnsupportedOperationException(); } @Override public void delete(DBID id) { throw new UnsupportedOperationException(); } @Override public SimpleTypeInformation getDataTypeInformation() { return TypeUtil.DBID; } @Override public ResultHierarchy getHierarchy() { return ClusterOrderResult.this.getHierarchy(); } @Override public void setHierarchy(ResultHierarchy hierarchy) { ClusterOrderResult.this.setHierarchy(hierarchy); } } }