package happy.research.cf;
import happy.coding.io.Logs;
import happy.coding.io.Strings;
import happy.coding.math.Sims;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.junit.Test;
/**
* @author guoguibing
*/
public class TCF_t extends Thread_t
{
public TCF_t(int id)
{
super(id);
}
@Override
protected Map<String, Double> updateNNRatings(Map<String, Double> nnScores, Rating testRating)
{
/* build the original ratings of nearest neighbors on test item */
Map<String, Double> nnRatings = new HashMap<>();
String testItem = testRating.getItemId();
for (Entry<String, Double> entry : nnScores.entrySet())
{
String nn = entry.getKey();
Map<String, Rating> bsRatings = userRatingsMap.get(nn);
if (bsRatings == null) continue;
double bsRating = 0.0;
if (bsRatings.containsKey(testItem)) bsRating = bsRatings.get(testItem).getRating();
nnRatings.put(nn, bsRating);
}
/* use TCF to update ratings of nearest neighbors */
predictByTCF(nnRatings, testRating);
return nnRatings;
}
@SuppressWarnings("unchecked")
@Override
protected Map<String, Double>[] buildModel(Rating testRating)
{
String testUser = testRating.getUserId();
String testItem = testRating.getItemId();
double rating = testRating.getRating();
Map<String, Double> nnScores = new HashMap<>();
Map<String, Double> nnRatings = new HashMap<>();
Map<String, Rating> asRatings = userRatingsMap.get(testUser);
for (Entry<String, Map<String, Rating>> entry : userRatingsMap.entrySet())
{
String user = entry.getKey();
if (user.equals(testUser)) continue;
Map<String, Rating> bsRatings = entry.getValue();
double bsRating = 0.0;
if (bsRatings.containsKey(testItem)) bsRating = bsRatings.get(testItem).getRating();
List<Double> as = new ArrayList<>();
List<Double> bs = new ArrayList<>();
for (Entry<String, Rating> ar : asRatings.entrySet())
{
String item = ar.getKey();
if (item.equals(testItem)) continue;
if (bsRatings.containsKey(item))
{
as.add(ar.getValue().getRating());
bs.add(bsRatings.get(item).getRating());
}
}
double similarity = Sims.pcc(as, bs);
if (Double.isNaN(similarity)) continue;
/* use trust network to predict a rating for this user */
if (similarity > params.SIMILARITY_THRESHOLD)
{
nnScores.put(user, similarity);
nnRatings.put(user, bsRating);
}
}
if (rating > 0) predictByTCF(nnRatings, testRating);
int kNN = params.kNN;
if (kNN > 0 && nnScores.size() > kNN)
{
List<Pair> pairs = new ArrayList<>();
for (Entry<String, Double> en : nnScores.entrySet())
pairs.add(new Pair(en.getKey(), en.getValue()));
Collections.sort(pairs);
Collections.reverse(pairs);
Map<String, Double> ratings = new HashMap<>();
Map<String, Double> scores = new HashMap<>();
for (int i = 0; i < kNN; i++)
{
Pair p = pairs.get(i);
String item = p.left;
double score = p.right;
scores.put(item, score);
ratings.put(item, nnRatings.get(item));
}
nnScores = scores;
nnRatings = ratings;
}
return new Map[] { nnScores, nnRatings };
}
// adjust ratings using TCF algorithm
private void predictByTCF(Map<String, Double> nnRatings, Rating testRating)
{
String user = testRating.getUserId();
String item = testRating.getItemId();
/* if iteration is 2, then maximum is x^2: { tn, q = q0 + q1*x + q2*x^2} */
Map<String, Double> q0s = new HashMap<>();
Map<String, Double> q1s = new HashMap<>();
Map<String, Double> q2s = new HashMap<>();
Map<String, Integer> c0s = new HashMap<>();
Map<String, Integer> c1s = new HashMap<>();
Map<String, Integer> c2s = new HashMap<>();
/* initial step */
for (String tn : userTNsMap.keySet())
{
if (tn.equals(user))
{
q0s.put(user, 0.0);
q1s.put(user, 0.0);
q2s.put(user, 0.0);
c0s.put(user, 0);
c1s.put(user, 0);
c2s.put(user, 0);
continue;
}
double tnRating = 0.0;
Map<String, Rating> tnRatings = userRatingsMap.get(tn);
if (tnRatings != null && tnRatings.containsKey(item)) tnRating = tnRatings.get(item).getRating();
q0s.put(tn, tnRating);
q1s.put(tn, 0.0);
q2s.put(tn, 0.0);
c0s.put(tn, tnRating <= 0.0 ? 0 : 1);
c1s.put(tn, 0);
c2s.put(tn, 0);
}
/* adjust ratings in the trust network */
for (int i = 0; i < params.TCF_ITERATION; i++)
{
Map<String, Double> q1s_old = new HashMap<>(q1s);
Map<String, Integer> c1s_old = new HashMap<>(c1s);
for (String u : q0s.keySet())
{
double q1_new = 0.0, q2_new = 0.0;
int c1_new = 0, c2_new = 0;
Map<String, Double> tns = userTNsMap.get(u);
if (tns == null) continue;
for (String tn : tns.keySet())
{
if (q0s.containsKey(tn)) q1_new += q0s.get(tn);
if (q1s_old.containsKey(tn)) q2_new += q1s_old.get(tn);
if (c0s.containsKey(tn)) c1_new += c0s.get(tn);
if (c1s_old.containsKey(tn)) c2_new += c1s_old.get(tn);
}
q1s.put(u, q1_new);
q2s.put(u, q2_new);
c1s.put(u, c1_new);
c2s.put(u, c2_new);
}
}
/* update ratings */
for (String nn : nnRatings.keySet())
{
double nnRating = nnRatings.get(nn);
if (nnRating <= 0.0 && q0s.containsKey(nn))
{
double q1 = q1s.get(nn);
double q2 = q2s.get(nn);
int c1 = c1s.get(nn);
int c2 = c2s.get(nn);
nnRating = q1 / c1;
if (Double.isNaN(nnRating))
{
nnRating = q2 / c2;
if (Double.isNaN(nnRating)) nnRating = 0.0;
}
}
nnRatings.put(nn, nnRating);
}
}
/**
* This method is used to validate the implementation using the examples from the original paper
* <em>Trust-Based Infinitesimals for Enhanced Collaborative Filtering</em>
*/
@Test
private void validate()
{
String user = "1";
String item = "10";
Rating testRating = new Rating();
testRating.setUserId(user);
testRating.setItemId(item);
testRating.setRating(0.0);
/* trust network */
userTNsMap = new HashMap<>();
Map<String, Double> ns = null;
ns = new HashMap<>();
ns.put("2", 1.0);
ns.put("3", 1.0);
userTNsMap.put("1", ns);
ns = new HashMap<>();
ns.put("1", 1.0);
ns.put("3", 1.0);
userTNsMap.put("2", ns);
ns = new HashMap<>();
ns.put("4", 1.0);
userTNsMap.put("3", ns);
ns = new HashMap<>();
userTNsMap.put("4", ns);
/* user ratings */
userRatingsMap = new HashMap<>();
Map<String, Rating> ratings = null;
Rating rating = null;
ratings = new HashMap<>();
rating = new Rating();
rating.setUserId("1");
rating.setItemId(item);
rating.setRating(0.0);
ratings.put(item, rating);
userRatingsMap.put("1", ratings);
ratings = new HashMap<>();
rating = new Rating();
rating.setUserId("2");
rating.setItemId(item);
rating.setRating(3.0);
ratings.put(item, rating);
userRatingsMap.put("2", ratings);
ratings = new HashMap<>();
rating = new Rating();
rating.setUserId("3");
rating.setItemId(item);
rating.setRating(4.0);
ratings.put(item, rating);
userRatingsMap.put("3", ratings);
ratings = new HashMap<>();
rating = new Rating();
rating.setUserId("4");
rating.setItemId(item);
rating.setRating(5.0);
ratings.put(item, rating);
userRatingsMap.put("4", ratings);
Map<String, Double> nnRatings = new HashMap<>();
nnRatings.put("1", 0.0);
nnRatings.put("2", 3.0);
nnRatings.put("3", 4.0);
nnRatings.put("4", 5.0);
predictByTCF(nnRatings, testRating);
String result = Strings.toString(nnRatings);
Logs.debug(result);
}
}