/* * Seldon -- open source prediction engine * ======================================= * Copyright 2011-2015 Seldon Technologies Ltd and Rummble Ltd (http://www.seldon.io/) * ********************************************************************************************** * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ********************************************************************************************** */ package io.seldon.client.algorithm; import java.io.Serializable; import java.util.ArrayList; import java.util.Date; import java.util.List; import org.apache.log4j.Logger; public class CFAlgorithm implements Cloneable,Serializable { public enum CF_RECOMMENDER { RELEVANCE, TRUST_ITEMBASED, BEST_PREDICTION, SEMANTIC_VECTORS_USER_KNN, SEMANTIC_VECTORS_ITEM_KNN, MAHOUT_ITEMBASED, GRAPHLAB_PMF, MAHOUT_ALS, MAHOUT_FPGROWTH, MOST_POPULAR, CLUSTER_COUNTS, CLUSTER_COUNTS_DYNAMIC, CLUSTER_COUNTS_GLOBAL, SEMANTIC_VECTORS_SORT} public enum CF_PREDICTOR { RESNICK, RESNICK_ITEM, RESNICK_SARIC, WEIGHTED_MEDIAN, WEIGHTED_MEAN, SEMANTIC_VECTORS, MAHOUT_ALS, GRAPHLAB_PMF, USER_AVG, ITEM_AVG, NAIVE_BAYES, MID_RATING, MAX_RATING } public enum CF_SORTER { RELEVANCE, TAG_SIMILARITY, DEMOGRAPHICS, MAHOUT_FPGROWTH, SEMANTIC_VECTORS, CLUSTER_COUNTS, CLUSTER_COUNTS_DYNAMIC, NOOP, MOST_POPULAR_MEMBASED, MOST_POPULAR_WEIGHTED_MEMBASED, MOST_RECENT_MEMBASED, MOST_POP_RECENT_MEMBASED, WEB_SIMILARITY, COOCCURRENCE, STORM_TRUST} public enum CF_ITEM_COMPARATOR { SEMANTIC_VECTORS, TRUST_ITEM, MAHOUT_ITEM } public enum CF_STRATEGY { FIRST_SUCCESSFUL, WEIGHTED, RANK_SUM, ADD_MISSING } public enum CF_POSTPROCESSING { NONE, TIME_HITS, HITS, HITS_WEIGHTED, WEB_SIMILARITY } private static Logger logger = Logger.getLogger(CFAlgorithm.class.getName()); private List<CF_ITEM_COMPARATOR> itemComparators = new ArrayList<CF_ITEM_COMPARATOR>(); private CF_STRATEGY itemComparatorStrategy = CF_STRATEGY.FIRST_SUCCESSFUL; private List<CF_RECOMMENDER> recommenders = new ArrayList<CF_RECOMMENDER>(); private CF_STRATEGY recommenderStrategy = CF_STRATEGY.FIRST_SUCCESSFUL; private List<CF_PREDICTOR> predictors = new ArrayList<CF_PREDICTOR>(); private CF_STRATEGY predictorStrategy = CF_STRATEGY.FIRST_SUCCESSFUL; private List<CF_SORTER> sorters = new ArrayList<CF_SORTER>(); private CF_STRATEGY sorterStrategy = CF_STRATEGY.FIRST_SUCCESSFUL; private CF_POSTPROCESSING postprocessing = CF_POSTPROCESSING.NONE; private Date date; // base algorithm to run as if from this date private Date recommendAfter; // only provide recommendations for things created after this date private String name; private double minRating = 0; private double maxRating = 10; private int userCFLimit = 50; private int transactionActionType = 0; // the action_type that defines transactions (purchases, page views) // Recommendation parameters private int recommendK = 50; private double recommendMinTrust = 0.7; private int recommendationCachingTimeSecs = 600; // Prediction parameters private int predictK = 50; private int predictNeighbours = 500; private double predictMinTrust = 0.7; //Semantic Vector parameters private int minNumTxsForSV = 5; private int txHistorySizeForSV = 10; //Cluster parameters double longTermWeight = 1.0D; double shortTermWeight = 1.0D; double decayRateSecs = 3600; // ~ END field ~ public CFAlgorithm() { } // accessors public List<CF_RECOMMENDER> getRecommenders() { return recommenders; } public void setRecommenders(List<CF_RECOMMENDER> recommenders) { this.recommenders = recommenders; } public CF_STRATEGY getRecommenderStrategy() { return recommenderStrategy; } public void setRecommenderStrategy(CF_STRATEGY recommenderStrategy) { this.recommenderStrategy = recommenderStrategy; } public List<CF_PREDICTOR> getPredictors() { return predictors; } public void setPredictors(List<CF_PREDICTOR> predictors) { this.predictors = predictors; } public CF_STRATEGY getPredictorStrategy() { return predictorStrategy; } public void setPredictorStrategy(CF_STRATEGY predictorStrategy) { this.predictorStrategy = predictorStrategy; } public List<CF_SORTER> getSorters() { return sorters; } public void setSorters(List<CF_SORTER> sorters) { this.sorters = sorters; } public CF_STRATEGY getSorterStrategy() { return sorterStrategy; } public void setSorterStrategy(CF_STRATEGY sorterStrategy) { this.sorterStrategy = sorterStrategy; } public List<CF_ITEM_COMPARATOR> getItemComparators() { return itemComparators; } public void setItemComparators(List<CF_ITEM_COMPARATOR> itemComparators) { this.itemComparators = itemComparators; } public CF_STRATEGY getItemComparatorStrategy() { return itemComparatorStrategy; } public void setItemComparatorStrategy(CF_STRATEGY itemComparatorStrategy) { this.itemComparatorStrategy = itemComparatorStrategy; } public CF_POSTPROCESSING getPostprocessing() { return postprocessing; } public void setPostprocessing(CF_POSTPROCESSING postprocessing) { this.postprocessing = postprocessing; } public Date getDate() { return date; } public void setDate(Date date) { this.date = date; } public Date getRecommendAfter() { return recommendAfter; } public void setRecommendAfter(Date recommendAfter) { this.recommendAfter = recommendAfter; } public String getName() { return name; } public void setName(String name) { this.name = name; } public double getMinRating() { return minRating; } public void setMinRating(double minRating) { this.minRating = minRating; } public double getMaxRating() { return maxRating; } public void setMaxRating(double maxRating) { this.maxRating = maxRating; } public int getUserCFLimit() { return userCFLimit; } public void setUserCFLimit(int userCFLimit) { this.userCFLimit = userCFLimit; } public int getTransactionActionType() { return transactionActionType; } public void setTransactionActionType(int transactionActionType) { this.transactionActionType = transactionActionType; } public int getRecommendK() { return recommendK; } public void setRecommendK(int recommendK) { this.recommendK = recommendK; } public double getRecommendMinTrust() { return recommendMinTrust; } public void setRecommendMinTrust(double recommendMinTrust) { this.recommendMinTrust = recommendMinTrust; } public int getPredictK() { return predictK; } public void setPredictK(int predictK) { this.predictK = predictK; } public int getPredictNeighbours() { return predictNeighbours; } public void setPredictNeighbours(int predictNeighbours) { this.predictNeighbours = predictNeighbours; } public double getPredictMinTrust() { return predictMinTrust; } public void setPredictMinTrust(double predictMinTrust) { this.predictMinTrust = predictMinTrust; } public int getRecommendationCachingTimeSecs() { return recommendationCachingTimeSecs; } public void setRecommendationCachingTimeSecs(int recommendationCachingTimeSecs) { this.recommendationCachingTimeSecs = recommendationCachingTimeSecs; } // toString, equals, clone, hashCode: public int getMinNumTxsForSV() { return minNumTxsForSV; } public void setMinNumTxsForSV(int minNumTxsForSV) { this.minNumTxsForSV = minNumTxsForSV; } public int getTxHistorySizeForSV() { return txHistorySizeForSV; } public void setTxHistorySizeForSV(int txHistorySizeForSV) { this.txHistorySizeForSV = txHistorySizeForSV; } public double getLongTermWeight() { return longTermWeight; } public void setLongTermWeight(double longTermWeight) { this.longTermWeight = longTermWeight; } public double getShortTermWeight() { return shortTermWeight; } public void setShortTermWeight(double shortTermWeight) { this.shortTermWeight = shortTermWeight; } public double getDecayRateSecs() { return decayRateSecs; } public void setDecayRateSecs(double decayRateSecs) { this.decayRateSecs = decayRateSecs; } public String toString() { StringBuilder buf = new StringBuilder(); int count = 1; for (CF_RECOMMENDER recommender : recommenders) buf.append(" Recommender").append(count++).append(":").append(recommender.name()); count = 1; for (CF_PREDICTOR predictor : predictors) buf.append(" Predictor").append(count++).append(":").append(predictor.name()); count = 1; for (CF_SORTER sorter : sorters) buf.append(" Sorter").append(count++).append(":").append(sorter.name()); count = 1; for (CF_ITEM_COMPARATOR comparator : itemComparators) buf.append(" Item Comparator").append(count++).append(":").append(comparator.name()); return buf.toString(); } @Override public CFAlgorithm clone() throws CloneNotSupportedException { return (CFAlgorithm) super.clone(); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; CFAlgorithm that = (CFAlgorithm) o; if (Double.compare(that.maxRating, maxRating) != 0) return false; if (Double.compare(that.minRating, minRating) != 0) return false; if (predictK != that.predictK) return false; if (Double.compare(that.predictMinTrust, predictMinTrust) != 0) return false; if (predictNeighbours != that.predictNeighbours) return false; if (recommendK != that.recommendK) return false; if (Double.compare(that.recommendMinTrust, recommendMinTrust) != 0) return false; if (transactionActionType != that.transactionActionType) return false; if (userCFLimit != that.userCFLimit) return false; if (date != null ? !date.equals(that.date) : that.date != null) return false; if (itemComparatorStrategy != that.itemComparatorStrategy) return false; if (itemComparators != null ? !itemComparators.equals(that.itemComparators) : that.itemComparators != null) return false; if (name != null ? !name.equals(that.name) : that.name != null) return false; if (predictorStrategy != that.predictorStrategy) return false; if (predictors != null ? !predictors.equals(that.predictors) : that.predictors != null) return false; if (recommendAfter != null ? !recommendAfter.equals(that.recommendAfter) : that.recommendAfter != null) return false; if (recommenderStrategy != that.recommenderStrategy) return false; if (recommenders != null ? !recommenders.equals(that.recommenders) : that.recommenders != null) return false; if (sorterStrategy != that.sorterStrategy) return false; if (sorters != null ? !sorters.equals(that.sorters) : that.sorters != null) return false; if (postprocessing != that.postprocessing) return false; if (longTermWeight != that.longTermWeight) return false; if (shortTermWeight != that.shortTermWeight) return false; return true; } @Override public int hashCode() { int result; long temp; result = itemComparators != null ? itemComparators.hashCode() : 0; result = 31 * result + (itemComparatorStrategy != null ? itemComparatorStrategy.hashCode() : 0); result = 31 * result + (recommenders != null ? recommenders.hashCode() : 0); result = 31 * result + (recommenderStrategy != null ? recommenderStrategy.hashCode() : 0); result = 31 * result + (predictors != null ? predictors.hashCode() : 0); result = 31 * result + (predictorStrategy != null ? predictorStrategy.hashCode() : 0); result = 31 * result + (sorters != null ? sorters.hashCode() : 0); result = 31 * result + (sorterStrategy != null ? sorterStrategy.hashCode() : 0); result = 31 * result + (postprocessing != null ? postprocessing.hashCode() : 0); result = 31 * result + (date != null ? date.hashCode() : 0); result = 31 * result + (recommendAfter != null ? recommendAfter.hashCode() : 0); result = 31 * result + (name != null ? name.hashCode() : 0); temp = minRating != +0.0d ? Double.doubleToLongBits(minRating) : 0L; result = 31 * result + (int) (temp ^ (temp >>> 32)); temp = maxRating != +0.0d ? Double.doubleToLongBits(maxRating) : 0L; result = 31 * result + (int) (temp ^ (temp >>> 32)); result = 31 * result + userCFLimit; result = 31 * result + transactionActionType; result = 31 * result + recommendK; temp = recommendMinTrust != +0.0d ? Double.doubleToLongBits(recommendMinTrust) : 0L; result = 31 * result + (int) (temp ^ (temp >>> 32)); result = 31 * result + predictK; result = 31 * result + predictNeighbours; temp = predictMinTrust != +0.0d ? Double.doubleToLongBits(predictMinTrust) : 0L; result = 31 * result + (int) (temp ^ (temp >>> 32)); temp = longTermWeight != +0.0d ? Double.doubleToLongBits(longTermWeight) : 0L; result = 31 * result + (int) (temp ^ (temp >>> 32)); temp = shortTermWeight != +0.0d ? Double.doubleToLongBits(shortTermWeight) : 0L; result = 31 * result + (int) (temp ^ (temp >>> 32)); return result; } public void setParameter(String field, List<String> values) { try { //check if it's a multiple value if(values != null && !values.isEmpty()) { String value = values.iterator().next(); if("item_comparators".equals(field)) { List<CF_ITEM_COMPARATOR> list = new ArrayList<CF_ITEM_COMPARATOR>(); for(String val : values) { list.add(CF_ITEM_COMPARATOR.valueOf(val)); } setItemComparators(list); } else if("item_comparator_strategy".equals(field)) { setItemComparatorStrategy(CF_STRATEGY.valueOf(value)); } else if("recommenders".equals(field)) { List<CF_RECOMMENDER> list = new ArrayList<CF_RECOMMENDER>(); for(String val : values) { list.add(CF_RECOMMENDER.valueOf(val)); } setRecommenders(list); } else if("recommender_strategy".equals(field)) { setRecommenderStrategy(CF_STRATEGY.valueOf(value)); } else if("predictors".equals(field)) { List<CF_PREDICTOR> list = new ArrayList<CF_PREDICTOR>(); for(String val : values) { list.add(CF_PREDICTOR.valueOf(val)); } setPredictors(list); } else if("predictor_strategy".equals(field)) { setPredictorStrategy(CF_STRATEGY.valueOf(value)); } else if("sorters".equals(field)) { List<CF_SORTER> list = new ArrayList<CF_SORTER>(); for(String val : values) { list.add(CF_SORTER.valueOf(val)); } setSorters(list); } else if("sorter_strategy".equals(field)) { setSorterStrategy(CF_STRATEGY.valueOf(value)); } else if("postprocessing".equals(field)) { setPostprocessing(CF_POSTPROCESSING.valueOf(value)); } else if ("long_term_cluster_weight".equals(field)){ this.setLongTermWeight(Double.parseDouble(value)); } else if ("short_term_cluster_weight".equals(field)){ this.setShortTermWeight(Double.parseDouble(value)); } else { final String message = "Field : " + field + " not recognized"; logger.error(message, new Exception(message)); } } } catch(Exception e) { logger.error("Not able to process the field : " + field + " with value: " + values, e); } } public String toLogSorter() { String res = ""; //CF_SORTER if(sorters!=null && sorters.size()>0) { for(CF_SORTER s : sorters) { res += s.name() + "|"; } res = res.substring(0,res.length()-1); } //CF_STRATEGY if(sorterStrategy!=null) res +=";" + sorterStrategy.name(); //CF_POSTPROCESSING if(postprocessing!=null) res +=";" + postprocessing.name(); return res; } }