You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mahout.apache.org by sr...@apache.org on 2008/05/09 23:35:17 UTC
svn commit: r654943 [7/9] - in /lucene/mahout/trunk/core: ./ lib/
src/main/examples/org/ src/main/examples/org/apache/
src/main/examples/org/apache/mahout/ src/main/examples/org/apache/mahout/cf/
src/main/examples/org/apache/mahout/cf/taste/ src/main/e...
Added: lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/model/DataModel.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/model/DataModel.java?rev=654943&view=auto
==============================================================================
--- lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/model/DataModel.java (added)
+++ lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/model/DataModel.java Fri May 9 14:35:12 2008
@@ -0,0 +1,106 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.mahout.cf.taste.model;
+
+import org.apache.mahout.cf.taste.common.Refreshable;
+import org.apache.mahout.cf.taste.common.TasteException;
+
+import java.util.List;
+
+/**
+ * <p>Implementations represent a repository of information about {@link User}s and their
+ * associated {@link Preference}s for {@link Item}s.</p>
+ */
+public interface DataModel extends Refreshable {
+
+ /**
+ * @return a {@link List} of all {@link User}s in the model, ordered by {@link User}
+ * @throws TasteException if an error occurs while accessing the data
+ */
+ Iterable<? extends User> getUsers() throws TasteException;
+
+ /**
+ * @param id user ID
+ * @return {@link User} who has that ID
+ * @throws TasteException if an error occurs while accessing the data
+ * @throws java.util.NoSuchElementException if there is no such {@link User}
+ */
+ User getUser(Object id) throws TasteException;
+
+ /**
+ * @return a {@link List} of all {@link Item}s in the model, order by {@link Item}
+ * @throws TasteException if an error occurs while accessing the data
+ */
+ Iterable<? extends Item> getItems() throws TasteException;
+
+ /**
+ * @param id item ID
+ * @return {@link Item} that has that ID
+ * @throws TasteException if an error occurs while accessing the data
+ * @throws java.util.NoSuchElementException if there is no such {@link Item}
+ */
+ Item getItem(Object id) throws TasteException;
+
+ /**
+ * @param itemID item ID
+ * @return all existing {@link Preference}s expressed for that item, ordered by {@link User}
+ * @throws TasteException if an error occurs while accessing the data
+ */
+ Iterable<? extends Preference> getPreferencesForItem(Object itemID) throws TasteException;
+
+ /**
+ * @param itemID item ID
+ * @return all existing {@link Preference}s expressed for that item, ordered by {@link User},
+ * as an array
+ * @throws TasteException if an error occurs while accessing the data
+ */
+ Preference[] getPreferencesForItemAsArray(Object itemID) throws TasteException;
+
+ /**
+ * @return total number of {@link Item}s known to the model. This is generally the union
+ * of all {@link Item}s preferred by at least one {@link User} but could include more.
+ * @throws TasteException if an error occurs while accessing the data
+ */
+ int getNumItems() throws TasteException;
+
+ /**
+ * @return total number of {@link User}s known to the model.
+ * @throws TasteException if an error occurs while accessing the data
+ */
+ int getNumUsers() throws TasteException;
+
+ /**
+ * <p>Sets a particular preference (item plus rating) for a user.</p>
+ *
+ * @param userID user to set preference for
+ * @param itemID item to set preference for
+ * @param value preference value
+ * @throws TasteException if an error occurs while accessing the data
+ */
+ void setPreference(Object userID, Object itemID, double value) throws TasteException;
+
+ /**
+ * <p>Removes a particular preference for a user.</p>
+ *
+ * @param userID user from which to remove preference
+ * @param itemID item to remove preference for
+ * @throws TasteException if an error occurs while accessing the data
+ */
+ void removePreference(Object userID, Object itemID) throws TasteException;
+
+}
Added: lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/model/Item.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/model/Item.java?rev=654943&view=auto
==============================================================================
--- lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/model/Item.java (added)
+++ lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/model/Item.java Fri May 9 14:35:12 2008
@@ -0,0 +1,39 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.mahout.cf.taste.model;
+
+/**
+ * <p>Implementations of this interface represent items that {@link User}s have
+ * preferences for, and which can be recommended to them. {@link Item}s must have
+ * a unique ID of some kind, and must be {@link Comparable}.</p>
+ */
+public interface Item extends Comparable<Item> {
+
+ /**
+ * @return unique ID for this item
+ */
+ Object getID();
+
+ /**
+ * @return true if and only if this {@link Item} can be recommended to a user;
+ * for example, this could be false for an {@link Item} that is no longer
+ * available but which remains valuable for recommendation
+ */
+ boolean isRecommendable();
+
+}
Added: lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/model/JDBCDataModel.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/model/JDBCDataModel.java?rev=654943&view=auto
==============================================================================
--- lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/model/JDBCDataModel.java (added)
+++ lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/model/JDBCDataModel.java Fri May 9 14:35:12 2008
@@ -0,0 +1,35 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.mahout.cf.taste.model;
+
+import org.apache.mahout.cf.taste.common.TasteException;
+
+import javax.sql.DataSource;
+
+public interface JDBCDataModel extends DataModel {
+
+ DataSource getDataSource();
+
+ /**
+ * @param assumeExists assume the item exists; don't consult the underlying database. This is a necessary
+ * performance enhancement shortcut needed by slope one recommenders
+ * @see #getItem(Object)
+ */
+ Item getItem(Object id, boolean assumeExists) throws TasteException;
+
+}
\ No newline at end of file
Added: lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/model/Preference.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/model/Preference.java?rev=654943&view=auto
==============================================================================
--- lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/model/Preference.java (added)
+++ lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/model/Preference.java Fri May 9 14:35:12 2008
@@ -0,0 +1,50 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.mahout.cf.taste.model;
+
+/**
+ * <p>A {@link Preference} encapsulates an {@link Item} and a preference value, which
+ * indicates the strength of the preference for it. {@link Preference}s are associated
+ * to {@link User}s.</p>
+ */
+public interface Preference {
+
+ /**
+ * @return {@link User} who prefers the {@link Item}
+ */
+ User getUser();
+
+ /**
+ * @return {@link Item} that is preferred
+ */
+ Item getItem();
+
+ /**
+ * @return strength of the preference for that item. Zero should indicate "no preference either way";
+ * positive values indicate preference and negative values indicate dislike
+ */
+ double getValue();
+
+ /**
+ * Sets the strength of the preference for this item
+ *
+ * @param value new preference
+ */
+ void setValue(double value);
+
+}
Added: lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/model/User.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/model/User.java?rev=654943&view=auto
==============================================================================
--- lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/model/User.java (added)
+++ lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/model/User.java Fri May 9 14:35:12 2008
@@ -0,0 +1,53 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.mahout.cf.taste.model;
+
+/**
+ * <p>Implementations represent a user, who has preferences for {@link Item}s.</p>
+ */
+public interface User extends Comparable<User> {
+
+ /**
+ * @return unique user ID
+ */
+ Object getID();
+
+ /**
+ * @param itemID ID of item to get the user's preference for
+ * @return user's {@link Preference} for that {@link Item}, or <code>null</code> if the user expresses
+ * no such preference
+ */
+ Preference getPreferenceFor(Object itemID);
+
+ /**
+ * <p>Returns a sequence of {@link Preference}s for this {@link User} which can be iterated over.
+ * Note that the sequence <em>must</em> be "in order": ordered by {@link Item}.</p>
+ *
+ * @return a sequence of {@link Preference}s
+ */
+ Iterable<Preference> getPreferences();
+
+ /**
+ * <p>Returns an array view of {@link Preference}s for this {@link User}.
+ * Note that the sequence <em>must</em> be "in order": ordered by {@link Item}.</p>
+ *
+ * @return an array of {@link Preference}s
+ */
+ Preference[] getPreferencesAsArray();
+
+}
Added: lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/neighborhood/UserNeighborhood.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/neighborhood/UserNeighborhood.java?rev=654943&view=auto
==============================================================================
--- lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/neighborhood/UserNeighborhood.java (added)
+++ lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/neighborhood/UserNeighborhood.java Fri May 9 14:35:12 2008
@@ -0,0 +1,39 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.mahout.cf.taste.neighborhood;
+
+import org.apache.mahout.cf.taste.common.Refreshable;
+import org.apache.mahout.cf.taste.common.TasteException;
+import org.apache.mahout.cf.taste.model.User;
+
+import java.util.Collection;
+
+/**
+ * <p>Implementations of this interface compute a "neighborhood" of {@link User}s like a
+ * given {@link User}. This neighborhood can be used to compute recommendations then.</p>
+ */
+public interface UserNeighborhood extends Refreshable {
+
+ /**
+ * @param userID ID of user for which a neighborhood will be computed
+ * @return {@link Collection} of {@link User}s in the neighborhood
+ * @throws org.apache.mahout.cf.taste.common.TasteException if an error occurs while accessing data
+ */
+ Collection<User> getUserNeighborhood(Object userID) throws TasteException;
+
+}
Added: lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/recommender/ClusteringRecommender.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/recommender/ClusteringRecommender.java?rev=654943&view=auto
==============================================================================
--- lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/recommender/ClusteringRecommender.java (added)
+++ lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/recommender/ClusteringRecommender.java Fri May 9 14:35:12 2008
@@ -0,0 +1,48 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.mahout.cf.taste.recommender;
+
+import org.apache.mahout.cf.taste.common.TasteException;
+import org.apache.mahout.cf.taste.model.User;
+
+import java.util.Collection;
+
+/**
+ * <p>Interface implemented by "clustering" recommenders.</p>
+ */
+public interface ClusteringRecommender extends Recommender {
+
+ /**
+ * <p>Returns the cluster of users to which the given {@link User}, denoted by user ID,
+ * belongs.</p>
+ *
+ * @param userID user ID for which to find a cluster
+ * @return {@link Collection} of {@link User}s in the requested user's cluster
+ * @throws TasteException if an error occurs while accessing the {@link org.apache.mahout.cf.taste.model.DataModel}
+ */
+ Collection<User> getCluster(Object userID) throws TasteException;
+
+ /**
+ * <p>Returns all clusters of users.</p>
+ *
+ * @return {@link Collection} of {@link Collection}s of {@link User}s
+ * @throws TasteException if an error occurs while accessing the {@link org.apache.mahout.cf.taste.model.DataModel}
+ */
+ Collection<Collection<User>> getClusters() throws TasteException;
+
+}
Added: lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/recommender/ItemBasedRecommender.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/recommender/ItemBasedRecommender.java?rev=654943&view=auto
==============================================================================
--- lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/recommender/ItemBasedRecommender.java (added)
+++ lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/recommender/ItemBasedRecommender.java Fri May 9 14:35:12 2008
@@ -0,0 +1,92 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.mahout.cf.taste.recommender;
+
+import org.apache.mahout.cf.taste.common.TasteException;
+import org.apache.mahout.cf.taste.impl.common.Pair;
+import org.apache.mahout.cf.taste.model.Item;
+
+import java.util.List;
+
+/**
+ * <p>Interface implemented by "item-based" recommenders.</p>
+ */
+public interface ItemBasedRecommender extends Recommender {
+
+ /**
+ * @param itemID ID of {@link Item} for which to find most similar other {@link Item}s
+ * @param howMany desired number of most similar {@link Item}s to find
+ * @return {@link Item}s most similar to the given item, ordered from most similar to least
+ * @throws TasteException if an error occurs while accessing the {@link org.apache.mahout.cf.taste.model.DataModel}
+ */
+ List<RecommendedItem> mostSimilarItems(Object itemID, int howMany) throws TasteException;
+
+ /**
+ * @param itemID ID of {@link Item} for which to find most similar other {@link Item}s
+ * @param howMany desired number of most similar {@link Item}s to find
+ * @param rescorer {@link Rescorer} which can adjust item-item correlation
+ * estimates used to determine most similar items
+ * @return {@link Item}s most similar to the given item, ordered from most similar to least
+ * @throws TasteException if an error occurs while accessing the {@link org.apache.mahout.cf.taste.model.DataModel}
+ */
+ List<RecommendedItem> mostSimilarItems(Object itemID,
+ int howMany,
+ Rescorer<Pair<Item, Item>> rescorer) throws TasteException;
+
+ /**
+ * @param itemIDs IDs of {@link Item} for which to find most similar other {@link Item}s
+ * @param howMany desired number of most similar {@link Item}s to find
+ * estimates used to determine most similar items
+ * @return {@link Item}s most similar to the given items, ordered from most similar to least
+ * @throws TasteException if an error occurs while accessing the {@link org.apache.mahout.cf.taste.model.DataModel}
+ */
+ List<RecommendedItem> mostSimilarItems(List<Object> itemIDs, int howMany) throws TasteException;
+
+ /**
+ * @param itemIDs IDs of {@link Item} for which to find most similar other {@link Item}s
+ * @param howMany desired number of most similar {@link Item}s to find
+ * @param rescorer {@link Rescorer} which can adjust item-item correlation
+ * estimates used to determine most similar items
+ * @return {@link Item}s most similar to the given items, ordered from most similar to least
+ * @throws TasteException if an error occurs while accessing the {@link org.apache.mahout.cf.taste.model.DataModel}
+ */
+ List<RecommendedItem> mostSimilarItems(List<Object> itemIDs,
+ int howMany,
+ Rescorer<Pair<Item, Item>> rescorer) throws TasteException;
+
+ /**
+ * <p>Lists the {@link Item}s that were most influential in recommending a given item to a given user.
+ * Exactly how this is determined is left to the implementation, but, generally this will return items
+ * that the user prefers and that are similar to the given item.</p>
+ *
+ * <p>This returns a {@link List} of {@link RecommendedItem} which is a little misleading since it's
+ * returning recommend<strong>ing</strong> items, but, I thought it more natural to just reuse this
+ * class since it encapsulates an {@link Item} and value. The value here does not necessarily have
+ * a consistent interpretation or expected range; it will be higher the more influential the {@link Item}
+ * was in the recommendation.</p>
+ *
+ * @param userID ID of {@link org.apache.mahout.cf.taste.model.User} who was recommended the {@link Item}
+ * @param itemID ID of {@link Item} that was recommended
+ * @param howMany maximum number of {@link Item}s to return
+ * @return {@link List} of {@link RecommendedItem}, ordered from most influential in recommended the given
+ * {@link Item} to least
+ * @throws TasteException if an error occurs while accessing the {@link org.apache.mahout.cf.taste.model.DataModel}
+ */
+ List<RecommendedItem> recommendedBecause(Object userID, Object itemID, int howMany) throws TasteException;
+
+}
Added: lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/recommender/RecommendedItem.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/recommender/RecommendedItem.java?rev=654943&view=auto
==============================================================================
--- lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/recommender/RecommendedItem.java (added)
+++ lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/recommender/RecommendedItem.java Fri May 9 14:35:12 2008
@@ -0,0 +1,43 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.mahout.cf.taste.recommender;
+
+import org.apache.mahout.cf.taste.model.Item;
+
+/**
+ * <p>Implementations encapsulate items that are recommended, and include
+ * the {@link org.apache.mahout.cf.taste.model.Item} recommended and a value expressing
+ * the strength of the preference.</p>
+ */
+public interface RecommendedItem extends Comparable<RecommendedItem> {
+
+ /**
+ * @return the recommended {@link Item}
+ */
+ Item getItem();
+
+ /**
+ * <p>A value expressing the strength of the preference for the recommended
+ * {@link Item}. The range of the values depends on the implementation.
+ * Implementations must use larger values to express stronger preference.</p>
+ *
+ * @return strength of the preference
+ */
+ double getValue();
+
+}
Added: lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/recommender/Recommender.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/recommender/Recommender.java?rev=654943&view=auto
==============================================================================
--- lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/recommender/Recommender.java (added)
+++ lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/recommender/Recommender.java Fri May 9 14:35:12 2008
@@ -0,0 +1,83 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.mahout.cf.taste.recommender;
+
+import org.apache.mahout.cf.taste.common.Refreshable;
+import org.apache.mahout.cf.taste.common.TasteException;
+import org.apache.mahout.cf.taste.model.DataModel;
+import org.apache.mahout.cf.taste.model.Item;
+
+import java.util.List;
+
+/**
+ * <p>Implementations of this interface can recommend {@link Item}s for a
+ * {@link org.apache.mahout.cf.taste.model.User}. Implementations will likely take advantage of several
+ * classes in other packages here to compute this.</p>
+ */
+public interface Recommender extends Refreshable {
+
+ /**
+ * @param userID user for which recommendations are to be computed
+ * @param howMany desired number of recommendations
+ * @return {@link List} of recommended {@link RecommendedItem}s, ordered from most strongly
+ * recommend to least
+ * @throws TasteException if an error occurs while accessing the {@link org.apache.mahout.cf.taste.model.DataModel}
+ */
+ List<RecommendedItem> recommend(Object userID, int howMany) throws TasteException;
+
+ /**
+ * @param userID user for which recommendations are to be computed
+ * @param howMany desired number of recommendations
+ * @param rescorer rescoring function to apply before final list of recommendations is determined
+ * @return {@link List} of recommended {@link RecommendedItem}s, ordered from most strongly
+ * recommend to least
+ * @throws TasteException if an error occurs while accessing the {@link org.apache.mahout.cf.taste.model.DataModel}
+ */
+ List<RecommendedItem> recommend(Object userID, int howMany, Rescorer<Item> rescorer) throws TasteException;
+
+ /**
+ * @param userID user ID whose preference is to be estimated
+ * @param itemID item ID to estimate preference for
+ * @return an estimated preference if the user has not expressed a preference for the item, or else
+ * the user's actual preference for the item. If a preference cannot be estimated, returns
+ * {@link Double#NaN}
+ * @throws TasteException if an error occurs while accessing the {@link org.apache.mahout.cf.taste.model.DataModel}
+ */
+ double estimatePreference(Object userID, Object itemID) throws TasteException;
+
+ /**
+ * @param userID user to set preference for
+ * @param itemID item to set preference for
+ * @param value preference value
+ * @throws TasteException if an error occurs while accessing the {@link org.apache.mahout.cf.taste.model.DataModel}
+ */
+ void setPreference(Object userID, Object itemID, double value) throws TasteException;
+
+ /**
+ * @param userID user from which to remove preference
+ * @param itemID item for which to remove preference
+ * @throws TasteException if an error occurs while accessing the {@link org.apache.mahout.cf.taste.model.DataModel}
+ */
+ void removePreference(Object userID, Object itemID) throws TasteException;
+
+ /**
+ * @return {@link DataModel} used by this {@link Recommender}
+ */
+ DataModel getDataModel();
+
+}
Added: lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/recommender/Rescorer.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/recommender/Rescorer.java?rev=654943&view=auto
==============================================================================
--- lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/recommender/Rescorer.java (added)
+++ lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/recommender/Rescorer.java Fri May 9 14:35:12 2008
@@ -0,0 +1,42 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.mahout.cf.taste.recommender;
+
+/**
+ * <p>A {@link Rescorer} simply assigns a new "score" to a thing like an
+ * {@link org.apache.mahout.cf.taste.model.Item} or {@link org.apache.mahout.cf.taste.model.User} which a {@link Recommender}
+ * is considering returning as a top recommendation. It may be used to arbitrarily re-rank the results
+ * according to application-specific logic before returning recommendations. For example, an application
+ * may want to boost the score of items in a certain category just for one request.</p>
+ *
+ * <p>A {@link Rescorer} can also exclude a thing from consideration entirely by returning <code>true</code>
+ * from {@link #isFiltered(Object)}.</p>
+ */
+public interface Rescorer<T> {
+
+ /**
+ * @param thing thing ({@link org.apache.mahout.cf.taste.model.Item} or
+ * {@link org.apache.mahout.cf.taste.model.User} really) to rescore
+ * @param originalScore original score
+ * @return modified score, or {@link Double#NaN} to indicate that this should be excluded entirely
+ */
+ double rescore(T thing, double originalScore);
+
+ boolean isFiltered(T thing);
+
+}
Added: lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/recommender/UserBasedRecommender.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/recommender/UserBasedRecommender.java?rev=654943&view=auto
==============================================================================
--- lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/recommender/UserBasedRecommender.java (added)
+++ lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/recommender/UserBasedRecommender.java Fri May 9 14:35:12 2008
@@ -0,0 +1,48 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.mahout.cf.taste.recommender;
+
+import org.apache.mahout.cf.taste.common.TasteException;
+import org.apache.mahout.cf.taste.impl.common.Pair;
+import org.apache.mahout.cf.taste.model.User;
+
+import java.util.List;
+
+/**
+ * <p>Interface implemented by "user-based" recommenders.</p>
+ */
+public interface UserBasedRecommender extends Recommender {
+
+ /**
+ * @param userID ID of {@link User} for which to find most similar other {@link User}s
+ * @param howMany desired number of most similar {@link User}s to find
+ * @return {@link User}s most similar to the given user
+ * @throws TasteException if an error occurs while accessing the {@link org.apache.mahout.cf.taste.model.DataModel}
+ */
+ List<User> mostSimilarUsers(Object userID, int howMany) throws TasteException;
+
+ /**
+ * @param userID ID of {@link User} for which to find most similar other {@link User}s
+ * @param howMany desired number of most similar {@link User}s to find
+ * @param rescorer {@link Rescorer} which can adjust user-user correlation
+ * estimates used to determine most similar users
+ * @return {@link User}s most similar to the given user
+ * @throws TasteException if an error occurs while accessing the {@link org.apache.mahout.cf.taste.model.DataModel}
+ */
+ List<User> mostSimilarUsers(Object userID, int howMany, Rescorer<Pair<User, User>> rescorer) throws TasteException;
+
+}
Added: lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/recommender/slopeone/DiffStorage.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/recommender/slopeone/DiffStorage.java?rev=654943&view=auto
==============================================================================
--- lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/recommender/slopeone/DiffStorage.java (added)
+++ lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/recommender/slopeone/DiffStorage.java Fri May 9 14:35:12 2008
@@ -0,0 +1,84 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.mahout.cf.taste.recommender.slopeone;
+
+import org.apache.mahout.cf.taste.common.Refreshable;
+import org.apache.mahout.cf.taste.common.TasteException;
+import org.apache.mahout.cf.taste.impl.common.RunningAverage;
+import org.apache.mahout.cf.taste.model.Item;
+import org.apache.mahout.cf.taste.model.Preference;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * <p>Implementations store item-item preference diffs for a
+ * {@link org.apache.mahout.cf.taste.impl.recommender.slopeone.SlopeOneRecommender}.
+ * It actually does a bit more for this implementation, like listing all items that may be
+ * considered for recommedation, in order to maximize what implementations can do
+ * to optimize the slope-one algorithm.</p>
+ *
+ * @see org.apache.mahout.cf.taste.impl.recommender.slopeone.SlopeOneRecommender
+ */
+public interface DiffStorage extends Refreshable {
+
+ /**
+ * @param itemID1
+ * @param itemID2
+ * @return {@link RunningAverage} encapsulating the average difference in preferences
+ * between items corresponding to <code>itemID1</code> and <code>itemID2</code>, in that direction; that is,
+ * it's the average of item 2's preferences minus item 1's preferences
+ * @throws TasteException
+ */
+ RunningAverage getDiff(Object itemID1, Object itemID2) throws TasteException;
+
+ /**
+ * @param userID user ID to get diffs for
+ * @param itemID itemID to assess
+ * @param prefs user's preferendces
+ * @return {@link List} of {@link RunningAverage} for that user's item-item diffs
+ * @throws TasteException
+ */
+ RunningAverage[] getDiffs(Object userID, Object itemID, Preference[] prefs) throws TasteException;
+
+ /**
+ * @param itemID
+ * @return {@link RunningAverage} encapsulating the average preference for the given item
+ * @throws TasteException
+ */
+ RunningAverage getAverageItemPref(Object itemID) throws TasteException;
+
+ /**
+ * <p>Updates internal data structures to reflect an update in a preference value for an item.</p>
+ *
+ * @param itemID item to update preference value for
+ * @param prefDelta amount by which preference value changed (or its old value, if being removed
+ * @param remove if <code>true</code>, operation reflects a removal rather than change of preference
+ * @throws TasteException
+ */
+ void updateItemPref(Object itemID, double prefDelta, boolean remove) throws TasteException;
+
+ /**
+ * @param userID
+ * @return {@link Item}s that may possibly be recommended to the given user, which may not be all
+ * {@link Item}s since the item-item diff matrix may be sparses
+ * @throws TasteException
+ */
+ Set<Item> getRecommendableItems(Object userID) throws TasteException;
+
+}
Added: lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/transforms/CorrelationTransform.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/transforms/CorrelationTransform.java?rev=654943&view=auto
==============================================================================
--- lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/transforms/CorrelationTransform.java (added)
+++ lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/transforms/CorrelationTransform.java Fri May 9 14:35:12 2008
@@ -0,0 +1,39 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.mahout.cf.taste.transforms;
+
+import org.apache.mahout.cf.taste.common.Refreshable;
+import org.apache.mahout.cf.taste.common.TasteException;
+
+/**
+ * <p>Implementations encapsulate some transformation on correlation values between two
+ * things, where things might be {@link org.apache.mahout.cf.taste.model.User}s or {@link org.apache.mahout.cf.taste.model.Item}s or
+ * something else.</p>
+ */
+public interface CorrelationTransform<T> extends Refreshable {
+
+ /**
+ * @param thing1
+ * @param thing2
+ * @param value original correlation between thing1 and thing2
+ * (should be in [-1,1])
+ * @return transformed correlation (should be in [-1,1])
+ */
+ double transformCorrelation(T thing1, T thing2, double value) throws TasteException;
+
+}
Added: lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/transforms/PreferenceTransform2.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/transforms/PreferenceTransform2.java?rev=654943&view=auto
==============================================================================
--- lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/transforms/PreferenceTransform2.java (added)
+++ lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/transforms/PreferenceTransform2.java Fri May 9 14:35:12 2008
@@ -0,0 +1,38 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.mahout.cf.taste.transforms;
+
+import org.apache.mahout.cf.taste.common.Refreshable;
+import org.apache.mahout.cf.taste.common.TasteException;
+import org.apache.mahout.cf.taste.model.Preference;
+
+/**
+ * <p>Implementations encapsulate a transform on a {@link Preference}'s value. These transformations are
+ * typically applied to values before they are used to compute a correlation value. They are typically not
+ * applied elsewhere; in particular {@link org.apache.mahout.cf.taste.model.DataModel}s no longer use a transform
+ * like this to transform all of their preference values at the source.</p>
+ *
+ * <p>This class sort of replaces the <code>PreferenceTransform</code> interface. It operates similarly, but
+ * is applied a bit differently within the framework. As such I wanted to make this an entirely new interface,
+ * but couldn't pick a name that seemed as applicable. Hence the simplistic name.</p>
+ */
+public interface PreferenceTransform2 extends Refreshable {
+
+ double getTransformedValue(Preference pref) throws TasteException;
+
+}
\ No newline at end of file
Added: lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/web/RecommenderService.jws
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/web/RecommenderService.jws?rev=654943&view=auto
==============================================================================
--- lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/web/RecommenderService.jws (added)
+++ lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/web/RecommenderService.jws Fri May 9 14:35:12 2008
@@ -0,0 +1,74 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Yes, no package statement. Since this JWS file is deployed at the server
+// root, it needs to not be in a package in order for Axis to find its generated
+// class file properly
+
+import org.apache.mahout.cf.taste.recommender.RecommendedItem;
+import org.apache.mahout.cf.taste.recommender.Recommender;
+import org.apache.mahout.cf.taste.web.RecommenderSingleton;
+
+import java.util.List;
+
+/**
+ * <p>A JWS file which exposes a {@link Recommender} as a web service via Axis.
+ * This service needs to be deployed alongside <code>RecommenderServlet</code>.</p>
+ *
+ * <p>This class exposes a subset of the {@link Recommender} API. In particular it
+ * does not support {@link Recommender#getDataModel()}
+ * or {@link Recommender#recommend(Object, int, org.apache.mahout.cf.taste.recommender.Rescorer)}
+ * since these involve data types which can't be transmitted in a web service.</p>
+ */
+public final class RecommenderService {
+
+ private final Recommender recommender;
+
+ public RecommenderService() {
+ // Assuming that this has already been initialized by RecommenderServlet
+ recommender = RecommenderSingleton.getInstance().getRecommender();
+ }
+
+ public String[][] recommend(final String userID, final int howMany) throws Exception {
+ final List<RecommendedItem> recommendations = recommender.recommend(userID, howMany);
+ final String[][] itemIDsAndPrefs = new String[recommendations.size()][2];
+ int i = 0;
+ for (final RecommendedItem item : recommendations) {
+ itemIDsAndPrefs[i][0] = String.valueOf(item.getValue());
+ itemIDsAndPrefs[i][1] = item.getItem().getID().toString();
+ i++;
+ }
+ return itemIDsAndPrefs;
+ }
+
+ public double estimatePreference(final String userID, final String itemID) throws Exception {
+ return recommender.estimatePreference(userID, itemID);
+ }
+
+ public void setPreference(final String userID, final String itemID, final double value) throws Exception {
+ recommender.setPreference(userID, itemID, value);
+ }
+
+ public void removePreference(final String userID, final String itemID) throws Exception {
+ recommender.removePreference(userID, itemID);
+ }
+
+ public void refresh() throws Exception {
+ recommender.refresh();
+ }
+
+}
Added: lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/web/RecommenderServlet.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/web/RecommenderServlet.java?rev=654943&view=auto
==============================================================================
--- lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/web/RecommenderServlet.java (added)
+++ lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/web/RecommenderServlet.java Fri May 9 14:35:12 2008
@@ -0,0 +1,161 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.mahout.cf.taste.web;
+
+import org.apache.mahout.cf.taste.common.TasteException;
+import org.apache.mahout.cf.taste.impl.model.ByValuePreferenceComparator;
+import org.apache.mahout.cf.taste.model.DataModel;
+import org.apache.mahout.cf.taste.model.Preference;
+import org.apache.mahout.cf.taste.recommender.RecommendedItem;
+import org.apache.mahout.cf.taste.recommender.Recommender;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * <p>A servlet which returns recommendations, as its name implies. The servlet accepts GET and POST
+ * HTTP requests, and looks for two parameters:</p>
+ *
+ * <ul>
+ * <li><em>userID</em>: the user ID for which to produce recommendations</li>
+ * <li><em>howMany</em>: the number of recommendations to produce</li>
+ * <li><em>debug</em>: (optional) output a lot of information that is useful in debugging.
+ * Defaults to false, of course.</li>
+ * </ul>
+ *
+ * <p>The response is text, and contains a list of the IDs of recommended items, in descending
+ * order of relevance, one per line.</p>
+ *
+ * <p>For example, you can get 10 recommendations for user 123 from the following URL (assuming
+ * you are running taste in a web application running locally on port 8080):<br/>
+ * <code>http://localhost:8080/taste/RecommenderServlet?userID=123&howMany=1</code></p>
+ *
+ * <p>This servlet requires one <code>init-param</code> in <code>web.xml</code>: it must find
+ * a parameter named "recommender-class" which is the name of a class that implements
+ * {@link Recommender} and has a no-arg constructor. The servlet will instantiate and use
+ * this {@link Recommender} to produce recommendations.</p>
+ */
+public final class RecommenderServlet extends HttpServlet {
+
+ private static final int NUM_TOP_PREFERENCES = 20;
+ private static final int DEFAULT_HOW_MANY = 20;
+
+ private Recommender recommender;
+
+ @Override
+ public void init(ServletConfig config) throws ServletException {
+ super.init(config);
+ String recommenderClassName = config.getInitParameter("recommender-class");
+ if (recommenderClassName == null) {
+ throw new ServletException("Servlet init-param \"recommender-class\" is not defined");
+ }
+ try {
+ RecommenderSingleton.initializeIfNeeded(recommenderClassName);
+ } catch (TasteException te) {
+ throw new ServletException(te);
+ }
+ recommender = RecommenderSingleton.getInstance().getRecommender();
+ }
+
+ @Override
+ public void doGet(HttpServletRequest request,
+ HttpServletResponse response) throws ServletException {
+
+ String userID = request.getParameter("userID");
+ if (userID == null) {
+ throw new ServletException("userID was not specified");
+ }
+ String howManyString = request.getParameter("howMany");
+ int howMany = howManyString == null ? DEFAULT_HOW_MANY : Integer.parseInt(howManyString);
+ boolean debug = Boolean.valueOf(request.getParameter("debug"));
+
+ try {
+
+ List<RecommendedItem> items = recommender.recommend(userID, howMany);
+
+ response.setContentType("text/plain");
+ response.setCharacterEncoding("UTF-8");
+ response.setHeader("Cache-Control", "no-cache");
+
+ PrintWriter writer = response.getWriter();
+ if (debug) {
+ DataModel dataModel = recommender.getDataModel();
+ writer.print("User:");
+ writer.println(dataModel.getUser(userID));
+ writer.print("Recommender: ");
+ writer.println(recommender);
+ writer.println();
+ writer.print("Top ");
+ writer.print(NUM_TOP_PREFERENCES);
+ writer.println(" Preferences:");
+ Preference[] rawPrefs = dataModel.getUser(userID).getPreferencesAsArray();
+ int length = rawPrefs.length;
+ Preference[] sortedPrefs = new Preference[length];
+ System.arraycopy(rawPrefs, 0, sortedPrefs, 0, length);
+ Arrays.sort(sortedPrefs, Collections.reverseOrder(ByValuePreferenceComparator.getInstance()));
+ // Cap this at 20 just to be brief
+ int max = Math.min(NUM_TOP_PREFERENCES, length);
+ for (int i = 0; i < max; i++) {
+ Preference pref = sortedPrefs[i];
+ writer.print(pref.getValue());
+ writer.print('\t');
+ writer.println(pref.getItem());
+ }
+ writer.println();
+ writer.println("Recommendations:");
+ for (RecommendedItem recommendedItem : items) {
+ writer.print(recommendedItem.getValue());
+ writer.print('\t');
+ writer.println(recommendedItem.getItem());
+ }
+ } else {
+ for (RecommendedItem recommendedItem : items) {
+ writer.print(recommendedItem.getValue());
+ writer.print('\t');
+ writer.println(recommendedItem.getItem().getID());
+ }
+ }
+
+ } catch (TasteException te) {
+ throw new ServletException(te);
+ } catch (IOException ioe) {
+ throw new ServletException(ioe);
+ }
+
+ }
+
+ @Override
+ public void doPost(HttpServletRequest request,
+ HttpServletResponse response) throws ServletException {
+ doGet(request, response);
+ }
+
+ @Override
+ public String toString() {
+ return "RecommenderServlet[recommender:" + recommender + ']';
+ }
+
+}
Added: lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/web/RecommenderSingleton.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/web/RecommenderSingleton.java?rev=654943&view=auto
==============================================================================
--- lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/web/RecommenderSingleton.java (added)
+++ lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/web/RecommenderSingleton.java Fri May 9 14:35:12 2008
@@ -0,0 +1,65 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.mahout.cf.taste.web;
+
+import org.apache.mahout.cf.taste.common.TasteException;
+import org.apache.mahout.cf.taste.recommender.Recommender;
+
+/**
+ * <p>A singleton which holds an instance of a {@link Recommender}. This is used to share
+ * a {@link Recommender} between {@link RecommenderServlet} and <code>RecommenderService.jws</code>.</p>
+ */
+public final class RecommenderSingleton {
+
+ private final Recommender recommender;
+
+ private static RecommenderSingleton instance;
+
+ public static synchronized RecommenderSingleton getInstance() {
+ if (instance == null) {
+ throw new IllegalStateException("Not initialized");
+ }
+ return instance;
+ }
+
+ public static synchronized void initializeIfNeeded(String recommenderClassName) throws TasteException {
+ if (instance == null) {
+ instance = new RecommenderSingleton(recommenderClassName);
+ }
+ }
+
+ private RecommenderSingleton(String recommenderClassName) throws TasteException {
+ if (recommenderClassName == null) {
+ throw new IllegalArgumentException("Recommender class name is null");
+ }
+ try {
+ recommender = Class.forName(recommenderClassName).asSubclass(Recommender.class).newInstance();
+ } catch (ClassNotFoundException cnfe) {
+ throw new TasteException(cnfe);
+ } catch (InstantiationException ie) {
+ throw new TasteException(ie);
+ } catch (IllegalAccessException iae) {
+ throw new TasteException(iae);
+ }
+ }
+
+ public Recommender getRecommender() {
+ return recommender;
+ }
+
+}
Added: lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/web/web.xml
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/web/web.xml?rev=654943&view=auto
==============================================================================
--- lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/web/web.xml (added)
+++ lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/web/web.xml Fri May 9 14:35:12 2008
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
+ "http://java.sun.com/dtd/web-app_2_3.dtd">
+
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<web-app>
+
+ <servlet>
+ <servlet-name>taste-recommender</servlet-name>
+ <display-name>Taste Recommender</display-name>
+ <description>Taste recommender servlet</description>
+ <servlet-class>org.apache.mahout.cf.taste.web.RecommenderServlet</servlet-class>
+ <init-param>
+ <param-name>recommender-class</param-name>
+ <param-value>@RECOMMENDER_CLASS@</param-value>
+ </init-param>
+ </servlet>
+
+ <servlet>
+ <servlet-name>axis</servlet-name>
+ <display-name>Apache-Axis Servlet</display-name>
+ <servlet-class>org.apache.axis.transport.http.AxisServlet</servlet-class>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>taste-recommender</servlet-name>
+ <url-pattern>/RecommenderServlet</url-pattern>
+ </servlet-mapping>
+ <servlet-mapping>
+ <servlet-name>axis</servlet-name>
+ <url-pattern>*.jws</url-pattern>
+ </servlet-mapping>
+
+ <!-- The rest of the config is adapted from Axis's default web app -->
+
+ <session-config>
+ <session-timeout>5</session-timeout>
+ </session-config>
+
+ <!-- currently the W3C havent settled on a media type for WSDL;
+ http://www.w3.org/TR/2003/WD-wsdl12-20030303/#ietf-draft
+ for now we go with the basic 'it's XML' response -->
+ <mime-mapping>
+ <extension>wsdl</extension>
+ <mime-type>text/xml</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>xsd</extension>
+ <mime-type>text/xml</mime-type>
+ </mime-mapping>
+
+</web-app>
\ No newline at end of file
Added: lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/common/CommonTest.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/common/CommonTest.java?rev=654943&view=auto
==============================================================================
--- lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/common/CommonTest.java (added)
+++ lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/common/CommonTest.java Fri May 9 14:35:12 2008
@@ -0,0 +1,42 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.mahout.cf.taste.common;
+
+import org.apache.mahout.cf.taste.impl.TasteTestCase;
+
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+
+/**
+ * <p>Tests common classes.</p>
+ */
+public final class CommonTest extends TasteTestCase {
+
+ public void testException() {
+ // Just make sure this all doesn't, ah, throw an exception
+ TasteException te1 = new TasteException();
+ TasteException te2 = new TasteException(te1);
+ TasteException te3 = new TasteException(te2.toString(), te2);
+ TasteException te4 = new TasteException(te3.toString());
+ te4.printStackTrace(new PrintStream(new ByteArrayOutputStream()));
+ te4.printStackTrace(new PrintWriter(new OutputStreamWriter(new ByteArrayOutputStream())));
+ }
+
+}
Added: lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/LoadTest.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/LoadTest.java?rev=654943&view=auto
==============================================================================
--- lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/LoadTest.java (added)
+++ lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/LoadTest.java Fri May 9 14:35:12 2008
@@ -0,0 +1,161 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.mahout.cf.taste.impl;
+
+import junit.textui.TestRunner;
+import org.apache.mahout.cf.taste.common.TasteException;
+import org.apache.mahout.cf.taste.correlation.ItemCorrelation;
+import org.apache.mahout.cf.taste.correlation.UserCorrelation;
+import org.apache.mahout.cf.taste.impl.common.RandomUtils;
+import org.apache.mahout.cf.taste.impl.correlation.PearsonCorrelation;
+import org.apache.mahout.cf.taste.impl.model.GenericDataModel;
+import org.apache.mahout.cf.taste.impl.model.GenericItem;
+import org.apache.mahout.cf.taste.impl.model.GenericPreference;
+import org.apache.mahout.cf.taste.impl.model.GenericUser;
+import org.apache.mahout.cf.taste.impl.neighborhood.NearestNUserNeighborhood;
+import org.apache.mahout.cf.taste.impl.recommender.CachingRecommender;
+import org.apache.mahout.cf.taste.impl.recommender.GenericItemBasedRecommender;
+import org.apache.mahout.cf.taste.impl.recommender.GenericUserBasedRecommender;
+import org.apache.mahout.cf.taste.impl.recommender.slopeone.SlopeOneRecommender;
+import org.apache.mahout.cf.taste.model.DataModel;
+import org.apache.mahout.cf.taste.model.Item;
+import org.apache.mahout.cf.taste.model.Preference;
+import org.apache.mahout.cf.taste.model.User;
+import org.apache.mahout.cf.taste.neighborhood.UserNeighborhood;
+import org.apache.mahout.cf.taste.recommender.Recommender;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * <p>Generates load on the whole implementation, for profiling purposes mostly.</p>
+ */
+public final class LoadTest extends TasteTestCase {
+
+ private static final Logger log = Logger.getLogger(LoadTest.class.getName());
+
+ private static final int NUM_USERS = 1600;
+ private static final int NUM_ITEMS = 800;
+ private static final int NUM_PREFS = 20;
+ private static final int NUM_THREADS = 4;
+
+ private final Random random = RandomUtils.getRandom();
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ setLogLevel(Level.INFO);
+ }
+
+ public void testSlopeOneLoad() throws Exception {
+ DataModel model = createModel();
+ Recommender recommender = new CachingRecommender(new SlopeOneRecommender(model));
+ doTestLoad(recommender, 30);
+ }
+
+ public void testItemLoad() throws Exception {
+ DataModel model = createModel();
+ ItemCorrelation itemCorrelation = new PearsonCorrelation(model);
+ Recommender recommender = new CachingRecommender(new GenericItemBasedRecommender(model, itemCorrelation));
+ doTestLoad(recommender, 120);
+ }
+
+ public void testUserLoad() throws Exception {
+ DataModel model = createModel();
+ UserCorrelation userCorrelation = new PearsonCorrelation(model);
+ UserNeighborhood neighborhood = new NearestNUserNeighborhood(10, userCorrelation, model);
+ Recommender recommender =
+ new CachingRecommender(new GenericUserBasedRecommender(model, neighborhood, userCorrelation));
+ doTestLoad(recommender, 20);
+ }
+
+ private DataModel createModel() {
+
+ List<Item> items = new ArrayList<Item>(NUM_ITEMS);
+ for (int i = 0; i < NUM_ITEMS; i++) {
+ items.add(new GenericItem<String>(String.valueOf(i)));
+ }
+
+ List<User> users = new ArrayList<User>(NUM_USERS);
+ for (int i = 0; i < NUM_USERS; i++) {
+ int numPrefs = random.nextInt(NUM_PREFS) + 1;
+ List<Preference> prefs = new ArrayList<Preference>(numPrefs);
+ for (int j = 0; j < numPrefs; j++) {
+ prefs.add(new GenericPreference(null, items.get(random.nextInt(NUM_ITEMS)), random.nextDouble()));
+ }
+ GenericUser<String> user = new GenericUser<String>(String.valueOf(i), prefs);
+ users.add(user);
+ }
+
+ return new GenericDataModel(users);
+ }
+
+ private void doTestLoad(Recommender recommender, int allowedTimeSec)
+ throws InterruptedException, ExecutionException {
+
+ ExecutorService executor = Executors.newFixedThreadPool(NUM_THREADS);
+ Collection<Future<?>> futures = new ArrayList<Future<?>>(NUM_THREADS);
+ Callable<?> loadTask = new LoadWorker(recommender);
+
+ long start = System.currentTimeMillis();
+ for (int i = 0; i < NUM_THREADS; i++) {
+ futures.add(executor.submit(loadTask));
+ }
+ for (Future<?> future : futures) {
+ future.get();
+ }
+ long end = System.currentTimeMillis();
+ long timeMS = end - start;
+ log.info("Load test completed in " + timeMS + "ms");
+ assertTrue(timeMS < 1000L * (long) allowedTimeSec);
+ }
+
+ private final class LoadWorker implements Callable<Object> {
+
+ private final Recommender recommender;
+
+ private LoadWorker(Recommender recommender) {
+ this.recommender = recommender;
+ }
+
+ public Object call() throws TasteException {
+ for (int i = 0; i < NUM_USERS / 2; i++) {
+ recommender.recommend(String.valueOf(random.nextInt(NUM_USERS)), 10);
+ }
+ recommender.refresh();
+ for (int i = NUM_USERS / 2; i < NUM_USERS; i++) {
+ recommender.recommend(String.valueOf(random.nextInt(NUM_USERS)), 10);
+ }
+ return null;
+ }
+ }
+
+ public static void main(String... args) {
+ TestRunner.run(LoadTest.class);
+ }
+
+}
Added: lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/TasteTestCase.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/TasteTestCase.java?rev=654943&view=auto
==============================================================================
--- lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/TasteTestCase.java (added)
+++ lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/TasteTestCase.java Fri May 9 14:35:12 2008
@@ -0,0 +1,87 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.mahout.cf.taste.impl;
+
+import junit.framework.TestCase;
+import org.apache.mahout.cf.taste.impl.common.RandomUtils;
+import org.apache.mahout.cf.taste.impl.model.GenericDataModel;
+import org.apache.mahout.cf.taste.impl.model.GenericItem;
+import org.apache.mahout.cf.taste.impl.model.GenericPreference;
+import org.apache.mahout.cf.taste.impl.model.GenericUser;
+import org.apache.mahout.cf.taste.model.DataModel;
+import org.apache.mahout.cf.taste.model.Preference;
+import org.apache.mahout.cf.taste.model.User;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.logging.Handler;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public abstract class TasteTestCase extends TestCase {
+
+ /**
+ * "Close enough" value for floating-point comparisons.
+ */
+ public static final double EPSILON = 0.00001;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ // make sure we always show all log output during tests
+ setLogLevel(Level.FINEST);
+ RandomUtils.useTestSeed();
+ }
+
+ protected static void setLogLevel(Level level) {
+ Logger log = Logger.getLogger("org.apache.mahout.cf.taste.impl");
+ log.setLevel(level);
+ for (Handler handler : log.getHandlers()) {
+ handler.setLevel(level);
+ }
+ }
+
+ public static User getUser(String userID, double... values) {
+ List<Preference> prefs = new ArrayList<Preference>(values.length);
+ int i = 0;
+ for (double value : values) {
+ prefs.add(new GenericPreference(null, new GenericItem<String>(String.valueOf(i)), value));
+ i++;
+ }
+ return new GenericUser<String>(userID, prefs);
+ }
+
+ public static DataModel getDataModel(User... users) {
+ return new GenericDataModel(Arrays.asList(users));
+ }
+
+ public static DataModel getDataModel() {
+ return new GenericDataModel(getMockUsers());
+ }
+
+ public static List<User> getMockUsers() {
+ List<User> users = new ArrayList<User>(4);
+ users.add(getUser("test1", 0.1, 0.3));
+ users.add(getUser("test2", 0.2, 0.3, 0.3));
+ users.add(getUser("test3", 0.4, 0.3, 0.5));
+ users.add(getUser("test4", 0.7, 0.3, 0.8));
+ return users;
+ }
+
+}
Added: lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/common/EmptyIteratorTest.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/common/EmptyIteratorTest.java?rev=654943&view=auto
==============================================================================
--- lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/common/EmptyIteratorTest.java (added)
+++ lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/common/EmptyIteratorTest.java Fri May 9 14:35:12 2008
@@ -0,0 +1,49 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.mahout.cf.taste.impl.common;
+
+import org.apache.mahout.cf.taste.impl.TasteTestCase;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+public final class EmptyIteratorTest extends TasteTestCase {
+
+ public void testIterator() {
+ Iterator<Object> mock = new EmptyIterator<Object>();
+ assertFalse(mock.hasNext());
+ try {
+ mock.next();
+ fail("Should have thrown NoSuchElementException");
+ } catch (NoSuchElementException nsee) {
+ // good
+ }
+ try {
+ mock.remove();
+ fail("Should have thrown UnsupportedOperationException");
+ } catch (UnsupportedOperationException uoe) {
+ // good
+ }
+ }
+
+ public void testIterable() {
+ Iterable<Object> mock = new EmptyIterable<Object>();
+ assertNotNull(mock.iterator());
+ }
+
+}
Added: lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/common/FastMapTest.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/common/FastMapTest.java?rev=654943&view=auto
==============================================================================
--- lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/common/FastMapTest.java (added)
+++ lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/common/FastMapTest.java Fri May 9 14:35:12 2008
@@ -0,0 +1,199 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.mahout.cf.taste.impl.common;
+
+import org.apache.mahout.cf.taste.impl.TasteTestCase;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+
+/**
+ * <p>Tests {@link FastMap}.</p>
+ */
+public final class FastMapTest extends TasteTestCase {
+
+ public void testPutAndGet() {
+ FastMap<String, String> map = new FastMap<String, String>();
+ assertNull(map.get("foo"));
+ map.put("foo", "bar");
+ assertEquals("bar", map.get("foo"));
+ }
+
+ public void testRemove() {
+ Map<String, String> map = new FastMap<String, String>();
+ map.put("foo", "bar");
+ map.remove("foo");
+ assertEquals(0, map.size());
+ assertTrue(map.isEmpty());
+ assertNull(map.get("foo"));
+ }
+
+ public void testClear() {
+ Map<String, String> map = new FastMap<String, String>();
+ map.put("foo", "bar");
+ map.clear();
+ assertEquals(0, map.size());
+ assertTrue(map.isEmpty());
+ assertNull(map.get("foo"));
+ }
+
+ public void testSizeEmpty() {
+ Map<String, String> map = new FastMap<String, String>();
+ assertEquals(0, map.size());
+ assertTrue(map.isEmpty());
+ map.put("foo", "bar");
+ assertEquals(1, map.size());
+ assertFalse(map.isEmpty());
+ map.remove("foo");
+ assertEquals(0, map.size());
+ assertTrue(map.isEmpty());
+ }
+
+ public void testContains() {
+ FastMap<String, String> map = buildTestFastMap();
+ assertTrue(map.containsKey("foo"));
+ assertTrue(map.containsKey("baz"));
+ assertTrue(map.containsKey("alpha"));
+ assertTrue(map.containsValue("bar"));
+ assertTrue(map.containsValue("bang"));
+ assertTrue(map.containsValue("beta"));
+ assertFalse(map.containsKey("something"));
+ assertFalse(map.containsValue("something"));
+ }
+
+ public void testNull() {
+ Map<String, String> map = new FastMap<String, String>();
+ try {
+ map.put(null, "bar");
+ fail("Should have thrown NullPointerException");
+ } catch (NullPointerException npe) {
+ // good
+ }
+ try {
+ map.put("foo", null);
+ fail("Should have thrown NullPointerException");
+ } catch (NullPointerException npe) {
+ // good
+ }
+ assertNull(map.get(null));
+ }
+
+ public void testRehash() {
+ FastMap<String, String> map = buildTestFastMap();
+ map.remove("foo");
+ map.rehash();
+ assertNull(map.get("foo"));
+ assertEquals("bang", map.get("baz"));
+ }
+
+ public void testGrow() {
+ FastMap<String, String> map = new FastMap<String, String>(1, FastMap.NO_MAX_SIZE);
+ map.put("foo", "bar");
+ map.put("baz", "bang");
+ assertEquals("bar", map.get("foo"));
+ assertEquals("bang", map.get("baz"));
+ }
+
+ public void testKeySet() {
+ FastMap<String, String> map = buildTestFastMap();
+ Collection<String> expected = new HashSet<String>(3);
+ expected.add("foo");
+ expected.add("baz");
+ expected.add("alpha");
+ Set<String> actual = map.keySet();
+ assertTrue(expected.containsAll(actual));
+ assertTrue(actual.containsAll(expected));
+ }
+
+ public void testValues() {
+ FastMap<String, String> map = buildTestFastMap();
+ Collection<String> expected = new HashSet<String>(3);
+ expected.add("bar");
+ expected.add("bang");
+ expected.add("beta");
+ Collection<String> actual = map.values();
+ assertTrue(expected.containsAll(actual));
+ assertTrue(actual.containsAll(expected));
+ }
+
+ public void testEntrySet() {
+ FastMap<String, String> map = buildTestFastMap();
+ Set<Map.Entry<String, String>> actual = map.entrySet();
+ Collection<String> expectedKeys = new HashSet<String>(3);
+ expectedKeys.add("foo");
+ expectedKeys.add("baz");
+ expectedKeys.add("alpha");
+ Collection<String> expectedValues = new HashSet<String>(3);
+ expectedValues.add("bar");
+ expectedValues.add("bang");
+ expectedValues.add("beta");
+ assertEquals(3, actual.size());
+ for (Map.Entry<String, String> entry : actual) {
+ expectedKeys.remove(entry.getKey());
+ expectedValues.remove(entry.getValue());
+ }
+ assertEquals(0, expectedKeys.size());
+ assertEquals(0, expectedValues.size());
+ }
+
+ public void testVersusHashMap() {
+ Map<Integer, String> actual = new FastMap<Integer, String>(1, 1000000);
+ Map<Integer, String> expected = new HashMap<Integer, String>(1000000);
+ Random r = RandomUtils.getRandom();
+ for (int i = 0; i < 1000000; i++) {
+ double d = r.nextDouble();
+ Integer key = r.nextInt(100);
+ if (d < 0.4) {
+ assertEquals(expected.get(key), actual.get(key));
+ } else {
+ if (d < 0.7) {
+ assertEquals(expected.put(key, "foo"), actual.put(key, "foo"));
+ } else {
+ assertEquals(expected.remove(key), actual.remove(key));
+ }
+ assertEquals(expected.size(), actual.size());
+ assertEquals(expected.isEmpty(), actual.isEmpty());
+ }
+ }
+ }
+
+ public void testMaxSize() {
+ Map<String, String> map = new FastMap<String, String>(1, 1);
+ map.put("foo", "bar");
+ assertEquals(1, map.size());
+ map.put("baz", "bang");
+ assertEquals(1, map.size());
+ assertNull(map.get("foo"));
+ map.put("baz", "buzz");
+ assertEquals(1, map.size());
+ assertEquals("buzz", map.get("baz"));
+ }
+
+ private static FastMap<String, String> buildTestFastMap() {
+ FastMap<String, String> map = new FastMap<String, String>();
+ map.put("foo", "bar");
+ map.put("baz", "bang");
+ map.put("alpha", "beta");
+ return map;
+ }
+
+}
Added: lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/common/IteratorUtilsTest.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/common/IteratorUtilsTest.java?rev=654943&view=auto
==============================================================================
--- lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/common/IteratorUtilsTest.java (added)
+++ lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/common/IteratorUtilsTest.java Fri May 9 14:35:12 2008
@@ -0,0 +1,69 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.mahout.cf.taste.impl.common;
+
+import junit.framework.TestCase;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.TreeSet;
+
+public final class IteratorUtilsTest extends TestCase {
+
+ private static final List<String> TEST_DATA;
+
+ static {
+ List<String> temp = new ArrayList<String>(3);
+ temp.add("bar");
+ temp.add("baz");
+ temp.add("foo");
+ TEST_DATA = Collections.unmodifiableList(temp);
+ }
+
+ public void testArray() {
+ String[] data = TEST_DATA.toArray(new String[3]);
+ assertEquals(TEST_DATA, IteratorUtils.iterableToList(new ArrayIterator<String>(data)));
+ }
+
+ public void testList() {
+ assertEquals(TEST_DATA, IteratorUtils.iterableToList(TEST_DATA));
+ }
+
+ public void testCollection() {
+ Collection<String> data = new TreeSet<String>();
+ data.add("foo");
+ data.add("bar");
+ data.add("baz");
+ assertEquals(TEST_DATA, IteratorUtils.iterableToList(data));
+ }
+
+ public void testComparator() {
+ Collection<String> data = new ArrayList<String>(3);
+ data.add("baz");
+ data.add("bar");
+ data.add("foo");
+ assertEquals(TEST_DATA, IteratorUtils.iterableToList(data, String.CASE_INSENSITIVE_ORDER));
+ }
+
+ public void testEmpty() {
+ assertEquals(0, IteratorUtils.iterableToList(new ArrayList<Object>(0)).size());
+ }
+
+}
Added: lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/common/RunningAverageAndStdDevTest.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/common/RunningAverageAndStdDevTest.java?rev=654943&view=auto
==============================================================================
--- lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/common/RunningAverageAndStdDevTest.java (added)
+++ lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/common/RunningAverageAndStdDevTest.java Fri May 9 14:35:12 2008
@@ -0,0 +1,65 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.mahout.cf.taste.impl.common;
+
+import org.apache.mahout.cf.taste.impl.TasteTestCase;
+
+public final class RunningAverageAndStdDevTest extends TasteTestCase {
+
+ public void testFull() {
+ doTestAverageAndStdDev(new FullRunningAverageAndStdDev());
+ }
+
+ public void testCompact() {
+ doTestAverageAndStdDev(new CompactRunningAverageAndStdDev());
+ }
+
+ private static void doTestAverageAndStdDev(RunningAverageAndStdDev average) {
+
+ assertEquals(0, average.getCount());
+ assertTrue(Double.isNaN(average.getAverage()));
+ assertTrue(Double.isNaN(average.getStandardDeviation()));
+
+ average.addDatum(6.0);
+ assertEquals(1, average.getCount());
+ assertEquals(6.0, average.getAverage(), EPSILON);
+ assertTrue(Double.isNaN(average.getStandardDeviation()));
+
+ average.addDatum(6.0);
+ assertEquals(2, average.getCount());
+ assertEquals(6.0, average.getAverage(), EPSILON);
+ assertEquals(0.0, average.getStandardDeviation(), EPSILON);
+
+ average.removeDatum(6.0);
+ assertEquals(1, average.getCount());
+ assertEquals(6.0, average.getAverage(), EPSILON);
+ assertTrue(Double.isNaN(average.getStandardDeviation()));
+
+ average.addDatum(-4.0);
+ assertEquals(2, average.getCount());
+ assertEquals(1.0, average.getAverage(), EPSILON);
+ assertEquals(5.0 * 1.4142135623730951, average.getStandardDeviation(), EPSILON);
+
+ average.removeDatum(4.0);
+ assertEquals(1, average.getCount());
+ assertEquals(-2.0, average.getAverage(), EPSILON);
+ assertTrue(Double.isNaN(average.getStandardDeviation()));
+
+ }
+
+}