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 [8/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/test/java/org/apache/mahout/cf/taste/impl/common/RunningAverageTest.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/common/RunningAverageTest.java?rev=654943&view=auto
==============================================================================
--- lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/common/RunningAverageTest.java (added)
+++ lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/common/RunningAverageTest.java Fri May  9 14:35:12 2008
@@ -0,0 +1,67 @@
+/**
+ * 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;
+
+/**
+ * <p>Tests {@link FullRunningAverage}.</p>
+ */
+public final class RunningAverageTest extends TasteTestCase {
+
+  public void testFull() {
+    doTestRunningAverage(new FullRunningAverage());
+  }
+
+  public void testCompact() {
+    doTestRunningAverage(new CompactRunningAverage());
+  }
+
+  private static void doTestRunningAverage(RunningAverage runningAverage) {
+
+    assertEquals(0, runningAverage.getCount());
+    assertTrue(Double.isNaN(runningAverage.getAverage()));
+    runningAverage.addDatum(1.0);
+    assertEquals(1, runningAverage.getCount());
+    assertEquals(1.0, runningAverage.getAverage());
+    runningAverage.addDatum(1.0);
+    assertEquals(2, runningAverage.getCount());
+    assertEquals(1.0, runningAverage.getAverage());
+    runningAverage.addDatum(4.0);
+    assertEquals(3, runningAverage.getCount());
+    assertEquals(2.0, runningAverage.getAverage());
+    runningAverage.addDatum(-4.0);
+    assertEquals(4, runningAverage.getCount());
+    assertEquals(0.5, runningAverage.getAverage());
+
+    runningAverage.removeDatum(-4.0);
+    assertEquals(3, runningAverage.getCount());
+    assertEquals(2.0, runningAverage.getAverage());
+    runningAverage.removeDatum(4.0);
+    assertEquals(2, runningAverage.getCount());
+    assertEquals(1.0, runningAverage.getAverage());
+
+    runningAverage.changeDatum(0.0);
+    assertEquals(2, runningAverage.getCount());
+    assertEquals(1.0, runningAverage.getAverage());
+    runningAverage.changeDatum(2.0);
+    assertEquals(2, runningAverage.getCount());
+    assertEquals(2.0, runningAverage.getAverage());
+  }
+
+}

Added: lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/correlation/AveragingPreferenceInferrerTest.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/correlation/AveragingPreferenceInferrerTest.java?rev=654943&view=auto
==============================================================================
--- lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/correlation/AveragingPreferenceInferrerTest.java (added)
+++ lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/correlation/AveragingPreferenceInferrerTest.java Fri May  9 14:35:12 2008
@@ -0,0 +1,45 @@
+/**
+ * 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.correlation;
+
+import org.apache.mahout.cf.taste.common.TasteException;
+import org.apache.mahout.cf.taste.correlation.PreferenceInferrer;
+import org.apache.mahout.cf.taste.impl.TasteTestCase;
+import org.apache.mahout.cf.taste.impl.model.GenericDataModel;
+import org.apache.mahout.cf.taste.impl.model.GenericItem;
+import org.apache.mahout.cf.taste.model.DataModel;
+import org.apache.mahout.cf.taste.model.Item;
+import org.apache.mahout.cf.taste.model.User;
+
+import java.util.Collections;
+
+/**
+ * <p>Tests {@link AveragingPreferenceInferrer}.</p>
+ */
+public final class AveragingPreferenceInferrerTest extends TasteTestCase {
+
+  public void testInferrer() throws TasteException {
+    User user1 = getUser("test1", 3.0, -2.0, 5.0);
+    Item item = new GenericItem<String>("3");
+    DataModel model = new GenericDataModel(Collections.singletonList(user1));
+    PreferenceInferrer inferrer = new AveragingPreferenceInferrer(model);
+    double inferred = inferrer.inferPreference(user1, item);
+    assertEquals(2.0, inferred);
+  }
+
+}

Added: lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/correlation/CorrelationTestCase.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/correlation/CorrelationTestCase.java?rev=654943&view=auto
==============================================================================
--- lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/correlation/CorrelationTestCase.java (added)
+++ lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/correlation/CorrelationTestCase.java Fri May  9 14:35:12 2008
@@ -0,0 +1,30 @@
+/**
+ * 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.correlation;
+
+import org.apache.mahout.cf.taste.impl.TasteTestCase;
+
+abstract class CorrelationTestCase extends TasteTestCase {
+
+  static void assertCorrelationEquals(double expected, double actual) {
+    assertTrue("Correlation > 1.0", actual <= 1.0);
+    assertTrue("Correlation < -1.0", actual >= -1.0);
+    assertEquals(expected, actual, EPSILON);
+  }
+
+}

Added: lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/correlation/GenericItemCorrelationTest.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/correlation/GenericItemCorrelationTest.java?rev=654943&view=auto
==============================================================================
--- lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/correlation/GenericItemCorrelationTest.java (added)
+++ lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/correlation/GenericItemCorrelationTest.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.correlation;
+
+import org.apache.mahout.cf.taste.correlation.ItemCorrelation;
+import org.apache.mahout.cf.taste.impl.model.GenericItem;
+import org.apache.mahout.cf.taste.model.DataModel;
+import org.apache.mahout.cf.taste.model.Item;
+import org.apache.mahout.cf.taste.model.User;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * <p>Tests {@link GenericItemCorrelation}.</p>
+ */
+public final class GenericItemCorrelationTest extends CorrelationTestCase {
+
+  public void testSimple() {
+    Item item1 = new GenericItem<String>("1");
+    Item item2 = new GenericItem<String>("2");
+    Item item3 = new GenericItem<String>("3");
+    Item item4 = new GenericItem<String>("4");
+    List<GenericItemCorrelation.ItemItemCorrelation> correlations =
+            new ArrayList<GenericItemCorrelation.ItemItemCorrelation>(4);
+    correlations.add(new GenericItemCorrelation.ItemItemCorrelation(item1, item2, 0.5));
+    correlations.add(new GenericItemCorrelation.ItemItemCorrelation(item2, item1, 0.6));
+    correlations.add(new GenericItemCorrelation.ItemItemCorrelation(item1, item1, 0.5));
+    correlations.add(new GenericItemCorrelation.ItemItemCorrelation(item1, item3, 0.3));
+    GenericItemCorrelation itemCorrelation = new GenericItemCorrelation(correlations);
+    assertEquals(1.0, itemCorrelation.itemCorrelation(item1, item1));
+    assertEquals(0.6, itemCorrelation.itemCorrelation(item1, item2));
+    assertEquals(0.6, itemCorrelation.itemCorrelation(item2, item1));
+    assertEquals(0.3, itemCorrelation.itemCorrelation(item1, item3));
+    assertTrue(Double.isNaN(itemCorrelation.itemCorrelation(item3, item4)));
+  }
+
+  public void testFromCorrelation() throws Exception {
+    User user1 = getUser("test1", 1.0, 2.0);
+    User user2 = getUser("test2", 2.0, 5.0);
+    User user3 = getUser("test3", 3.0, 6.0);
+    DataModel dataModel = getDataModel(user1, user2, user3);
+    ItemCorrelation otherCorrelation = new PearsonCorrelation(dataModel);
+    ItemCorrelation itemCorrelation = new GenericItemCorrelation(otherCorrelation, dataModel);
+    assertCorrelationEquals(1.0,
+                            itemCorrelation.itemCorrelation(dataModel.getItem("0"), dataModel.getItem("0")));
+    assertCorrelationEquals(0.960768922830523,
+                            itemCorrelation.itemCorrelation(dataModel.getItem("0"), dataModel.getItem("1")));
+  }
+
+}

Added: lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/correlation/PearsonCorrelationTest.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/correlation/PearsonCorrelationTest.java?rev=654943&view=auto
==============================================================================
--- lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/correlation/PearsonCorrelationTest.java (added)
+++ lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/correlation/PearsonCorrelationTest.java Fri May  9 14:35:12 2008
@@ -0,0 +1,184 @@
+/**
+ * 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.correlation;
+
+import org.apache.mahout.cf.taste.correlation.ItemCorrelation;
+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.Collections;
+
+/**
+ * <p>Tests {@link PearsonCorrelation}.</p>
+ */
+public final class PearsonCorrelationTest extends CorrelationTestCase {
+
+  public void testFullCorrelation1() throws Exception {
+    User user1 = getUser("test1", 3.0, -2.0);
+    User user2 = getUser("test2", 3.0, -2.0);
+    DataModel dataModel = getDataModel(user1, user2);
+    double correlation = new PearsonCorrelation(dataModel).userCorrelation(user1, user2);
+    assertCorrelationEquals(1.0, correlation);
+  }
+
+  public void testFullCorrelation1Weighted() throws Exception {
+    User user1 = getUser("test1", 3.0, -2.0);
+    User user2 = getUser("test2", 3.0, -2.0);
+    DataModel dataModel = getDataModel(user1, user2);
+    double correlation = new PearsonCorrelation(dataModel, true).userCorrelation(user1, user2);
+    assertCorrelationEquals(1.0, correlation);
+  }
+
+  public void testFullCorrelation2() throws Exception {
+    User user1 = getUser("test1", 3.0, 3.0);
+    User user2 = getUser("test2", 3.0, 3.0);
+    DataModel dataModel = getDataModel(user1, user2);
+    double correlation = new PearsonCorrelation(dataModel).userCorrelation(user1, user2);
+    // Yeah, undefined in this case
+    assertTrue(Double.isNaN(correlation));
+  }
+
+  public void testNoCorrelation1() throws Exception {
+    User user1 = getUser("test1", 3.0, -2.0);
+    User user2 = getUser("test2", -3.0, 2.0);
+    DataModel dataModel = getDataModel(user1, user2);
+    double correlation = new PearsonCorrelation(dataModel).userCorrelation(user1, user2);
+    assertCorrelationEquals(-1.0, correlation);
+  }
+
+  public void testNoCorrelation1Weighted() throws Exception {
+    User user1 = getUser("test1", 3.0, -2.0);
+    User user2 = getUser("test2", -3.0, 2.0);
+    DataModel dataModel = getDataModel(user1, user2);
+    double correlation = new PearsonCorrelation(dataModel, true).userCorrelation(user1, user2);
+    assertCorrelationEquals(-1.0, correlation);
+  }
+
+  public void testNoCorrelation2() throws Exception {
+    Preference pref1 = new GenericPreference(null, new GenericItem<String>("1"), 1.0);
+    GenericUser<String> user1 = new GenericUser<String>("test1", Collections.singletonList(pref1));
+    Preference pref2 = new GenericPreference(null, new GenericItem<String>("2"), 1.0);
+    GenericUser<String> user2 = new GenericUser<String>("test2", Collections.singletonList(pref2));
+    DataModel dataModel = getDataModel(user1, user2);
+    double correlation = new PearsonCorrelation(dataModel).userCorrelation(user1, user2);
+    assertTrue(Double.isNaN(correlation));
+  }
+
+  public void testNoCorrelation3() throws Exception {
+    User user1 = getUser("test1", 90.0, 80.0, 70.0);
+    User user2 = getUser("test2", 70.0, 80.0, 90.0);
+    DataModel dataModel = getDataModel(user1, user2);
+    double correlation = new PearsonCorrelation(dataModel).userCorrelation(user1, user2);
+    assertCorrelationEquals(-1.0, correlation);
+  }
+
+  public void testSimple() throws Exception {
+    User user1 = getUser("test1", 1.0, 2.0, 3.0);
+    User user2 = getUser("test2", 2.0, 5.0, 6.0);
+    DataModel dataModel = getDataModel(user1, user2);
+    double correlation = new PearsonCorrelation(dataModel).userCorrelation(user1, user2);
+    assertCorrelationEquals(0.9607689228305227, correlation);
+  }
+
+  public void testSimpleWeighted() throws Exception {
+    User user1 = getUser("test1", 1.0, 2.0, 3.0);
+    User user2 = getUser("test2", 2.0, 5.0, 6.0);
+    DataModel dataModel = getDataModel(user1, user2);
+    double correlation = new PearsonCorrelation(dataModel, true).userCorrelation(user1, user2);
+    assertCorrelationEquals(0.9901922307076306, correlation);
+  }
+
+  public void testFullItemCorrelation1() throws Exception {
+    User user1 = getUser("test1", 3.0, 3.0);
+    User user2 = getUser("test2", -2.0, -2.0);
+    DataModel dataModel = getDataModel(user1, user2);
+    double correlation =
+            new PearsonCorrelation(dataModel).itemCorrelation(dataModel.getItem("0"), dataModel.getItem("1"));
+    assertCorrelationEquals(1.0, correlation);
+  }
+
+  public void testFullItemCorrelation2() throws Exception {
+    User user1 = getUser("test1", 3.0, 3.0);
+    User user2 = getUser("test2", 3.0, 3.0);
+    DataModel dataModel = getDataModel(user1, user2);
+    double correlation =
+            new PearsonCorrelation(dataModel).itemCorrelation(dataModel.getItem("0"), dataModel.getItem("1"));
+    // Yeah, undefined in this case
+    assertTrue(Double.isNaN(correlation));
+  }
+
+  public void testNoItemCorrelation1() throws Exception {
+    User user1 = getUser("test1", 3.0, -3.0);
+    User user2 = getUser("test2", -2.0, 2.0);
+    DataModel dataModel = getDataModel(user1, user2);
+    double correlation =
+            new PearsonCorrelation(dataModel).itemCorrelation(dataModel.getItem("0"), dataModel.getItem("1"));
+    assertCorrelationEquals(-1.0, correlation);
+  }
+
+  public void testNoItemCorrelation2() throws Exception {
+    Preference pref1 = new GenericPreference(null, new GenericItem<String>("1"), 1.0);
+    GenericUser<String> user1 = new GenericUser<String>("test1", Collections.singletonList(pref1));
+    Preference pref2 = new GenericPreference(null, new GenericItem<String>("2"), 1.0);
+    GenericUser<String> user2 = new GenericUser<String>("test2", Collections.singletonList(pref2));
+    DataModel dataModel = getDataModel(user1, user2);
+    double correlation =
+            new PearsonCorrelation(dataModel).itemCorrelation(dataModel.getItem("1"), dataModel.getItem("2"));
+    assertTrue(Double.isNaN(correlation));
+  }
+
+  public void testNoItemCorrelation3() throws Exception {
+    User user1 = getUser("test1", 90.0, 70.0);
+    User user2 = getUser("test2", 80.0, 80.0);
+    User user3 = getUser("test3", 70.0, 90.0);
+    DataModel dataModel = getDataModel(user1, user2, user3);
+    double correlation =
+            new PearsonCorrelation(dataModel).itemCorrelation(dataModel.getItem("0"), dataModel.getItem("1"));
+    assertCorrelationEquals(-1.0, correlation);
+  }
+
+  public void testSimpleItem() throws Exception {
+    User user1 = getUser("test1", 1.0, 2.0);
+    User user2 = getUser("test2", 2.0, 5.0);
+    User user3 = getUser("test3", 3.0, 6.0);
+    DataModel dataModel = getDataModel(user1, user2, user3);
+    double correlation =
+            new PearsonCorrelation(dataModel).itemCorrelation(dataModel.getItem("0"), dataModel.getItem("1"));
+    assertCorrelationEquals(0.9607689228305227, correlation);
+  }
+
+  public void testSimpleItemWeighted() throws Exception {
+    User user1 = getUser("test1", 1.0, 2.0);
+    User user2 = getUser("test2", 2.0, 5.0);
+    User user3 = getUser("test3", 3.0, 6.0);
+    DataModel dataModel = getDataModel(user1, user2, user3);
+    ItemCorrelation itemCorrelation = new PearsonCorrelation(dataModel, true);
+    double correlation = itemCorrelation.itemCorrelation(dataModel.getItem("0"), dataModel.getItem("1"));
+    assertCorrelationEquals(0.9901922307076306, correlation);
+  }
+
+  public void testRefresh() {
+    // Make sure this doesn't throw an exception
+    new PearsonCorrelation(getDataModel()).refresh();
+  }
+
+}

Added: lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/correlation/SpearmanCorrelationTest.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/correlation/SpearmanCorrelationTest.java?rev=654943&view=auto
==============================================================================
--- lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/correlation/SpearmanCorrelationTest.java (added)
+++ lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/correlation/SpearmanCorrelationTest.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.correlation;
+
+import org.apache.mahout.cf.taste.model.DataModel;
+import org.apache.mahout.cf.taste.model.User;
+
+/**
+ * <p>Tests {@link SpearmanCorrelation}.</p>
+ */
+public final class SpearmanCorrelationTest extends CorrelationTestCase {
+
+  public void testFullCorrelation1() throws Exception {
+    User user1 = getUser("test1", 1.0, 2.0, 3.0);
+    User user2 = getUser("test2", 1.0, 2.0, 3.0);
+    DataModel dataModel = getDataModel(user1, user2);
+    double correlation = new SpearmanCorrelation(dataModel).userCorrelation(user1, user2);
+    assertCorrelationEquals(1.0, correlation);
+  }
+
+  public void testFullCorrelation2() throws Exception {
+    User user1 = getUser("test1", 1.0, 2.0, 3.0);
+    User user2 = getUser("test2", 4.0, 5.0, 6.0);
+    DataModel dataModel = getDataModel(user1, user2);
+    double correlation = new SpearmanCorrelation(dataModel).userCorrelation(user1, user2);
+    assertCorrelationEquals(1.0, correlation);
+  }
+
+  public void testAnticorrelation() throws Exception {
+    User user1 = getUser("test1", 1.0, 2.0, 3.0);
+    User user2 = getUser("test2", 3.0, 2.0, 1.0);
+    DataModel dataModel = getDataModel(user1, user2);
+    double correlation = new SpearmanCorrelation(dataModel).userCorrelation(user1, user2);
+    assertCorrelationEquals(-1.0, correlation);
+  }
+
+  public void testSimple() throws Exception {
+    User user1 = getUser("test1", 1.0, 2.0, 3.0);
+    User user2 = getUser("test2", 2.0, 3.0, 1.0);
+    DataModel dataModel = getDataModel(user1, user2);
+    double correlation = new SpearmanCorrelation(dataModel).userCorrelation(user1, user2);
+    assertCorrelationEquals(-0.5, correlation);
+  }
+
+  public void testRefresh() {
+    // Make sure this doesn't throw an exception
+    new SpearmanCorrelation(getDataModel()).refresh();
+  }
+
+}

Added: lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/eval/AverageAbsoluteDifferenceRecommenderEvaluatorTest.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/eval/AverageAbsoluteDifferenceRecommenderEvaluatorTest.java?rev=654943&view=auto
==============================================================================
--- lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/eval/AverageAbsoluteDifferenceRecommenderEvaluatorTest.java (added)
+++ lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/eval/AverageAbsoluteDifferenceRecommenderEvaluatorTest.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.impl.eval;
+
+import org.apache.mahout.cf.taste.common.TasteException;
+import org.apache.mahout.cf.taste.eval.RecommenderBuilder;
+import org.apache.mahout.cf.taste.eval.RecommenderEvaluator;
+import org.apache.mahout.cf.taste.impl.TasteTestCase;
+import org.apache.mahout.cf.taste.impl.recommender.slopeone.SlopeOneRecommender;
+import org.apache.mahout.cf.taste.model.DataModel;
+import org.apache.mahout.cf.taste.recommender.Recommender;
+
+public final class AverageAbsoluteDifferenceRecommenderEvaluatorTest extends TasteTestCase {
+
+  public void testEvaluate() throws Exception {
+    DataModel model = getDataModel();
+    RecommenderBuilder builder = new RecommenderBuilder() {
+      public Recommender buildRecommender(DataModel dataModel) throws TasteException {
+        return new SlopeOneRecommender(dataModel);
+      }
+    };
+    RecommenderEvaluator evaluator =
+            new AverageAbsoluteDifferenceRecommenderEvaluator();
+    double eval = evaluator.evaluate(builder, model, 0.75, 1.0);
+    assertEquals(0.26387685767414826, eval, EPSILON);
+  }
+
+}

Added: lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/eval/GenericRecommenderIRStatsEvaluatorImplTest.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/eval/GenericRecommenderIRStatsEvaluatorImplTest.java?rev=654943&view=auto
==============================================================================
--- lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/eval/GenericRecommenderIRStatsEvaluatorImplTest.java (added)
+++ lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/eval/GenericRecommenderIRStatsEvaluatorImplTest.java Fri May  9 14:35:12 2008
@@ -0,0 +1,46 @@
+/**
+ * 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.eval;
+
+import org.apache.mahout.cf.taste.common.TasteException;
+import org.apache.mahout.cf.taste.eval.IRStatistics;
+import org.apache.mahout.cf.taste.eval.RecommenderBuilder;
+import org.apache.mahout.cf.taste.eval.RecommenderIRStatsEvaluator;
+import org.apache.mahout.cf.taste.impl.TasteTestCase;
+import org.apache.mahout.cf.taste.impl.recommender.slopeone.SlopeOneRecommender;
+import org.apache.mahout.cf.taste.model.DataModel;
+import org.apache.mahout.cf.taste.recommender.Recommender;
+
+public final class GenericRecommenderIRStatsEvaluatorImplTest extends TasteTestCase {
+
+  public void testEvaluate() throws Exception {
+    DataModel model = getDataModel();
+    RecommenderBuilder builder = new RecommenderBuilder() {
+      public Recommender buildRecommender(DataModel dataModel) throws TasteException {
+        return new SlopeOneRecommender(dataModel);
+      }
+    };
+    RecommenderIRStatsEvaluator evaluator = new GenericRecommenderIRStatsEvaluator();
+    IRStatistics stats = evaluator.evaluate(builder, model, 5, 0.2, 1.0);
+    assertNotNull(stats);
+    assertEquals(0.2, stats.getPrecision(), EPSILON);
+    assertEquals(1.0, stats.getRecall(), EPSILON);
+    assertEquals(0.33333, stats.getF1Measure(), EPSILON);
+  }
+
+}
\ No newline at end of file

Added: lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/eval/RMSRecommenderEvaluatorTest.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/eval/RMSRecommenderEvaluatorTest.java?rev=654943&view=auto
==============================================================================
--- lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/eval/RMSRecommenderEvaluatorTest.java (added)
+++ lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/eval/RMSRecommenderEvaluatorTest.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.impl.eval;
+
+import org.apache.mahout.cf.taste.common.TasteException;
+import org.apache.mahout.cf.taste.eval.RecommenderBuilder;
+import org.apache.mahout.cf.taste.eval.RecommenderEvaluator;
+import org.apache.mahout.cf.taste.impl.TasteTestCase;
+import org.apache.mahout.cf.taste.impl.recommender.slopeone.SlopeOneRecommender;
+import org.apache.mahout.cf.taste.model.DataModel;
+import org.apache.mahout.cf.taste.recommender.Recommender;
+
+public final class RMSRecommenderEvaluatorTest extends TasteTestCase {
+
+  public void testEvaluate() throws Exception {
+    DataModel model = getDataModel();
+    RecommenderBuilder builder = new RecommenderBuilder() {
+      public Recommender buildRecommender(DataModel dataModel) throws TasteException {
+        return new SlopeOneRecommender(dataModel);
+      }
+    };
+    RecommenderEvaluator evaluator = new RMSRecommenderEvaluator();
+    double eval = evaluator.evaluate(builder, model, 0.75, 1.0);
+    assertEquals(0.26387685767414826, eval, EPSILON);
+  }
+
+}
\ No newline at end of file

Added: lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/model/file/FileDataModelTest.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/model/file/FileDataModelTest.java?rev=654943&view=auto
==============================================================================
--- lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/model/file/FileDataModelTest.java (added)
+++ lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/model/file/FileDataModelTest.java Fri May  9 14:35:12 2008
@@ -0,0 +1,150 @@
+/**
+ * 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.model.file;
+
+import org.apache.mahout.cf.taste.common.TasteException;
+import org.apache.mahout.cf.taste.correlation.UserCorrelation;
+import org.apache.mahout.cf.taste.impl.TasteTestCase;
+import org.apache.mahout.cf.taste.impl.correlation.PearsonCorrelation;
+import org.apache.mahout.cf.taste.impl.neighborhood.NearestNUserNeighborhood;
+import org.apache.mahout.cf.taste.impl.recommender.GenericUserBasedRecommender;
+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.neighborhood.UserNeighborhood;
+import org.apache.mahout.cf.taste.recommender.Recommender;
+
+import java.io.File;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * <p>Tests {@link FileDataModel}.</p>
+ */
+public final class FileDataModelTest extends TasteTestCase {
+
+  private static final File testFile = new File("src/test/java/org/apache/mahout/cf/taste/impl/model/file/test1.txt");
+
+  private DataModel model;
+
+  @Override
+  public void setUp() throws Exception {
+    super.setUp();
+    model = new FileDataModel(testFile);
+  }
+
+  public void testFile() throws Exception {
+    UserCorrelation userCorrelation = new PearsonCorrelation(model);
+    UserNeighborhood neighborhood = new NearestNUserNeighborhood(2, userCorrelation, model);
+    Recommender recommender = new GenericUserBasedRecommender(model, neighborhood, userCorrelation);
+    assertEquals(2, recommender.recommend("A123", 3).size());
+    assertEquals(2, recommender.recommend("B234", 3).size());
+    assertEquals(1, recommender.recommend("C345", 3).size());
+
+    // Make sure this doesn't throw an exception
+    model.refresh();
+  }
+
+  public void testItem() throws Exception {
+    assertEquals("456", model.getItem("456").getID());
+  }
+
+  public void testGetItems() throws Exception {
+    Iterable<? extends Item> items = model.getItems();
+    assertNotNull(items);
+    Iterator<? extends Item> it = items.iterator();
+    assertNotNull(it);
+    assertTrue(it.hasNext());
+    assertEquals("123", it.next().getID());
+    assertTrue(it.hasNext());
+    assertEquals("234", it.next().getID());
+    assertTrue(it.hasNext());
+    assertEquals("456", it.next().getID());
+    assertTrue(it.hasNext());
+    assertEquals("654", it.next().getID());
+    assertTrue(it.hasNext());
+    assertEquals("789", it.next().getID());
+    assertFalse(it.hasNext());
+    try {
+      it.next();
+      fail("Should throw NoSuchElementException");
+    } catch (NoSuchElementException nsee) {
+      // good
+    }
+  }
+
+  public void testPreferencesForItem() throws Exception {
+    Iterable<? extends Preference> prefs = model.getPreferencesForItem("456");
+    assertNotNull(prefs);
+    Iterator<? extends Preference> it = prefs.iterator();
+    assertNotNull(it);
+    assertTrue(it.hasNext());
+    Preference pref1 = it.next();
+    assertEquals("A123", pref1.getUser().getID());
+    assertEquals("456", pref1.getItem().getID());
+    assertTrue(it.hasNext());
+    Preference pref2 = it.next();
+    assertEquals("D456", pref2.getUser().getID());
+    assertEquals("456", pref2.getItem().getID());
+    assertFalse(it.hasNext());
+    try {
+      it.next();
+      fail("Should throw NoSuchElementException");
+    } catch (NoSuchElementException nsee) {
+      // good
+    }
+  }
+
+  public void testGetNumUsers() throws Exception {
+    assertEquals(4, model.getNumUsers());
+  }
+
+  public void testSetPreference() throws Exception {
+    try {
+      model.setPreference(null, null, 0.0);
+      fail("Should have thrown UnsupportedOperationException");
+    } catch (UnsupportedOperationException uoe) {
+      // good
+    }
+  }
+
+  public void testRefresh() throws Exception {
+    final AtomicBoolean initialized = new AtomicBoolean(false);
+    Runnable initializer = new Runnable() {
+      public void run() {
+        try {
+          model.getNumUsers();
+          initialized.set(true);
+        } catch (TasteException te) {
+          // oops
+        }
+      }
+    };
+    new Thread(initializer).start();
+    Thread.sleep(1000L); // wait a second for thread to start and call getNumUsers()
+    model.getNumUsers(); // should block
+    assertTrue(initialized.get());
+    assertEquals(4, model.getNumUsers());
+  }
+
+  public void testToString() {
+    assertTrue(model.toString().length() > 0);
+  }
+
+}

Added: lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/model/file/test1.txt
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/model/file/test1.txt?rev=654943&view=auto
==============================================================================
--- lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/model/file/test1.txt (added)
+++ lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/model/file/test1.txt Fri May  9 14:35:12 2008
@@ -0,0 +1,10 @@
+A123,456,0.1
+A123,789,0.6
+A123,654,0.7
+B234,123,0.5
+B234,234,1.0
+C345,789,0.6
+C345,654,0.7
+C345,123,1.0
+C345,234,0.5
+D456,456,0.1
\ No newline at end of file

Added: lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/model/jdbc/MySQLJDBCDataModelTest.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/model/jdbc/MySQLJDBCDataModelTest.java?rev=654943&view=auto
==============================================================================
--- lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/model/jdbc/MySQLJDBCDataModelTest.java (added)
+++ lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/model/jdbc/MySQLJDBCDataModelTest.java Fri May  9 14:35:12 2008
@@ -0,0 +1,208 @@
+/**
+ * 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.model.jdbc;
+
+import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
+import org.apache.mahout.cf.taste.correlation.UserCorrelation;
+import org.apache.mahout.cf.taste.impl.TasteTestCase;
+import org.apache.mahout.cf.taste.impl.correlation.PearsonCorrelation;
+import org.apache.mahout.cf.taste.impl.model.GenericItem;
+import org.apache.mahout.cf.taste.impl.model.GenericUser;
+import static org.apache.mahout.cf.taste.impl.model.jdbc.MySQLJDBCDataModel.DEFAULT_ITEM_ID_COLUMN;
+import static org.apache.mahout.cf.taste.impl.model.jdbc.MySQLJDBCDataModel.DEFAULT_PREFERENCE_COLUMN;
+import static org.apache.mahout.cf.taste.impl.model.jdbc.MySQLJDBCDataModel.DEFAULT_PREFERENCE_TABLE;
+import static org.apache.mahout.cf.taste.impl.model.jdbc.MySQLJDBCDataModel.DEFAULT_USER_ID_COLUMN;
+import org.apache.mahout.cf.taste.impl.neighborhood.NearestNUserNeighborhood;
+import org.apache.mahout.cf.taste.impl.recommender.GenericUserBasedRecommender;
+import org.apache.mahout.cf.taste.impl.recommender.slopeone.MemoryDiffStorage;
+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 org.apache.mahout.cf.taste.recommender.slopeone.DiffStorage;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * <p>Tests {@link MySQLJDBCDataModel}.</p>
+ *
+ * <p>Requires a MySQL 4.x+ database running on the localhost, with a passwordless user named "mysql" available,
+ * a database named "test".</p>
+ */
+public final class MySQLJDBCDataModelTest extends TasteTestCase {
+
+  private DataModel model;
+
+  @Override
+  public void setUp() throws Exception {
+
+    super.setUp();
+
+    MysqlDataSource dataSource = new MysqlDataSource();
+    dataSource.setUser("mysql");
+    dataSource.setDatabaseName("test");
+    dataSource.setServerName("localhost");
+
+    Connection connection = dataSource.getConnection();
+    try {
+
+      PreparedStatement dropStatement =
+        connection.prepareStatement("DROP TABLE IF EXISTS " + DEFAULT_PREFERENCE_TABLE);
+      try {
+        dropStatement.execute();
+      } finally {
+        dropStatement.close();
+      }
+
+      PreparedStatement createStatement =
+        connection.prepareStatement("CREATE TABLE " + DEFAULT_PREFERENCE_TABLE + " (" +
+                                    DEFAULT_USER_ID_COLUMN + " VARCHAR(4) NOT NULL, " +
+                                    DEFAULT_ITEM_ID_COLUMN + " VARCHAR(4) NOT NULL, " +
+                                    DEFAULT_PREFERENCE_COLUMN + " FLOAT NOT NULL, " +
+                                    "PRIMARY KEY (" + DEFAULT_USER_ID_COLUMN + ", " +
+                                    DEFAULT_ITEM_ID_COLUMN + "), " +
+                                    "INDEX (" + DEFAULT_USER_ID_COLUMN + "), " +
+                                    "INDEX (" + DEFAULT_ITEM_ID_COLUMN + ") )");
+      try {
+        createStatement.execute();
+      } finally {
+        createStatement.close();
+      }
+
+      PreparedStatement insertStatement =
+        connection.prepareStatement("INSERT INTO " + DEFAULT_PREFERENCE_TABLE + " VALUES (?, ?, ?)");
+      try {
+        String[] users =
+                new String[]{"A123", "A123", "A123", "B234", "B234", "C345", "C345", "C345", "C345", "D456"};
+        String[] itemIDs =
+                new String[]{"456", "789", "654", "123", "234", "789", "654", "123", "234", "456"};
+        double[] preferences = new double[]{0.1, 0.6, 0.7, 0.5, 1.0, 0.6, 0.7, 1.0, 0.5, 0.1};
+        for (int i = 0; i < users.length; i++) {
+          insertStatement.setString(1, users[i]);
+          insertStatement.setString(2, itemIDs[i]);
+          insertStatement.setDouble(3, preferences[i]);
+          insertStatement.execute();
+        }
+      } finally {
+        insertStatement.close();
+      }
+
+    } finally {
+      connection.close();
+    }
+
+    model = new MySQLJDBCDataModel(dataSource);
+  }
+
+  public void testStatements() throws Exception {
+    assertEquals(4, model.getNumUsers());
+    assertEquals(5, model.getNumItems());
+    assertEquals(new GenericUser<String>("A123", Collections.<Preference>emptyList()), model.getUser("A123"));
+    assertEquals(new GenericItem<String>("456"), model.getItem("456"));
+    Preference pref = model.getUser("A123").getPreferenceFor("456");
+    assertNotNull(pref);
+    assertEquals(0.1, pref.getValue(), EPSILON);
+    model.setPreference("A123", "456", 0.2);
+    Preference pref1 = model.getUser("A123").getPreferenceFor("456");
+    assertNotNull(pref1);
+    assertEquals(0.2, pref1.getValue(), EPSILON);
+    model.removePreference("A123", "456");
+    assertNull(model.getUser("A123").getPreferenceFor("456"));
+  }
+
+  public void testDatabase() throws Exception {
+    UserCorrelation userCorrelation = new PearsonCorrelation(model);
+    UserNeighborhood neighborhood = new NearestNUserNeighborhood(2, userCorrelation, model);
+    Recommender recommender = new GenericUserBasedRecommender(model, neighborhood, userCorrelation);
+    assertEquals(2, recommender.recommend("A123", 3).size());
+    assertEquals(2, recommender.recommend("B234", 3).size());
+    assertEquals(1, recommender.recommend("C345", 3).size());
+
+    // Make sure this doesn't throw an exception
+    model.refresh();
+  }
+
+  public void testItem() throws Exception {
+    assertEquals("456", model.getItem("456").getID());
+  }
+
+  public void testPreferencesForItem() throws Exception {
+    Iterable<? extends Preference> prefs = model.getPreferencesForItem("456");
+    assertNotNull(prefs);
+    Iterator<? extends Preference> it = prefs.iterator();
+    assertNotNull(it);
+    assertTrue(it.hasNext());
+    Preference pref1 = it.next();
+    assertEquals("A123", pref1.getUser().getID());
+    assertEquals("456", pref1.getItem().getID());
+    assertTrue(it.hasNext());
+    Preference pref2 = it.next();
+    assertEquals("D456", pref2.getUser().getID());
+    assertEquals("456", pref2.getItem().getID());
+    assertFalse(it.hasNext());
+    try {
+      it.next();
+      fail("Should throw NoSuchElementException");
+    } catch (NoSuchElementException nsee) {
+      // good
+    }
+  }
+
+  public void testPreferencesForItemOrder() throws Exception {
+    for (Item item : model.getItems()) {
+      Iterable<? extends Preference> prefs = model.getPreferencesForItem(item.getID());
+      User lastUser = null;
+      for (Preference pref : prefs) {
+        User thisUser = pref.getUser();
+        if (lastUser != null) {
+          String lastID = (String) lastUser.getID();
+          String ID = (String) thisUser.getID();
+          assertTrue(lastID.compareTo(ID) < 0);
+        }
+        lastUser = thisUser;
+      }
+    }
+  }
+
+  public void testSetPreference() throws Exception {
+    model.setPreference("A123", "409", 2.0);
+    Preference pref = model.getUser("A123").getPreferenceFor("409");
+    assertNotNull(pref);
+    assertEquals(2.0, pref.getValue());
+    model.setPreference("A123", "409", 1.0);
+    Preference pref1 = model.getUser("A123").getPreferenceFor("409");
+    assertNotNull(pref1);
+    assertEquals(1.0, pref1.getValue());
+  }
+
+  public void testSetPrefMemoryDiffUpdates() throws Exception {
+    DiffStorage diffStorage = new MemoryDiffStorage(model, false, false, Long.MAX_VALUE);
+    Recommender recommender = new SlopeOneRecommender(model, true, true, diffStorage);
+    assertEquals(0.5, diffStorage.getDiff("456", "789").getAverage(), EPSILON);
+    recommender.setPreference("A123", "456", 0.7);
+    assertEquals(-0.1, diffStorage.getDiff("456", "789").getAverage(), EPSILON);
+  }
+
+}

Added: lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/neighborhood/DummyCorrelation.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/neighborhood/DummyCorrelation.java?rev=654943&view=auto
==============================================================================
--- lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/neighborhood/DummyCorrelation.java (added)
+++ lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/neighborhood/DummyCorrelation.java Fri May  9 14:35:12 2008
@@ -0,0 +1,46 @@
+/**
+ * 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.neighborhood;
+
+import org.apache.mahout.cf.taste.correlation.ItemCorrelation;
+import org.apache.mahout.cf.taste.correlation.PreferenceInferrer;
+import org.apache.mahout.cf.taste.correlation.UserCorrelation;
+import org.apache.mahout.cf.taste.model.Item;
+import org.apache.mahout.cf.taste.model.User;
+
+final class DummyCorrelation implements UserCorrelation, ItemCorrelation {
+
+  public double userCorrelation(User user1, User user2) {
+    return 1.0 / Math.abs(user1.getPreferencesAsArray()[0].getValue() -
+                          user2.getPreferencesAsArray()[0].getValue());
+  }
+
+  public double itemCorrelation(Item item1, Item item2) {
+    // Make up something wacky
+    return (double) (item1.hashCode() - item2.hashCode());
+  }
+
+  public void setPreferenceInferrer(PreferenceInferrer inferrer) {
+    throw new UnsupportedOperationException();
+  }
+
+  public void refresh() {
+    // do nothing
+  }
+
+}

Added: lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/neighborhood/NearestNNeighborhoodTest.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/neighborhood/NearestNNeighborhoodTest.java?rev=654943&view=auto
==============================================================================
--- lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/neighborhood/NearestNNeighborhoodTest.java (added)
+++ lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/neighborhood/NearestNNeighborhoodTest.java Fri May  9 14:35:12 2008
@@ -0,0 +1,68 @@
+/**
+ * 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.neighborhood;
+
+import org.apache.mahout.cf.taste.impl.model.GenericDataModel;
+import org.apache.mahout.cf.taste.impl.TasteTestCase;
+import org.apache.mahout.cf.taste.model.DataModel;
+import org.apache.mahout.cf.taste.model.User;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * <p>Tests {@link NearestNUserNeighborhood}.</p>
+ */
+public final class NearestNNeighborhoodTest extends TasteTestCase {
+
+  public void testNeighborhood() throws Exception {
+
+    List<User> users = getMockUsers();
+    DataModel dataModel = new GenericDataModel(users);
+
+    Collection<User> neighborhood =
+            new NearestNUserNeighborhood(1, new DummyCorrelation(), dataModel).getUserNeighborhood("test1");
+    assertNotNull(neighborhood);
+    assertEquals(1, neighborhood.size());
+    assertTrue(neighborhood.contains(users.get(1)));
+
+    Collection<User> neighborhood2 =
+            new NearestNUserNeighborhood(2, new DummyCorrelation(), dataModel).getUserNeighborhood("test2");
+    assertNotNull(neighborhood2);
+    assertEquals(2, neighborhood2.size());
+    assertTrue(neighborhood2.contains(users.get(0)));
+    assertTrue(neighborhood2.contains(users.get(2)));
+
+    Collection<User> neighborhood3 =
+            new NearestNUserNeighborhood(4, new DummyCorrelation(), dataModel).getUserNeighborhood("test4");
+    assertNotNull(neighborhood3);
+    assertEquals(3, neighborhood3.size());
+    assertTrue(neighborhood3.contains(users.get(0)));
+    assertTrue(neighborhood3.contains(users.get(1)));
+    assertTrue(neighborhood3.contains(users.get(2)));
+
+  }
+
+  public void testRefresh() throws Exception {
+    // Make sure this doesn't throw an exception
+    DataModel dataModel = new GenericDataModel(Collections.singletonList(getUser("test1", 0.1)));
+    new NearestNUserNeighborhood(1, new DummyCorrelation(), dataModel).refresh();
+  }
+
+}

Added: lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/neighborhood/ThresholdNeighborhoodTest.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/neighborhood/ThresholdNeighborhoodTest.java?rev=654943&view=auto
==============================================================================
--- lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/neighborhood/ThresholdNeighborhoodTest.java (added)
+++ lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/neighborhood/ThresholdNeighborhoodTest.java Fri May  9 14:35:12 2008
@@ -0,0 +1,66 @@
+/**
+ * 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.neighborhood;
+
+import org.apache.mahout.cf.taste.impl.model.GenericDataModel;
+import org.apache.mahout.cf.taste.impl.TasteTestCase;
+import org.apache.mahout.cf.taste.model.DataModel;
+import org.apache.mahout.cf.taste.model.User;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * <p>Tests {@link ThresholdUserNeighborhood}.</p>
+ */
+public final class ThresholdNeighborhoodTest extends TasteTestCase {
+
+  public void testNeighborhood() throws Exception {
+
+    List<User> users = getMockUsers();
+    DataModel dataModel = new GenericDataModel(users);
+
+    Collection<User> neighborhood =
+            new ThresholdUserNeighborhood(20.0, new DummyCorrelation(), dataModel).getUserNeighborhood("test1");
+    assertNotNull(neighborhood);
+    assertTrue(neighborhood.isEmpty());
+
+    Collection<User> neighborhood2 =
+            new ThresholdUserNeighborhood(10.0, new DummyCorrelation(), dataModel).getUserNeighborhood("test1");
+    assertNotNull(neighborhood2);
+    assertEquals(1, neighborhood2.size());
+    assertTrue(neighborhood2.contains(users.get(1)));
+
+    Collection<User> neighborhood3 =
+            new ThresholdUserNeighborhood(1.0, new DummyCorrelation(), dataModel).getUserNeighborhood("test2");
+    assertNotNull(neighborhood3);
+    assertEquals(3, neighborhood3.size());
+    assertTrue(neighborhood3.contains(users.get(0)));
+    assertTrue(neighborhood3.contains(users.get(2)));
+    assertTrue(neighborhood3.contains(users.get(3)));
+
+  }
+
+  public void testRefresh() throws Exception {
+    // Make sure this doesn't throw an exception
+    DataModel dataModel = new GenericDataModel(Collections.singletonList(getUser("test1", 0.1)));
+    new ThresholdUserNeighborhood(20.0, new DummyCorrelation(), dataModel).refresh();
+  }
+
+}

Added: lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/recommender/CachingRecommenderTest.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/recommender/CachingRecommenderTest.java?rev=654943&view=auto
==============================================================================
--- lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/recommender/CachingRecommenderTest.java (added)
+++ lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/recommender/CachingRecommenderTest.java 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.
+ */
+
+package org.apache.mahout.cf.taste.impl.recommender;
+
+import org.apache.mahout.cf.taste.model.Item;
+import org.apache.mahout.cf.taste.recommender.Recommender;
+import org.apache.mahout.cf.taste.recommender.Rescorer;
+import org.apache.mahout.cf.taste.impl.TasteTestCase;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * <p>Tests {@link CachingRecommender}.</p>
+ */
+public final class CachingRecommenderTest extends TasteTestCase {
+
+  public void testRecommender() throws Exception {
+    AtomicInteger recommendCount = new AtomicInteger();
+    Recommender mockRecommender = new MockRecommender(recommendCount);
+
+    Recommender cachingRecommender = new CachingRecommender(mockRecommender);
+    cachingRecommender.recommend("1", 1);
+    assertEquals(1, recommendCount.get());
+    cachingRecommender.recommend("2", 1);
+    assertEquals(2, recommendCount.get());
+    cachingRecommender.recommend("1", 1);
+    assertEquals(2, recommendCount.get());
+    cachingRecommender.recommend("2", 1);
+    assertEquals(2, recommendCount.get());
+    cachingRecommender.refresh();
+    cachingRecommender.recommend("1", 1);
+    assertEquals(3, recommendCount.get());
+    cachingRecommender.recommend("2", 1);
+    assertEquals(4, recommendCount.get());
+    cachingRecommender.recommend("3", 1);
+    assertEquals(5, recommendCount.get());
+
+    // Results from this recommend() method can't be cached:
+    Rescorer<Item> rescorer = NullRescorer.getItemInstance();
+    cachingRecommender.refresh();
+    cachingRecommender.recommend("1", 1, rescorer);
+    assertEquals(6, recommendCount.get());
+    cachingRecommender.recommend("2", 1, rescorer);
+    assertEquals(7, recommendCount.get());
+    cachingRecommender.recommend("1", 1, rescorer);
+    assertEquals(8, recommendCount.get());
+    cachingRecommender.recommend("2", 1, rescorer);
+    assertEquals(9, recommendCount.get());
+
+    cachingRecommender.refresh();
+    cachingRecommender.estimatePreference("test1", "1");
+    assertEquals(10, recommendCount.get());
+    cachingRecommender.estimatePreference("test1", "2");
+    assertEquals(11, recommendCount.get());
+    cachingRecommender.estimatePreference("test1", "2");
+    assertEquals(11, recommendCount.get());
+  }
+
+}

Added: lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/recommender/GenericItemBasedRecommenderTest.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/recommender/GenericItemBasedRecommenderTest.java?rev=654943&view=auto
==============================================================================
--- lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/recommender/GenericItemBasedRecommenderTest.java (added)
+++ lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/recommender/GenericItemBasedRecommenderTest.java Fri May  9 14:35:12 2008
@@ -0,0 +1,216 @@
+/**
+ * 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.recommender;
+
+import org.apache.mahout.cf.taste.correlation.ItemCorrelation;
+import org.apache.mahout.cf.taste.impl.correlation.GenericItemCorrelation;
+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.TasteTestCase;
+import org.apache.mahout.cf.taste.model.DataModel;
+import org.apache.mahout.cf.taste.model.Item;
+import org.apache.mahout.cf.taste.model.User;
+import org.apache.mahout.cf.taste.recommender.ItemBasedRecommender;
+import org.apache.mahout.cf.taste.recommender.RecommendedItem;
+import org.apache.mahout.cf.taste.recommender.Recommender;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * <p>Tests {@link GenericItemBasedRecommender}.</p>
+ */
+public final class GenericItemBasedRecommenderTest extends TasteTestCase {
+
+  public void testRecommender() throws Exception {
+    Recommender recommender = buildRecommender();
+    List<RecommendedItem> recommended = recommender.recommend("test1", 1);
+    assertNotNull(recommended);
+    assertEquals(1, recommended.size());
+    RecommendedItem firstRecommended = recommended.get(0);
+    assertEquals(new GenericItem<String>("2"), firstRecommended.getItem());
+    assertEquals(0.1, firstRecommended.getValue(), EPSILON);
+  }
+
+  public void testHowMany() throws Exception {
+    List<User> users = new ArrayList<User>(3);
+    users.add(getUser("test1", 0.1, 0.2));
+    users.add(getUser("test2", 0.2, 0.3, 0.3, 0.6));
+    users.add(getUser("test3", 0.4, 0.4, 0.5, 0.9));
+    users.add(getUser("test4", 0.1, 0.4, 0.5, 0.8, 0.9, 1.0));
+    users.add(getUser("test5", 0.2, 0.3, 0.6, 0.7, 0.1, 0.2));
+    DataModel dataModel = new GenericDataModel(users);
+    Collection<GenericItemCorrelation.ItemItemCorrelation> correlations =
+            new ArrayList<GenericItemCorrelation.ItemItemCorrelation>(6);
+    for (int i = 0; i < 6; i++) {
+      for (int j = i + 1; j < 6; j++) {
+        correlations.add(
+                new GenericItemCorrelation.ItemItemCorrelation(new GenericItem<String>(String.valueOf(i)),
+                                                               new GenericItem<String>(String.valueOf(j)),
+                                                               1.0 / (1.0 + (double) i + (double) j)));
+      }
+    }
+    ItemCorrelation correlation = new GenericItemCorrelation(correlations);
+    Recommender recommender = new GenericItemBasedRecommender(dataModel, correlation);
+    List<RecommendedItem> fewRecommended = recommender.recommend("test1", 2);
+    List<RecommendedItem> moreRecommended = recommender.recommend("test1", 4);
+    for (int i = 0; i < fewRecommended.size(); i++) {
+      assertEquals(fewRecommended.get(i).getItem(), moreRecommended.get(i).getItem());
+    }
+  }
+
+  public void testRescorer() throws Exception {
+    List<User> users = new ArrayList<User>(3);
+    users.add(getUser("test1", 0.1, 0.2));
+    users.add(getUser("test2", 0.2, 0.3, 0.3, 0.6));
+    users.add(getUser("test3", 0.4, 0.4, 0.5, 0.9));
+    DataModel dataModel = new GenericDataModel(users);
+    Item item1 = new GenericItem<String>("0");
+    Item item2 = new GenericItem<String>("1");
+    Item item3 = new GenericItem<String>("2");
+    Item item4 = new GenericItem<String>("3");
+    Collection<GenericItemCorrelation.ItemItemCorrelation> correlations =
+            new ArrayList<GenericItemCorrelation.ItemItemCorrelation>(6);
+    correlations.add(new GenericItemCorrelation.ItemItemCorrelation(item1, item2, 1.0));
+    correlations.add(new GenericItemCorrelation.ItemItemCorrelation(item1, item3, 0.5));
+    correlations.add(new GenericItemCorrelation.ItemItemCorrelation(item1, item4, 0.2));
+    correlations.add(new GenericItemCorrelation.ItemItemCorrelation(item2, item3, 0.7));
+    correlations.add(new GenericItemCorrelation.ItemItemCorrelation(item2, item4, 0.5));
+    correlations.add(new GenericItemCorrelation.ItemItemCorrelation(item3, item4, 0.9));
+    ItemCorrelation correlation = new GenericItemCorrelation(correlations);
+    Recommender recommender = new GenericItemBasedRecommender(dataModel, correlation);
+    List<RecommendedItem> originalRecommended = recommender.recommend("test1", 2);
+    List<RecommendedItem> rescoredRecommended =
+            recommender.recommend("test1", 2, new ReversingRescorer<Item>());
+    assertNotNull(originalRecommended);
+    assertNotNull(rescoredRecommended);
+    assertEquals(2, originalRecommended.size());
+    assertEquals(2, rescoredRecommended.size());
+    assertEquals(originalRecommended.get(0).getItem(), rescoredRecommended.get(1).getItem());
+    assertEquals(originalRecommended.get(1).getItem(), rescoredRecommended.get(0).getItem());
+  }
+
+  public void testEstimatePref() throws Exception {
+    Recommender recommender = buildRecommender();
+    assertEquals(0.1, recommender.estimatePreference("test1", "2"), EPSILON);
+  }
+
+  /**
+   * Contributed test case that verifies fix for bug
+   * <a href="http://sourceforge.net/tracker/index.php?func=detail&amp;aid=1396128&amp;group_id=138771&amp;atid=741665">
+   * 1396128</a>.
+   */
+  public void testBestRating() throws Exception {
+    Recommender recommender = buildRecommender();
+    List<RecommendedItem> recommended = recommender.recommend("test1", 1);
+    assertNotNull(recommended);
+    assertEquals(1, recommended.size());
+    RecommendedItem firstRecommended = recommended.get(0);
+    // item one should be recommended because it has a greater rating/score
+    assertEquals(new GenericItem<String>("2"), firstRecommended.getItem());
+    assertEquals(0.1, firstRecommended.getValue(), EPSILON);
+  }
+
+  public void testMostSimilar() throws Exception {
+    ItemBasedRecommender recommender = buildRecommender();
+    List<RecommendedItem> similar = recommender.mostSimilarItems("0", 2);
+    assertNotNull(similar);
+    assertEquals(2, similar.size());
+    RecommendedItem first = similar.get(0);
+    RecommendedItem second = similar.get(1);
+    assertEquals("1", first.getItem().getID());
+    assertEquals(1.0, first.getValue(), EPSILON);
+    assertEquals("2", second.getItem().getID());
+    assertEquals(0.5, second.getValue(), EPSILON);
+  }
+
+  public void testMostSimilarToMultiple() throws Exception {
+    ItemBasedRecommender recommender = buildRecommender2();
+    List<Object> itemIDs = new ArrayList<Object>(2);
+    itemIDs.add("0");
+    itemIDs.add("1");
+    List<RecommendedItem> similar = recommender.mostSimilarItems(itemIDs, 2);
+    assertNotNull(similar);
+    assertEquals(2, similar.size());
+    RecommendedItem first = similar.get(0);
+    RecommendedItem second = similar.get(1);
+    assertEquals("2", first.getItem().getID());
+    assertEquals(0.85, first.getValue(), EPSILON);
+    assertEquals("3", second.getItem().getID());
+    assertEquals(-0.3, second.getValue(), EPSILON);
+  }
+
+  public void testRecommendedBecause() throws Exception {
+    ItemBasedRecommender recommender = buildRecommender2();
+    List<RecommendedItem> recommendedBecause = recommender.recommendedBecause("test1", "4", 3);
+    assertNotNull(recommendedBecause);
+    assertEquals(3, recommendedBecause.size());
+    RecommendedItem first = recommendedBecause.get(0);
+    RecommendedItem second = recommendedBecause.get(1);
+    RecommendedItem third = recommendedBecause.get(2);
+    assertEquals("2", first.getItem().getID());
+    assertEquals(0.99, first.getValue(), EPSILON);
+    assertEquals("3", second.getItem().getID());
+    assertEquals(0.4, second.getValue(), EPSILON);
+    assertEquals("0", third.getItem().getID());
+    assertEquals(0.2, third.getValue(), EPSILON);
+  }
+
+  private static ItemBasedRecommender buildRecommender() {
+    DataModel dataModel = new GenericDataModel(getMockUsers());
+    Collection<GenericItemCorrelation.ItemItemCorrelation> correlations =
+            new ArrayList<GenericItemCorrelation.ItemItemCorrelation>(2);
+    Item item1 = new GenericItem<String>("0");
+    Item item2 = new GenericItem<String>("1");
+    Item item3 = new GenericItem<String>("2");
+    correlations.add(new GenericItemCorrelation.ItemItemCorrelation(item1, item2, 1.0));
+    correlations.add(new GenericItemCorrelation.ItemItemCorrelation(item1, item3, 0.5));
+    ItemCorrelation correlation = new GenericItemCorrelation(correlations);
+    return new GenericItemBasedRecommender(dataModel, correlation);
+  }
+
+  private static ItemBasedRecommender buildRecommender2() {
+    List<User> users = new ArrayList<User>(4);
+    users.add(getUser("test1", 0.1, 0.3, 0.9, 0.8));
+    users.add(getUser("test2", 0.2, 0.3, 0.3, 0.4));
+    users.add(getUser("test3", 0.4, 0.3, 0.5, 0.1, 0.1));
+    users.add(getUser("test4", 0.7, 0.3, 0.8, 0.5, 0.6));
+    DataModel dataModel = new GenericDataModel(users);
+    Collection<GenericItemCorrelation.ItemItemCorrelation> correlations =
+            new ArrayList<GenericItemCorrelation.ItemItemCorrelation>(10);
+    Item item1 = new GenericItem<String>("0");
+    Item item2 = new GenericItem<String>("1");
+    Item item3 = new GenericItem<String>("2");
+    Item item4 = new GenericItem<String>("3");
+    Item item5 = new GenericItem<String>("4");
+    correlations.add(new GenericItemCorrelation.ItemItemCorrelation(item1, item2, 1.0));
+    correlations.add(new GenericItemCorrelation.ItemItemCorrelation(item1, item3, 0.8));
+    correlations.add(new GenericItemCorrelation.ItemItemCorrelation(item1, item4, -0.6));
+    correlations.add(new GenericItemCorrelation.ItemItemCorrelation(item1, item5, 1.0));
+    correlations.add(new GenericItemCorrelation.ItemItemCorrelation(item2, item3, 0.9));
+    correlations.add(new GenericItemCorrelation.ItemItemCorrelation(item2, item4, 0.0));
+    correlations.add(new GenericItemCorrelation.ItemItemCorrelation(item2, item2, 1.0));
+    correlations.add(new GenericItemCorrelation.ItemItemCorrelation(item3, item4, -0.1));
+    correlations.add(new GenericItemCorrelation.ItemItemCorrelation(item3, item5, 0.1));
+    correlations.add(new GenericItemCorrelation.ItemItemCorrelation(item4, item5, -0.5));
+    ItemCorrelation correlation = new GenericItemCorrelation(correlations);
+    return new GenericItemBasedRecommender(dataModel, correlation);
+  }
+
+}

Added: lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/recommender/GenericUserBasedRecommenderTest.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/recommender/GenericUserBasedRecommenderTest.java?rev=654943&view=auto
==============================================================================
--- lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/recommender/GenericUserBasedRecommenderTest.java (added)
+++ lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/recommender/GenericUserBasedRecommenderTest.java Fri May  9 14:35:12 2008
@@ -0,0 +1,140 @@
+/**
+ * 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.recommender;
+
+import org.apache.mahout.cf.taste.correlation.UserCorrelation;
+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.neighborhood.NearestNUserNeighborhood;
+import org.apache.mahout.cf.taste.impl.TasteTestCase;
+import org.apache.mahout.cf.taste.model.DataModel;
+import org.apache.mahout.cf.taste.model.Item;
+import org.apache.mahout.cf.taste.model.User;
+import org.apache.mahout.cf.taste.neighborhood.UserNeighborhood;
+import org.apache.mahout.cf.taste.recommender.RecommendedItem;
+import org.apache.mahout.cf.taste.recommender.Recommender;
+import org.apache.mahout.cf.taste.recommender.UserBasedRecommender;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * <p>Tests {@link GenericUserBasedRecommender}.</p>
+ */
+public final class GenericUserBasedRecommenderTest extends TasteTestCase {
+
+  public void testRecommender() throws Exception {
+    Recommender recommender = buildRecommender();
+    List<RecommendedItem> recommended = recommender.recommend("test1", 1);
+    assertNotNull(recommended);
+    assertEquals(1, recommended.size());
+    RecommendedItem firstRecommended = recommended.get(0);
+    assertEquals(new GenericItem<String>("2"), firstRecommended.getItem());
+    assertEquals(0.3, firstRecommended.getValue());
+  }
+
+  public void testHowMany() throws Exception {
+    List<User> users = new ArrayList<User>(3);
+    users.add(getUser("test1", 0.1, 0.2));
+    users.add(getUser("test2", 0.2, 0.3, 0.3, 0.6));
+    users.add(getUser("test3", 0.4, 0.4, 0.5, 0.9));
+    users.add(getUser("test4", 0.1, 0.4, 0.5, 0.8, 0.9, 1.0));
+    users.add(getUser("test5", 0.2, 0.3, 0.6, 0.7, 0.1, 0.2));
+    DataModel dataModel = new GenericDataModel(users);
+    UserCorrelation correlation = new PearsonCorrelation(dataModel);
+    UserNeighborhood neighborhood = new NearestNUserNeighborhood(2, correlation, dataModel);
+    Recommender recommender = new GenericUserBasedRecommender(dataModel, neighborhood, correlation);
+    List<RecommendedItem> fewRecommended = recommender.recommend("test1", 2);
+    List<RecommendedItem> moreRecommended = recommender.recommend("test1", 4);
+    for (int i = 0; i < fewRecommended.size(); i++) {
+      assertEquals(fewRecommended.get(i).getItem(), moreRecommended.get(i).getItem());
+    }
+  }
+
+  public void testRescorer() throws Exception {
+    List<User> users = new ArrayList<User>(3);
+    users.add(getUser("test1", 0.1, 0.2));
+    users.add(getUser("test2", 0.2, 0.3, 0.3, 0.6));
+    users.add(getUser("test3", 0.4, 0.4, 0.5, 0.9));
+    DataModel dataModel = new GenericDataModel(users);
+    UserCorrelation correlation = new PearsonCorrelation(dataModel);
+    UserNeighborhood neighborhood = new NearestNUserNeighborhood(1, correlation, dataModel);
+    Recommender recommender = new GenericUserBasedRecommender(dataModel, neighborhood, correlation);
+    List<RecommendedItem> originalRecommended = recommender.recommend("test1", 2);
+    List<RecommendedItem> rescoredRecommended =
+            recommender.recommend("test1", 2, new ReversingRescorer<Item>());
+    assertNotNull(originalRecommended);
+    assertNotNull(rescoredRecommended);
+    assertEquals(2, originalRecommended.size());
+    assertEquals(2, rescoredRecommended.size());
+    assertEquals(originalRecommended.get(0).getItem(), rescoredRecommended.get(1).getItem());
+    assertEquals(originalRecommended.get(1).getItem(), rescoredRecommended.get(0).getItem());
+  }
+
+  public void testEstimatePref() throws Exception {
+    Recommender recommender = buildRecommender();
+    assertEquals(0.3, recommender.estimatePreference("test1", "2"));
+  }
+
+  public void testBestRating() throws Exception {
+    Recommender recommender = buildRecommender();
+    List<RecommendedItem> recommended = recommender.recommend("test1", 1);
+    assertNotNull(recommended);
+    assertEquals(1, recommended.size());
+    RecommendedItem firstRecommended = recommended.get(0);
+    // item one should be recommended because it has a greater rating/score
+    assertEquals(new GenericItem<String>("2"), firstRecommended.getItem());
+    assertEquals(0.3, firstRecommended.getValue(), EPSILON);
+  }
+
+  public void testMostSimilar() throws Exception {
+    UserBasedRecommender recommender = buildRecommender();
+    List<User> similar = recommender.mostSimilarUsers("test1", 2);
+    assertNotNull(similar);
+    assertEquals(2, similar.size());
+    User first = similar.get(0);
+    User second = similar.get(1);
+    assertEquals("test2", first.getID());
+    assertEquals("test3", second.getID());
+  }
+
+  public void testIsolatedUser() throws Exception {
+    List<User> users = new ArrayList<User>(3);
+    users.add(getUser("test1", 0.1, 0.2));
+    users.add(getUser("test2", 0.2, 0.3, 0.3, 0.6));
+    users.add(getUser("test3", 0.4, 0.4, 0.5, 0.9));
+    users.add(getUser("test4"));
+    DataModel dataModel = new GenericDataModel(users);
+    UserCorrelation correlation = new PearsonCorrelation(dataModel);
+    UserNeighborhood neighborhood = new NearestNUserNeighborhood(3, correlation, dataModel);
+    UserBasedRecommender recommender = new GenericUserBasedRecommender(dataModel, neighborhood, correlation);
+    Collection<User> mostSimilar = recommender.mostSimilarUsers("test4", 3);
+    assertNotNull(mostSimilar);
+    assertEquals(0, mostSimilar.size());
+  }
+
+  private static UserBasedRecommender buildRecommender() throws Exception {
+    DataModel dataModel = new GenericDataModel(getMockUsers());
+    UserCorrelation correlation = new PearsonCorrelation(dataModel);
+    UserNeighborhood neighborhood = new NearestNUserNeighborhood(1, correlation, dataModel);
+    return new GenericUserBasedRecommender(dataModel, neighborhood, correlation);
+  }
+
+}

Added: lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/recommender/MockRecommender.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/recommender/MockRecommender.java?rev=654943&view=auto
==============================================================================
--- lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/recommender/MockRecommender.java (added)
+++ lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/recommender/MockRecommender.java Fri May  9 14:35:12 2008
@@ -0,0 +1,85 @@
+/**
+ * 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.recommender;
+
+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.GenericUser;
+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.recommender.RecommendedItem;
+import org.apache.mahout.cf.taste.recommender.Recommender;
+import org.apache.mahout.cf.taste.recommender.Rescorer;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+
+final class MockRecommender implements Recommender {
+
+  private final AtomicInteger recommendCount;
+
+  MockRecommender(AtomicInteger recommendCount) {
+    this.recommendCount = recommendCount;
+  }
+
+  public List<RecommendedItem> recommend(Object userID, int howMany) {
+    recommendCount.incrementAndGet();
+    return Collections.<RecommendedItem>singletonList(
+            new GenericRecommendedItem(new GenericItem<String>("1"), 1.0));
+  }
+
+  public List<RecommendedItem> recommend(Object userID,
+                                         int howMany,
+                                         Rescorer<Item> rescorer) {
+    return recommend(userID, howMany);
+  }
+
+  public double estimatePreference(Object userID, Object itemID) {
+    recommendCount.incrementAndGet();
+    return 0.0;
+  }
+
+  public void setPreference(Object userID, Object itemID, double value) {
+    // do nothing
+  }
+
+  public void removePreference(Object userID, Object itemID) {
+    // do nothing
+  }
+
+  public DataModel getDataModel() {
+    User user1 = new GenericUser<String>("1", Collections.<Preference>emptyList());
+    User user2 = new GenericUser<String>("2", Collections.<Preference>emptyList());
+    User user3 = new GenericUser<String>("3", Collections.<Preference>emptyList());
+    List<User> users = new ArrayList<User>(3);
+    users.add(user1);
+    users.add(user2);
+    users.add(user3);
+    return new GenericDataModel(users);
+  }
+
+  public void refresh() {
+    // do nothing
+  }
+
+}

Added: lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/recommender/NullRescorerTest.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/recommender/NullRescorerTest.java?rev=654943&view=auto
==============================================================================
--- lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/recommender/NullRescorerTest.java (added)
+++ lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/recommender/NullRescorerTest.java Fri May  9 14:35:12 2008
@@ -0,0 +1,55 @@
+/**
+ * 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.recommender;
+
+import junit.framework.TestCase;
+import org.apache.mahout.cf.taste.impl.model.GenericItem;
+import org.apache.mahout.cf.taste.impl.model.GenericUser;
+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.recommender.Rescorer;
+
+import java.util.Collections;
+
+/**
+ * <p>Tests {@link NullRescorer}.</p>
+ */
+public final class NullRescorerTest extends TestCase {
+
+  public void testItemRescorer() throws Exception {
+    Rescorer<Item> rescorer = NullRescorer.getItemInstance();
+    assertNotNull(rescorer);
+    Item item = new GenericItem<String>("test");
+    assertEquals(1.0, rescorer.rescore(item, 1.0));
+    assertEquals(1.0, rescorer.rescore(null, 1.0));
+    assertEquals(0.0, rescorer.rescore(item, 0.0));
+    assertTrue(Double.isNaN(rescorer.rescore(item, Double.NaN)));
+  }
+
+  public void testUserRescorer() throws Exception {
+    Rescorer<User> rescorer = NullRescorer.getUserInstance();
+    assertNotNull(rescorer);
+    User user = new GenericUser<String>("test", Collections.<Preference>emptyList());
+    assertEquals(1.0, rescorer.rescore(user, 1.0));
+    assertEquals(1.0, rescorer.rescore(null, 1.0));
+    assertEquals(0.0, rescorer.rescore(user, 0.0));
+    assertTrue(Double.isNaN(rescorer.rescore(user, Double.NaN)));
+  }
+
+}

Added: lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/recommender/ReversingRescorer.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/recommender/ReversingRescorer.java?rev=654943&view=auto
==============================================================================
--- lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/recommender/ReversingRescorer.java (added)
+++ lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/recommender/ReversingRescorer.java Fri May  9 14:35:12 2008
@@ -0,0 +1,36 @@
+/**
+ * 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.recommender;
+
+import org.apache.mahout.cf.taste.recommender.Rescorer;
+
+/**
+ * <p>Simple {@link Rescorer} which negates the given score, thus reversing
+ * order of rankings.</p>
+ */
+public final class ReversingRescorer<T> implements Rescorer<T> {
+
+  public double rescore(T thing, double originalScore) {
+    return Double.isNaN(originalScore) ? Double.NaN : -originalScore;
+  }
+
+  public boolean isFiltered(T thing) {
+    return false;
+  }
+
+}