aggregated = tuples.foldByKey(Double.NaN, Functions.<Double>last());
}
Collection<UserItemStrength> input = aggregated.map(new TupleToUserItemStrength()).collect();
Solver XTXsolver;
Solver YTYsolver;
try {
XTXsolver = model.getXTXSolver();
YTYsolver = model.getYTYSolver();
} catch (SingularMatrixSolverException smse) {
return Collections.emptyList();
}
Collection<String> result = new ArrayList<>();
for (UserItemStrength uis : input) {
String user = uis.getUser();
String item = uis.getItem();
double value = uis.getStrength();
// Xu is the current row u in the X user-feature matrix
float[] Xu = model.getUserVector(user);
// Yi is the current row i in the Y item-feature matrix
float[] Yi = model.getItemVector(item);
double[] newXu = null;
if (Yi != null) {
// Let Qui = Xu * (Yi)^t -- it's the current estimate of user-item interaction
// in Q = X * Y^t
// 0.5 reflects a "don't know" state
double currentValue = Xu == null ? 0.5 : VectorMath.dot(Xu, Yi);
double targetQui = computeTargetQui(value, currentValue);
// The entire vector Qu' is just 0, with Qui' in position i
// More generally we are looking for Qu' = Xu' * Y^t
if (!Double.isNaN(targetQui)) {
// Solving Qu' = Xu' * Y^t for Xu', now that we have Qui', as:
// Qu' * Y * (Y^t * Yi)^-1 = Xu'
// Qu' is 0 except for one value at position i, so it's really (Qui')*Yi
float[] QuiYi = Yi.clone();
for (int i = 0; i < QuiYi.length; i++) {
QuiYi[i] *= targetQui;
}
newXu = YTYsolver.solveFToD(QuiYi);
}
}
// Similarly for Y vs X
double[] newYi = null;