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    
011    package com.chemaxon.descriptors.metrics;
012    
013    
014    import com.chemaxon.apidiscovery.Validators;
015    import com.chemaxon.apidiscovery.annotations.BuilderClass;
016    import com.chemaxon.apidiscovery.annotations.Description;
017    import com.chemaxon.apidiscovery.annotations.Parameter;
018    import com.chemaxon.apidiscovery.annotations.Parametrized;
019    import com.chemaxon.apidiscovery.interfaces.ParameterBuilder;
020    import com.chemaxon.common.annotations.PublicAPI;
021    import com.google.common.annotations.Beta;
022    import java.io.Serializable;
023    
024    /**
025     * Parameters for Tversky index.
026     *
027     * <p>Please note that this class is marked with @Beta annotation, so it can be subject of incompatible changes or
028     * removal in later releases.</p>
029     *
030     * @see <a href="http://en.wikipedia.org/wiki/Tversky_index">http://en.wikipedia.org/wiki/Tversky_index</a>
031     *
032     * @author Gabor Imre
033     */
034    @Description(
035            shortName = "Tversky",
036            name = "Tversky index",
037            description = "Tversky index is a parametrizable metric which symmetry or other"
038                + " metric properties depends on the actual parameter values.")
039    @BuilderClass(builderClass = TverskyParameters.Builder.class)
040    @Beta
041    @PublicAPI
042    public class TverskyParameters implements MetricMetadata, BinaryVectorComparator, Serializable {
043    
044        /**
045         * Serial version.
046         */
047        private static final long serialVersionUID = 0;
048    
049        /**
050         * Coefficient for target only part.
051         */
052        private final double coeffTarget;
053    
054        /**
055         * Coefficient for query only part.
056         */
057        private final double coeffQuery;
058    
059        /**
060         * Instantiate with builder's actual state.
061         *
062         * @param builder
063         *            State to represent
064         */
065        protected TverskyParameters(TverskyParameters.Builder builder) {
066            this.coeffTarget = builder.coeffTarget;
067            this.coeffQuery = builder.coeffQuery;
068        }
069    
070        /**
071         * Instantiate with default values.
072         */
073        public TverskyParameters() {
074            this(new TverskyParameters.Builder());
075        }
076    
077        @Override
078        public boolean isSymmetric() {
079            return this.coeffTarget == this.coeffQuery;
080        }
081    
082        @Override
083        public boolean isNonNegative() {
084            return true;
085        }
086    
087        @Override
088        public boolean isDissimilarityZeroIFFEquals() {
089            return this.coeffTarget > 0 && this.coeffQuery > 0;
090        }
091    
092        @Override
093        public boolean isTriangeInequalityHolds() {
094            return this.coeffTarget == 1.0 && this.coeffQuery == 1.0;
095        }
096    
097        @Override
098        public boolean isMetricSpace() {
099            return isSymmetric() && isNonNegative() && isDissimilarityZeroIFFEquals()
100                    && isTriangeInequalityHolds();
101        }
102    
103        @Override
104        public double calculateBinaryVectorDissimilarity(int bitCount, int targetOnly, int queryOnly, int common) {
105            BinaryVectorComparator.Util.checkBitCounts(bitCount, targetOnly, queryOnly, common);
106            if (common == 0) {
107                return 1.0;
108            } else {
109                return 1.0 - (double) common / (this.coeffTarget * targetOnly + this.coeffQuery * queryOnly + common);
110            }
111        }
112    
113        @Override
114        public double calculateBinaryVectorSimilarity(int bitCount, int targetOnly, int queryOnly, int common) {
115            BinaryVectorComparator.Util.checkBitCounts(bitCount, targetOnly, queryOnly, common);
116    
117            if (common == 0) {
118                return 0.0;
119            } else {
120                return (double) common / (this.coeffTarget * targetOnly + this.coeffQuery * queryOnly + common);
121            }
122        }
123    
124        @Override
125        public double getLowerBoundForBinaryVectors(int bitCount) {
126            return 0.0;
127        }
128    
129        @Override
130        public double getUpperBoundForBinaryVectors(int bitCount) {
131            return 1.0;
132        }
133    
134        /**
135         * Query side coefficient.
136         *
137         * @return Query coefficient
138         */
139        public double getCoeffQuery() {
140            return this.coeffQuery;
141        }
142    
143        /**
144         * Target side coefficient.
145         *
146         * @return Target coefficient
147         */
148        public double getCoeffTarget() {
149            return this.coeffTarget;
150        }
151    
152        @Override
153        public String toString() {
154            return "Tversky (coeffTarget = " + this.coeffTarget + " coeffQuery = " + this.coeffQuery + ")";
155        }
156    
157        /**
158         * Builder for parameter object.
159         */
160        @Parametrized
161        @Beta
162        public static class Builder implements ParameterBuilder<TverskyParameters> {
163    
164            /**
165             * Target coefficient.
166             */
167            @Parameter(order = 1, validateWith = { Validators.UnitInterval.class })
168            @Description(
169                    shortName = "coeffT",
170                    name = "Target-only side coefficient (alpha)",
171                    description = "Coefficient of the target-only part, usually denoted as alpha.")
172            private double coeffTarget;
173    
174            /**
175             * Query coefficient.
176             */
177            @Parameter(order = 2, validateWith = { Validators.UnitInterval.class })
178            @Description(
179                    shortName = "coeffQ",
180                    name = "Query-only side coefficient (beta)",
181                    description = "Coefficient of the query-only part, usually denoted as beta.")
182            private double coeffQuery;
183    
184            /**
185             * Instantiate builder with default values.
186             */
187            public Builder() {
188                this.coeffTarget = 1.0;
189                this.coeffQuery = 1.0;
190            }
191    
192            /**
193             * Instantiate builder based on an actual parameter instance.
194             *
195             * @param parameters Parameters to use
196             */
197            public Builder(TverskyParameters parameters) {
198                this.coeffQuery = parameters.coeffQuery;
199                this.coeffTarget = parameters.coeffTarget;
200            }
201    
202            /**
203             * Set target coefficient.
204             *
205             * @param coeffTarget
206             *            Target coefficient
207             * @return Reference to this
208             */
209            public Builder coeffTarget(double coeffTarget) {
210                if (coeffTarget < 0) {
211                    throw new IllegalArgumentException("Target coefficient (" + coeffTarget
212                            + ") must be nonnegative.");
213                }
214                this.coeffTarget = coeffTarget;
215                return this;
216            }
217    
218            /**
219             * Set query coefficient.
220             *
221             * @param coeffQuery
222             *            Query coefficient
223             * @return Reference to this
224             */
225            public Builder coeffQuery(double coeffQuery) {
226                if (coeffQuery < 0) {
227                    throw new IllegalArgumentException("Query coefficient (" + coeffQuery
228                            + ") must be nonnegative.");
229                }
230                this.coeffQuery = coeffQuery;
231                return this;
232            }
233    
234            /**
235             * Build immutable parameter object.
236             *
237             * @return Parameter object
238             */
239            public TverskyParameters build() {
240                return new TverskyParameters(this);
241            }
242        }
243    
244    }