public double apply(long user, long item, double value) { return domain.clampValue(value); } }
/** * Create a new preference domain quantizer. * * @see PreferenceDomain#PreferenceDomain(double, double, double) */ @SuppressWarnings("unused") public PreferenceDomainQuantizer(double min, double max, double prec) { this(new PreferenceDomain(min, max, prec)); }
@Nonnull @Override public Map<Long, Double> predict(long user, @Nonnull Collection<Long> items) { Map<Long, Double> scores = scorer.score(user, items); if (preferenceDomain == null) { return scores; } else { return preferenceDomain.clampVector(scores); } }
@Override public boolean equals(Object o) { if (o == this) { return true; } else if (o instanceof PreferenceDomain) { PreferenceDomain od = (PreferenceDomain) o; EqualsBuilder eqb = new EqualsBuilder(); eqb.append(minimum, od.getMinimum()); eqb.append(maximum, od.getMaximum()); eqb.append(hasPrecision(), od.hasPrecision()); if (eqb.isEquals() && hasPrecision() && od.hasPrecision()) { eqb.append(precision, od.getPrecision()); } return eqb.isEquals(); } else { return false; } }
@Test public void testParamBuilder() { PreferenceDomainBuilder bld = new PreferenceDomainBuilder(1, 5); PreferenceDomain dom = bld.build(); assertThat(dom.getMinimum(), equalTo(1.0)); assertThat(dom.getMaximum(), equalTo(5.0)); assertThat(dom.getPrecision(), equalTo(0.0)); }
@Test public void testClampValue() { PreferenceDomain d = PreferenceDomain.fromString("[1.0,5.0]"); for (Map<Long,Double> vec: someMaps(longs(), doubles(0.0, 8.0))) { Long2DoubleMap clamped = d.clampVector(vec); assertThat(clamped.keySet(), equalTo(vec.keySet())); for (Long k: vec.keySet()) { double v = vec.get(k); if (v < 1.0) { assertThat(clamped, hasEntry(k, 1.0)); } else if (v > 5.0) { assertThat(clamped, hasEntry(k, 5.0)); } else { assertThat(clamped, hasEntry(k, v)); } } } } }
@Override public int hashCode() { HashCodeBuilder hcb = new HashCodeBuilder(); hcb.append(minimum).append(maximum); if (hasPrecision()) { hcb.append(precision); } return hcb.toHashCode(); }
@Test(expected = IllegalArgumentException.class) public void testParseInverted() { PreferenceDomain.fromString("[2.5, -1]"); }
static RealVector makeValues(PreferenceDomain domain) { if (!domain.hasPrecision()) { throw new IllegalArgumentException("domain is not discrete"); } final double min = domain.getMinimum(); final double max = domain.getMaximum(); final double prec = domain.getPrecision(); final double nv = (max - min) / prec; int n = (int) nv; if (Math.abs(nv - n) > 1.0e-6) { n += 1; // one more to cover everything... } if (n == 0) { throw new IllegalArgumentException("range has no elements"); } double[] values = new double[n + 1]; for (int i = 0; i <= n; i++) { values[i] = min + (prec * i); } return new ArrayRealVector(values); }
@Test public void testSetMinMax() { PreferenceDomainBuilder bld = new PreferenceDomainBuilder(); bld.setMinimum(-1) .setMaximum(1); PreferenceDomain dom = bld.build(); assertThat(dom.getMinimum(), equalTo(-1.0)); assertThat(dom.getMaximum(), equalTo(1.0)); assertThat(dom.getPrecision(), equalTo(0.0)); }
@Override public int hashCode() { HashCodeBuilder hcb = new HashCodeBuilder(); hcb.append(minimum).append(maximum); if (hasPrecision()) { hcb.append(precision); } return hcb.toHashCode(); }
private LenskitConfiguration makeDataConfig(Context ctx) { LenskitConfiguration config = new LenskitConfiguration(); config.bind(DataAccessObject.class).toProvider(new DAOProvider()); String dspec = ctx.options.getString("domain"); if (dspec != null) { PreferenceDomain domain = PreferenceDomain.fromString(dspec); config.bind(PreferenceDomain.class).to(domain); } return config; }
@Test public void testParseDiscrete() { PreferenceDomain d = PreferenceDomain.fromString("[1.0,5.0]/0.5"); assertThat(d.getMinimum(), closeTo(1.0, 1.0e-6)); assertThat(d.getMaximum(), closeTo(5.0, 1.0e-6)); assertTrue(d.hasPrecision()); assertThat(d.getPrecision(), equalTo(0.5)); }
/** * Prepare the updater for updating the feature values for a particular user/item ID. * * @param feature The feature we are training. * @param rating The rating value. * @param estimate The estimate through the previous feature. * @param uv The user feature value. * @param iv The item feature value. * @param trail The sum of the trailing feature value products. */ public void prepare(int feature, double rating, double estimate, double uv, double iv, double trail) { // Compute prediction double pred = estimate + uv * iv; PreferenceDomain dom = updateRule.getDomain(); if (dom != null) { pred = dom.clampValue(pred); } pred += trail; // Compute the err and store this value error = rating - pred; userFeatureValue = uv; itemFeatureValue = iv; // Update statistics n += 1; sse += error * error; }
@Test public void testSetAll() { PreferenceDomainBuilder bld = new PreferenceDomainBuilder(); bld.setMinimum(1.0) .setMaximum(5) .setPrecision(0.5); PreferenceDomain dom = bld.build(); assertThat(dom.getMinimum(), equalTo(1.0)); assertThat(dom.getMaximum(), equalTo(5.0)); assertThat(dom.getPrecision(), equalTo(0.5)); }
@Override public PreferenceDomain build() { Preconditions.checkState(!Double.isNaN(minimum), "no minimum preference specified"); Preconditions.checkState(!Double.isNaN(maximum), "no maximum preference specified"); return new PreferenceDomain(minimum, maximum, precision); } }
@Nonnull @Override public Map<Long, Double> predict(long user, @Nonnull Collection<Long> items) { Map<Long, Double> scores = scorer.score(user, items); if (preferenceDomain == null) { return scores; } else { return preferenceDomain.clampVector(scores); } }
@Test public void testParseInt() { PreferenceDomain d = PreferenceDomain.fromString("[ 1 , 5 ] / 1"); assertThat(d.getMinimum(), closeTo(1.0, 1.0e-6)); assertThat(d.getMaximum(), closeTo(5.0, 1.0e-6)); assertTrue(d.hasPrecision()); assertThat(d.getPrecision(), equalTo(1.0)); }
@Nonnull @Override public ResultMap predictWithDetails(long user, @Nonnull Collection<Long> items) { ResultMap scores = scorer.scoreWithDetails(user, items); List<Result> rescored = new ArrayList<>(scores.size()); for (Result r: scores) { double val = r.getScore(); if (preferenceDomain != null) { val = preferenceDomain.clampValue(val); } rescored.add(Results.rescore(r, val)); } return Results.newResultMap(rescored); } }
/** * Parse a preference domain from a string specification. * <p> * Continuous preference domains are specified as {@code [min, max]}; discrete domains * as {@code min:max[/prec/}. For example, a 0.5-5.0 half-star rating scale is represented * as {@code [0.5, 5.0]/0.5}. * * @param spec The string specifying the preference domain. * @return The preference domain represented by {@code spec}. * @throws IllegalArgumentException if {@code spec} is not a valid domain specification. */ @Nonnull public static PreferenceDomain fromString(@Nonnull String spec) { Matcher m = specRE.matcher(spec); if (!m.matches()) { throw new IllegalArgumentException("invalid domain specification"); } double min = Double.parseDouble(m.group(1)); double max = Double.parseDouble(m.group(2)); String precs = m.group(3); if (precs != null) { double prec = Double.parseDouble(precs); return new PreferenceDomain(min, max, prec); } else { return new PreferenceDomain(min, max); } }