// Copyright (C) 2011 Zeno Gantner, Chris Newell
//
// This file is part of MyMediaLite.
//
// MyMediaLite is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// MyMediaLite 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 MyMediaLite. If not, see <http://www.gnu.org/licenses/>.
package org.mymedialite.data;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import java.util.ArrayList;
import java.util.List;
import org.mymedialite.datatype.IBooleanMatrix;
/**
* Data structure for implicit, positive-only user feedback.
* This data structure supports incremental updates.
* @version 2.03
*/
public class PosOnlyFeedback<T extends IBooleanMatrix> extends DataSet implements IPosOnlyFeedback {
/** By-user access, users are stored in the rows, items in the columns */
public IBooleanMatrix userMatrix;
/** By-item access, items are stored in the rows, users in the columns */
public IBooleanMatrix itemMatrix;
private Class<T> c;
/**
* Create a PosOnlyFeedback object.
* @param c the Class<T>
* @throws InstantiationException
* @throws IllegalAccessException
*/
public PosOnlyFeedback(Class<T> c) throws InstantiationException, IllegalAccessException {
super();
this.c = c;
userMatrix = c.newInstance();
}
/**
* By-user access, users are stored in the rows, items in the columns.
*/
public IBooleanMatrix userMatrix() {
if(userMatrix == null) userMatrix = getUserMatrixCopy();
return userMatrix;
}
/**
* By-item access, items are stored in the rows, users in the columns.
*/
public IBooleanMatrix itemMatrix() {
if(itemMatrix == null) itemMatrix = getItemMatrixCopy();
return itemMatrix;
}
@Override
public IBooleanMatrix getUserMatrixCopy() {
T matrix = null;
try {
matrix = c.newInstance();
for (int index = 0; index < size(); index++)
matrix.set(users.getInt(index), items.getInt(index), true);
} catch (Exception e) { }
return matrix;
}
@Override
public IBooleanMatrix getItemMatrixCopy() {
T matrix = null;
try {
matrix = c.newInstance();
for (int index = 0; index < size(); index++)
matrix.set(items.getInt(index), users.getInt(index), true);
} catch (Exception e) { }
return matrix;
}
/**
* Add a user-item event to the data structure
* @param user_id the user ID
* @param item_id the item ID
*/
public void add(int user_id, int item_id) {
users.add(user_id);
items.add(item_id);
if (userMatrix != null) userMatrix.set(user_id, item_id, true);
if (itemMatrix != null) itemMatrix.set(item_id, user_id, true);
if (user_id > maxUserID) maxUserID = user_id;
if (item_id > maxItemID) maxItemID = item_id;
}
/**
* Remove a user-item event from the data structure.
* @param user_id the user ID
* @param item_id >the item ID
*/
public void remove(int user_id, int item_id) {
Integer index;
while((index = tryGetIndex(user_id, item_id)) != null) {
users.remove(index);
items.remove(index);
}
if (userMatrix != null) userMatrix.set(user_id, item_id, false);
if (itemMatrix != null) itemMatrix.set(item_id, user_id, false);
}
/**
* Remove the event with a given index
* @param index the index of the event to be removed
*/
public void remove(int index) {
int user_id = users.getInt(index);
int item_id = items.getInt(index);
users.remove(index);
items.remove(index);
if (tryGetIndex(user_id, item_id) == -1) {
if (userMatrix != null) userMatrix.set(user_id, item_id, false);
if (itemMatrix != null) itemMatrix.set(item_id, user_id, false);
}
}
/**
* Remove all feedback by a given user.
* @param user_id the user ID
*/
public void removeUser(int user_id) {
IntList indices = new IntArrayList();
if (byUser != null)
indices = byUser().get(user_id);
else if (userMatrix != null)
indices = new IntArrayList(userMatrix.get(user_id));
else
for (int index = 0; index < size(); index++)
if (users.getInt(index) == user_id)
indices.add(index);
// assumption: indices is sorted
for (int i = indices.size() - 1; i >= 0; i--) {
users.remove(indices.getInt(i));
items.remove(indices.getInt(i));
}
if (userMatrix != null)
userMatrix.get(user_id).clear();
if (itemMatrix != null)
for (int i = 0; i < itemMatrix.numberOfRows(); i++)
itemMatrix.get(i).remove(user_id);
}
/**
* Remove all feedback about a given item</summary>
* @param item_id the item ID
*/
public void removeItem(int item_id) {
IntList indices = new IntArrayList();
if (byItem != null)
indices = byItem().get(item_id);
else if (itemMatrix != null)
indices = new IntArrayList(itemMatrix.get(item_id));
else
for (int index = 0; index < size(); index++)
if (items.getInt(index) == item_id)
indices.add(index);
// assumption: indices is sorted
for (int i = indices.size() - 1; i >= 0; i--) {
users.remove(indices.getInt(i));
items.remove(indices.getInt(i));
}
if (userMatrix != null)
for (int u = 0; u < userMatrix.numberOfRows(); u++)
userMatrix.get(u).remove(item_id);
if (itemMatrix != null)
itemMatrix.get(item_id).clear();
}
@Override
public IPosOnlyFeedback transpose() {
PosOnlyFeedback<T> transpose = null;
try {
transpose = new PosOnlyFeedback<T>(c);
transpose.users = new IntArrayList(this.items);
transpose.items = new IntArrayList(this.users);
} catch (Exception e) { }
return transpose;
}
}