001    /*
002     * Copyright (c) 1998-2014 ChemAxon Ltd. All Rights Reserved.
003     *
004     * This software is the confidential and proprietary information of
005     * ChemAxon. You shall not disclose such Confidential Information
006     * and shall use it only in accordance with the terms of the agreements
007     * you entered into with ChemAxon.
008     *
009     */
010    package com.chemaxon.overlap;
011    
012    import com.google.common.annotations.Beta;
013    
014    /**
015     * Arrays of primitives backed implementation of {@link KnnResults}.
016     *
017     * <p>
018     * Note that query and target IDs might refer to the same or different sets. If they refer to the same set the nearest
019     * neighbor list does not contain self ID.</p>
020     *
021     * @author Gabor Imre
022     */
023    @Beta
024    public final class KnnResultsImpl implements KnnResults {
025    
026        /**
027         * Serial version.
028         */
029        private static final long serialVersionUID = 0;
030    
031        /**
032         * Number of nearest neighbor IDs stored.
033         */
034        private final int k;
035    
036        /**
037         * Array of query IDs.
038         */
039        private final int[] queries;
040    
041        /**
042         * Concatenated kNN lists.
043         *
044         * <p>
045         * First item is the nearest neighbor of the first query (having index [0] and ID {@link #queries}[0]; Item n+1
046         * (having index n]) is the nearest neighbor of the second query (having index [1] and ID {@link #queries}[1])</p>
047         */
048        private final int[] targets;
049    
050        /**
051         * Array of dissimilarities.
052         *
053         * <p>
054         * Items are associate to the contents of {@link #targets} array.</p>
055         */
056        private final double[] dissimilarities;
057    
058        /**
059         * Construct.
060         *
061         * @param k Number of nearest neighbors recorded for each query
062         * @param queries Query IDs
063         * @param targets Concatenated lists of nearest neighbors
064         * @param dissimilarities Dissimilarities
065         * @throws IllegalArgumentException when array sizes mismatch or dissimilarities array contains NaN or INF value
066         */
067        public KnnResultsImpl(int k, int[] queries, int[] targets, double[] dissimilarities) {
068            if (queries.length * k != targets.length || targets.length != dissimilarities.length) {
069                throw new IllegalArgumentException("Sizes mismatch k: " + k + " q: " + queries.length
070                        + " t: " + targets.length + " d: " + dissimilarities.length);
071            }
072    
073            // Check for NaN/inf values
074            for (int i = 0; i < dissimilarities.length; i++) {
075                if (Double.isNaN(dissimilarities[i]) || Double.isInfinite(dissimilarities[i])) {
076                    throw new IllegalArgumentException("Illegal triplet: q: " + queries[i] + " t: " + targets[i]
077                            + " d: " + dissimilarities[i]);
078                }
079            }
080    
081            // Check for ordering
082            for (int i = 0; i < queries.length; i++) {
083                for (int j = 0; j < k - 1; j++) {
084                    if (dissimilarities[i * k + j] > dissimilarities[i * k + j + 1]) {
085                        throw new IllegalArgumentException("Dissimilarity order mismatch");
086                    }
087                }
088            }
089    
090            this.k = k;
091            this.queries = queries.clone();
092            this.targets = targets.clone();
093            this.dissimilarities = dissimilarities.clone();
094        }
095    
096    
097        @Override
098        public int getQuery(int index) {
099            return this.queries[index];
100        }
101    
102    
103        @Override
104        public String toString() {
105            return "kNN results k: " + this.k + " queries: " + this.queries.length;
106        }
107    
108        @Override
109        public int getK() {
110            return this.k;
111        }
112    
113        @Override
114        public int getQueryCount() {
115            return this.queries.length;
116        }
117    
118        @Override
119        public int[] getNeighbors(int index) {
120            final int[] ret = new int[this.k];
121            System.arraycopy(this.targets, index * this.k, ret, 0, this.k);
122            return ret;
123        }
124    
125        @Override
126        public double[] getDissimilarities(int index) {
127            final double[] ret = new double[this.k];
128            System.arraycopy(this.dissimilarities, index * this.k, ret, 0, this.k);
129            return ret;
130        }
131    
132        /**
133         * Low level array copy to target arrays.
134         *
135         * @param k Target k
136         * @param startIndex First query index to write
137         * @param q qurey index array
138         * @param t concatenated knn lists
139         * @param d dissimilarities
140         * @throws IllegalArgumentException when trivial consistency checks fail
141         */
142        public void copyTo(int k, int startIndex, int[] q, int[] t, double[] d) {
143            if (k != this.k) {
144                throw new IllegalArgumentException("K mismatch. got: " + k + ", expected: " + this.k);
145            }
146            if (t.length != q.length * k || t.length != d.length) {
147                throw new IllegalArgumentException("Array size mismatch. q: " + q.length + " expected t/d: " + q.length * k
148                        + "got t: " + t.length + ", d: " + d.length);
149            }
150            System.arraycopy(this.queries, 0, q, startIndex, this.queries.length);
151            System.arraycopy(this.targets, 0, t, startIndex * k, this.queries.length * k);
152            System.arraycopy(this.dissimilarities, 0, d, startIndex * k, this.queries.length * k);
153        }
154    
155    }