Joseph Wakeling wrote: > No ... ! That was true in the original code I posted, but following > bearophile's kind example that part of the code was updated to a form > along the lines of,
Just for reference, here are the two pieces of code, for side-by-side comparison. As far as I can tell the algorithms are identical; the results are certainly identical; and the D code is significantly slower (on the order of half the speed on my machine). I don't know what the cause is, but my guess would be something along the lines of iterating across or writing values to arrays in D. The C++ code relies on the Boost libraries, another reason to like D2: you get a lot of nice functionality built-in ... :-) Best wishes, -- Joe
import std.stdio: printf; import std.math: sqrt, pow; import std.random; struct Rating { size_t user; size_t object; double value; } class ReputationAlgorithm { this() {} this(ref Rating[] ratings, ref double[] reputationUser, ref double[] reputationObject) {} } class AvgWeighted : ReputationAlgorithm { double[] weightSum; uint opCall(ref Rating[] ratings, ref double[] reputationUser, ref double[] reputationObject) { weightSum.length = reputationObject.length; weightSum[] = 0; reputationObject[] = 0; foreach(ref const(Rating) r; ratings) { reputationObject[r.object] += reputationUser[r.user]*r.value; weightSum[r.object] += reputationUser[r.user]; } foreach(size_t o, ref double r; reputationObject) r /= weightSum[o]; return 0; } this() {} this(ref Rating[] ratings, ref double[] reputationUser, ref double[] reputationObject) { opCall(ratings,reputationUser,reputationObject); } } final class AvgArithmetic : AvgWeighted { uint opCall(ref Rating[] ratings, ref double[] reputationUser, ref double[] reputationObject) { reputationUser[] = 1; return super.opCall(ratings,reputationUser,reputationObject); } this() {} this(ref Rating[] ratings, ref double[] reputationUser, ref double[] reputationObject) { opCall(ratings,reputationUser,reputationObject); } } final class Yzlm : AvgWeighted { double beta; double convergenceRequirement; double errorMin; size_t[] userLinks; double[] weightSum; double[] oldReputationObject; double objectReputationUpdate(ref Rating[] ratings, ref double[] reputationUser, ref double[] reputationObject) { double diff = 0; double[] tmp = oldReputationObject; oldReputationObject = reputationObject; reputationObject = tmp; reputationObject[] = 0; super.opCall(ratings,reputationUser,reputationObject); foreach(size_t object, ref const(double) r; reputationObject) { auto aux = r - oldReputationObject[object]; diff += aux*aux; } return sqrt(diff); } void userReputationUpdate(ref Rating[] ratings, ref double[] reputationUser, ref double[] reputationObject) { reputationUser[] = 0; foreach(ref const(Rating) r; ratings) { auto aux = r.value - reputationObject[r.object]; reputationUser[r.user] += aux*aux; } foreach(size_t user, ref double r; reputationUser) { if(userLinks[user]>0) r = pow( (r/userLinks[user]) + errorMin, -beta); } } uint opCall(ref Rating[] ratings, ref double[] reputationUser, ref double[] reputationObject) { userLinks.length = reputationUser.length; userLinks[] = 0; weightSum.length = reputationObject.length; oldReputationObject.length = reputationObject.length; foreach(ref const(Rating) r; ratings) userLinks[r.user]++; double diff; uint iterations=0; do { userReputationUpdate(ratings, reputationUser, reputationObject); diff = objectReputationUpdate(ratings, reputationUser, reputationObject); ++iterations; } while(diff > convergenceRequirement); printf("Exited in %u iterations with diff = %g < %g\n", iterations,diff,convergenceRequirement); return iterations; } this() {} this(double b, double c, double e) { beta = b; convergenceRequirement = c; errorMin = e; assert(beta>=0); assert(convergenceRequirement>0); assert(errorMin>=0); } this(ref Rating[] ratings, ref double[] reputationUser, ref double[] reputationObject, double b, double c, double e) { this(b,c,e); opCall(ratings, reputationUser, reputationObject); } } void main() { Rating[] ratings; double[] reputationObject, reputationUser; double[] objectQuality, userError; uint iterTotal = 0; Mt19937 rng; auto aa = new AvgArithmetic; auto yzlm = new Yzlm(0.8,1e-12,1e-36); rng.seed(1001); reputationObject.length = 1000; reputationUser.length = 1000; objectQuality.length = reputationObject.length; userError.length = reputationUser.length; ratings.length = reputationObject.length*reputationUser.length; for(uint i=0;i<100;++i) { foreach(ref double Q; objectQuality) Q = uniform(0.0,10.0,rng); foreach(ref double sigma2; userError) sigma2 = uniform(0.0,1.0,rng); size_t pos = 0; foreach(size_t object, ref double Q; objectQuality) { foreach(size_t user, ref double sigma2; userError) { ratings[pos++] = Rating(user, object, uniform((Q-sigma2), (Q+sigma2), rng)); } } ratings.length = pos; printf("We now have %u ratings.\n",ratings.length); aa(ratings,reputationUser,reputationObject); iterTotal += yzlm(ratings,reputationUser,reputationObject); double deltaQ = 0; foreach(size_t object, ref const(double) r; reputationObject) deltaQ += pow( (r-objectQuality[object]), 2.0); deltaQ = sqrt(deltaQ/reputationObject.length); printf("[%u] Error in quality estimate: %g\n",i,deltaQ); } printf("Total iterations: %u\n",iterTotal); }
#include <iostream> #include <vector> #include <boost/foreach.hpp> #include <boost/random.hpp> using namespace std; using namespace boost; namespace fric { class Rating { private: size_t _user; size_t _object; double _value; public: Rating() {} Rating(size_t u, size_t o, double v) { _user = u; _object = o; _value = v; } const size_t &user() { return _user; } const size_t &object() { return _object; } const double &value() { return _value; } }; class ReputationAlgorithm { virtual size_t reputation(vector<double> &reputationUser, vector<double> &reputationObject, vector<Rating> &ratings) { return 0; } virtual size_t addRating(vector<double> &reputationUser, vector<double> &reputationObject, vector<Rating> &ratings, Rating r) { return 0; } }; namespace algorithm { class AvgWeighted : ReputationAlgorithm { protected: vector<double> weightSum; public: virtual size_t reputation(vector<double> &reputationUser, vector<double> &reputationObject, vector<Rating> &ratings) { weightSum.assign(reputationObject.size(),0.0); reputationObject.assign(reputationObject.size(),0.0); BOOST_FOREACH(Rating &r, ratings) { reputationObject.at(r.object()) += reputationUser.at(r.user()) * r.value(); weightSum.at(r.object()) += reputationUser.at(r.user()); } for(size_t o=0;o!=reputationObject.size();++o) reputationObject.at(o) /= (weightSum.at(o)>0) ? weightSum.at(o) : 1; return 0; } }; class AvgArithmetic : AvgWeighted { public: virtual size_t reputation(vector<double> &reputationUser, vector<double> &reputationObject, vector<Rating> &ratings) { reputationUser.assign(reputationUser.size(),1.0); return AvgWeighted::reputation(reputationUser,reputationObject,ratings); } }; class Yzlm : AvgWeighted { private: double _beta; double _convergence; double _errorMin; protected: vector<double> oldReputationObject; vector<size_t> userLinks; double objectReputationUpdate(vector<double> &reputationUser, vector<double> &reputationObject, vector<Rating> &ratings) { double diff = 0.0; oldReputationObject.assign(reputationObject.begin(), reputationObject.end()); AvgWeighted::reputation(reputationUser, reputationObject, ratings); for(size_t o=0;o!=reputationObject.size();++o) { double aux = reputationObject.at(o) - oldReputationObject.at(o); diff += aux*aux; } return sqrt(diff); } void userReputationUpdate(vector<double> &reputationUser, vector<double> &reputationObject, vector<Rating> &ratings) { reputationUser.assign(reputationUser.size(),0.0); BOOST_FOREACH(Rating &r, ratings) { double aux = r.value() - reputationObject.at(r.object()); reputationUser.at(r.user()) += aux*aux; } for(size_t u=0;u!=reputationUser.size();++u) { if(userLinks.at(u)!=0) reputationUser.at(u) = pow( (reputationUser.at(u)/userLinks.at(u)) + errorMin(), -beta() ); } } public: void init(double b, double c, double e) { _beta = b; _convergence = c; _errorMin = e; } const double &beta() { return _beta; } const double &convergence() { return _convergence; } const double &errorMin() { return _errorMin; } virtual size_t reputation(vector<double> &reputationUser, vector<double> &reputationObject, vector<Rating> &ratings) { double diff; size_t iterations = 0; userLinks.assign(reputationUser.size(),0); oldReputationObject.resize(reputationObject.size()); BOOST_FOREACH(Rating &r, ratings) userLinks.at(r.user())++; do { userReputationUpdate(reputationUser,reputationObject,ratings); diff = objectReputationUpdate(reputationUser,reputationObject,ratings); ++iterations; } while(diff > convergence()); cout << "Exited in " << iterations << " iterations with diff = " << diff << endl; return iterations; } Yzlm(double b, double c, double e) { init(b,c,e); } }; } } using namespace fric; int main() { vector<double> reputationUser, reputationObject; vector<double> userError, objectQuality; vector<Rating> ratings; mt19937 rng; algorithm::AvgArithmetic aa; algorithm::Yzlm yzlm(0.8,1e-12,1e-36); reputationUser.resize(100); reputationObject.resize(1); rng.seed(1001); for(size_t u=0;u!=reputationUser.size();++u) ratings.push_back(Rating(u,0,u+1)); aa.reputation(reputationUser,reputationObject,ratings); BOOST_FOREACH(double r, reputationObject) cout << r << endl; objectQuality.resize(1000); userError.resize(1000); reputationObject.resize(objectQuality.size()); reputationUser.resize(userError.size()); ratings.reserve(objectQuality.size() * userError.size()); size_t iterTotal = 0; for(size_t i=0;i!=100;++i) { BOOST_FOREACH(double &Q, objectQuality) Q = uniform_real<double> (0.0, 10.0) (rng); BOOST_FOREACH(double &err, userError) err = uniform_real<double> (0.0, 1.0) (rng); ratings.resize(0); for(size_t o=0;o!=objectQuality.size();++o) { for(size_t u=0;u!=userError.size();++u) { ratings.push_back( Rating(u, o, uniform_real<double> (objectQuality.at(o)-userError.at(u), objectQuality.at(o)+userError.at(u)) (rng) ) ); } } cout << "We now have " << ratings.size() << " ratings, with capacity for " << ratings.capacity() << endl; aa.reputation(reputationUser, reputationObject, ratings); iterTotal += yzlm.reputation(reputationUser, reputationObject, ratings); double deltaQ = 0.0; for(size_t o=0;o!=objectQuality.size();++o) { double aux = reputationObject.at(o) - objectQuality.at(o); deltaQ += aux*aux; } deltaQ = sqrt(deltaQ/objectQuality.size()); cout << "[" << i << "] Error in quality estimate: " << deltaQ << endl; } cout << endl; cout << "Total iterations: " << iterTotal << endl; return 0; }