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;
}

Reply via email to