/*
 * LensKit, an open source recommender systems toolkit.
 * Copyright 2010-2012 Regents of the University of Minnesota and contributors
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 51
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */
package org.grouplens.lenskit.baseline;

import org.grouplens.grapht.annotation.DefaultProvider;
import org.grouplens.lenskit.core.Shareable;
import org.grouplens.lenskit.core.Transient;
import org.grouplens.lenskit.data.dao.DataAccessObject;
import org.grouplens.lenskit.params.Damping;
import org.grouplens.lenskit.vectors.MutableSparseVector;
import org.grouplens.lenskit.vectors.SparseVector;
import org.grouplens.lenskit.vectors.VectorEntry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;

import static java.lang.Math.abs;

/**
 * Rating scorer that returns the user's average rating for all predictions.
 *
 * If the user has no ratings, the global mean is returned.  This is done by
 * actually computing the average offset from the global mean and adding back
 * the global mean for the returned prediction.
 *
 * @author Michael Ekstrand <ekstrand@cs.umn.edu>
 */
@DefaultProvider(UserMeanPredictor.Provider.class)
@Shareable
public class UserMeanPredictor extends GlobalMeanPredictor {
    private static final Logger logger = LoggerFactory.getLogger(UserMeanPredictor.class);

    /**
     * A builder that creates UserMeanPredictors.
     *
     * @author Michael Ludwig <mludwig@cs.umn.edu>
     */
    public static class Provider implements javax.inject.Provider<UserMeanPredictor> {
        private double smoothing = 0;
        private DataAccessObject dao;

        @Inject
        public Provider(@Transient DataAccessObject dao,
                        @Damping double damping) {
            this.dao = dao;
            smoothing = damping;
        }

        @Override
        public UserMeanPredictor get() {
            logger.debug("Building new user mean scorer");

            logger.debug("smoothing = {}", smoothing);
            double mean = GlobalMeanPredictor.computeMeanRating(dao);
            logger.debug("Computed global mean {}", mean);
            return new UserMeanPredictor(mean, smoothing);
        }
    }

    private static final long serialVersionUID = 1L;

    private final double globalMean;
    private final double smoothing;

    @Inject
    public UserMeanPredictor(@Transient DataAccessObject dao,
                             @Damping double damping) {
        this(computeMeanRating(dao), damping);
    }

    /**
     * Construct a scorer that computes user means offset by the global mean.
     *
     * @param globalMean The mean rating value for all items.
     * @param damping    A damping term for the calculations.
     */
    public UserMeanPredictor(double globalMean, double damping) {
        super(globalMean);
        this.globalMean = globalMean;
        this.smoothing = damping;
    }

    static double average(SparseVector ratings, double globalMean, double smoothing) {
        if (ratings.isEmpty()) {
            return globalMean;
        }
        return (ratings.sum() + smoothing * globalMean) / (ratings.size() + smoothing);
    }

    /* (non-Javadoc)
     * @see org.grouplens.lenskit.RatingPredictor#predict(long, java.util.Map, java.util.Collection)
     */
    @Override
    public void predict(long user, SparseVector ratings,
                        MutableSparseVector output, boolean predictSet) {
        double mean = average(ratings, globalMean, smoothing);
        //noinspection AssertWithSideEffects
        assert smoothing != 0 || ratings.isEmpty() || abs(mean - ratings.mean()) < 1.0e-6;
        if (predictSet) {
            output.fill(mean);
        } else {
            for (VectorEntry e : output.fast(VectorEntry.State.UNSET)) {
                output.set(e, mean);
            }
        }
    }

    @Override
    public String toString() {
        String cls = getClass().getSimpleName();
        return String.format("%s(µ=%.3f, γ=%.2f)", cls, globalMean, smoothing);
    }

}

@@ -2280,16 +2280,175 @@
 t dao;%0A%0A
+        /**%0A         * Create a new user mean predictor.%0A         *%0A         * @param dao     The DAO.%0A         * @param damping The damping term.%0A         */%0A
         
@@ -2780,22 +2780,20 @@
 .debug(%22
-smooth
+damp
 ing = %7B%7D
@@ -3132,202 +3132,16 @@
 ble 
-smoothing;%0A%0A    @Inject%0A    public UserMeanPredictor(@Transient DataAccessObject dao,%0A                             @Damping double damping) %7B%0A        this(computeMeanRating(dao), damping);%0A    %7D
+damping;
 %0A%0A  
@@ -3249,23 +3249,17 @@
 ram 
-globalM
+m
 ean The 
 mean
@@ -3254,16 +3254,23 @@
 ean The 
+global 
 mean rat
@@ -3276,28 +3276,8 @@
 ting
- value for all items
 .%0A  
@@ -3296,14 +3296,8 @@
 damp
-ing   
  A d
@@ -3370,31 +3370,25 @@
 ctor(double 
-globalM
+m
 ean, double 
@@ -3383,35 +3383,32 @@
 ean, double damp
-ing
 ) %7B%0A        supe
@@ -3409,23 +3409,17 @@
   super(
-globalM
+m
 ean);%0A  
@@ -3424,21 +3424,16 @@
         
-this.
 globalMe
@@ -3437,23 +3437,17 @@
 lMean = 
-globalM
+m
 ean;%0A   
@@ -3455,19 +3455,12 @@
     
-this.smooth
+damp
 ing 
@@ -3461,27 +3461,24 @@
 mping = damp
-ing
 ;%0A    %7D%0A%0A   
@@ -4048,38 +4048,36 @@
 gs, globalMean, 
-smooth
+damp
 ing);%0A        //
@@ -4126,22 +4126,20 @@
  assert 
-smooth
+damp
 ing != 0
@@ -4573,30 +4573,28 @@
 globalMean, 
-smooth
+damp
 ing);%0A    %7D%0A

Clean up user mean docs