/* * Sifarish: Recommendation Engine * Author: Pranab Ghosh * * 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 org.sifarish.feature; import java.util.HashMap; import java.util.Map; import org.sifarish.util.Field; import org.sifarish.util.IDistanceStrategy; /** * Distance calculation strategy * @author pranab * */ public abstract class DistanceStrategy implements IDistanceStrategy { protected double sumWt; protected int scale; protected double power; protected double totalWt; protected int count; protected enum DistanceStatus { DistanceImploded, DistanceExploded, DistanceUntouched } private Map<Integer, DistanceStatus> attributeDistanceStatus = new HashMap<Integer, DistanceStatus>(); /** * @param scale */ public DistanceStrategy(int scale) { this.scale = scale; } /** * */ public void initialize() { sumWt = 0.0; totalWt = 0.0; count = 0; attributeDistanceStatus.clear(); } /** * @param distance * @param weight */ public abstract void accumulate(double distance, double weight); /** * @param distance * @param field */ public abstract void accumulate(double distance, Field field); /** * @param distance * @param field */ protected double getEffectiveDistance(double distance, Field field){ double effectDist = 0; String distanceFunction = field.getContAttrDistanceFunction(); double weight = field.getWeight(); double threshold = field.getFunctionThreshold(); if (distanceFunction.equals("none")) { effectDist = distance; } else if (distanceFunction.equals("nonLinear")) { //if weight < 1 then convex i.e. effective distance greater than distance otherwise concave effectDist = (1 / weight) * distance + ( 1 - 1 / weight) * distance * distance; } else if (distanceFunction.equals("sigmoid")) { //transtion at threshold, higher weight will simulate step function effectDist = 1.0 / (1 + Math.exp(-weight * (distance - threshold))); } else if (distanceFunction.equals("step")) { //transition at threshold effectDist = distance < threshold ? 0 : 1; } else if (distanceFunction.equals("ramp")) { //transtion at threshold effectDist = distance < threshold ? 0 : distance; } //check for distance implosion and explosion if (effectDist < field.getImplodeThreshold()) { attributeDistanceStatus.put(field.getOrdinal(), DistanceStatus.DistanceImploded); } else if (effectDist > field.getExplodeThreshold()) { attributeDistanceStatus.put(field.getOrdinal(), DistanceStatus.DistanceExploded); } return effectDist; } /** * @return */ protected DistanceStatus getDistanceStatus() { DistanceStatus status = DistanceStatus.DistanceUntouched; int explodedCount = 0; int implodedCount = 0; for (int fieldOrd : attributeDistanceStatus.keySet()) { if (attributeDistanceStatus.get(fieldOrd) == DistanceStatus.DistanceExploded) { ++explodedCount; } else { ++implodedCount; } } //explode or implode only when there is consensus if (explodedCount > 0 && implodedCount == 0) { status = DistanceStatus.DistanceExploded; } else if (implodedCount > 0 && explodedCount == 0) { status = DistanceStatus.DistanceImploded; } return status; } /** * @return */ public abstract int getSimilarity(); /** * @param isScaled * @return */ public abstract double getSimilarity(boolean isScaled); /** * @return */ public int getScale() { return scale; } /** * @param scale */ public void setScale(int scale) { this.scale = scale; } /** * @return */ public double getPower() { return power; } /** * @param power */ public void setPower(double power) { this.power = power; } }