/* * Copyright (C) 2015 Information Retrieval Group at Universidad Autónoma * de Madrid, http://ir.ii.uam.es * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package es.uam.eps.ir.ranksys.novdiv.reranking; import es.uam.eps.ir.ranksys.core.Recommendation; import es.uam.eps.ir.ranksys.core.util.Stats; import it.unimi.dsi.fastutil.ints.IntSortedSet; import it.unimi.dsi.fastutil.objects.Object2DoubleMap; import it.unimi.dsi.fastutil.objects.Object2DoubleOpenHashMap; import org.ranksys.core.util.tuples.Tuple2od; import java.util.List; import static java.lang.Math.min; /** * Linear combination re-ranker that combines the original score of the input * recommendation and a novelty component. * * @author Saúl Vargas (saul.vargas@uam.es) * @author Pablo Castells (pablo.castells@uam.es) * * @param <U> type of the users * @param <I> type of the items */ public abstract class LambdaReranker<U, I> extends GreedyReranker<U, I> { /** * Trade-off parameter of the linear combination. */ protected final double lambda; private final boolean norm; /** * Constructor. * * @param lambda trade-off parameter of the linear combination * @param cutoff how many items are re-ranked by the greedy selection. * @param norm decides whether apply a normalization of the values of the * component at each selection step */ public LambdaReranker(double lambda, int cutoff, boolean norm) { super(cutoff); this.lambda = lambda; this.norm = norm; } /** * Returns the permutation that is applied to the input recommendation * to generate the re-ranked recommendation. * * @param recommendation input recommendation * @param maxLength maximum length of the permutation * @return permutation that encodes the re-ranking */ @Override public int[] rerankPermutation(Recommendation<U, I> recommendation, int maxLength) { if (lambda == 0.0) { return getBasePerm(min(maxLength, recommendation.getItems().size())); } else { return super.rerankPermutation(recommendation, maxLength); } } @Override protected abstract GreedyUserReranker getUserReranker(Recommendation<U, I> recommendation, int maxLength); /** * User re-ranker for {@link LambdaReranker}. */ protected abstract class LambdaUserReranker extends GreedyUserReranker { /** * Statistics about relevance scores. */ protected Stats relStats; /** * Statistics about novelty scores. */ protected Stats novStats; /** * Map of the novelty of each item. */ protected Object2DoubleMap<I> novMap; /** * Constructor. * * @param recommendation input recommendation * @param maxLength maximum length of the re-ranked recommendation */ public LambdaUserReranker(Recommendation<U, I> recommendation, int maxLength) { super(recommendation, maxLength); } /** * Returns the normalized value of a relevance or novelty * score. * * @param score the relevance or novelty score * @param stats the relevance or novelty statistics * @return the normalized score */ protected double norm(double score, Stats stats) { if (norm) { return (score - stats.getMean()) / stats.getStandardDeviation(); } else { return score; } } @Override protected int selectItem(IntSortedSet remainingI, List<Tuple2od<I>> list) { novMap = new Object2DoubleOpenHashMap<>(); relStats = new Stats(); novStats = new Stats(); remainingI.forEach(i -> { Tuple2od<I> itemValue = list.get(i); double nov = nov(itemValue); novMap.put(itemValue.v1, nov); relStats.accept(itemValue.v2); novStats.accept(nov); }); return super.selectItem(remainingI, list); } @Override protected double value(Tuple2od<I> iv) { return (1 - lambda) * norm(iv.v2, relStats) + lambda * norm(novMap.getDouble(iv.v1), novStats); } /** * Returns the novelty score of an item. * * @param iv item-relevance pair * @return the novelty of the item */ protected abstract double nov(Tuple2od<I> iv); } }