<commit_before>#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <comedilib.h>
#include <getopt.h>

int subdev = 0;
int range = 0;
int aref = AREF_GROUND;
int chan[50];
int blink_msec = 300;

static struct option longopts[] = {
	{ "help", no_argument, 0, 'h' },
	{ "read", required_argument, 0, 'r' },
	{ "write", required_argument, 0, 'w' },
	{ "aread", required_argument, 0, 'i' },
	{ "awrite", required_argument, 0, 'o' },
	{ "subdev", required_argument, 0, 's' },
	{ "device", required_argument, 0, 'd' },
	{ "verbose", no_argument, 0, 'v' },
	{ "aref", required_argument, 0, 'z' },
	{ "range", required_argument, 0, 'x' },
	{ "config", required_argument, 0, 'c' },
	{ "autoconf", no_argument, 0, 'a' },
	{ "plus", required_argument, 0, 'p' },
	{ "blink", no_argument, 0, 'b' },
	{ "blink-msec", required_argument, 0, 'm' },
	{ "subdev-config", required_argument, 0, 'q' },
	{ NULL, 0, 0, 0 }
};
// --------------------------------------------------------------------------
enum Command
{
	cmdNOP,
	cmdDRead,
	cmdDWrite,
	cmdARead,
	cmdAWrite,
	cmdConfig,
	cmdSubConfig

} cmd;
// --------------------------------------------------------------------------
static void insn_config( comedi_t* card, int subdev, int channel, lsampl_t iotype, int range, int aref );
static void insn_subdev_config( comedi_t* card, int subdev, lsampl_t type );
// --------------------------------------------------------------------------

int main(int argc, char* argv[])
{
	comedi_t* card;
	lsampl_t data = 0;
	int optindex = 0;
	int opt = 0;
	const char* dev = "/dev/comedi0";
	int val = -1;
	cmd = cmdNOP;
	int verb = 0;
	int autoconf = 0;
	int cnum = 0;
	int blink = 0;
	int exret = EXIT_SUCCESS;

	memset(chan,-1,sizeof(chan));

	while( (opt = getopt_long(argc, argv, "habr:w:i:o:s:d:c:p:m:q:",longopts,&optindex)) != -1 )
	{
		switch (opt)
		{
			case 'h':
				printf("-h|--help 		- this message\n");
				printf("[-w|--write] chan val - write to digital channel\n");
				printf("[-r|--read] chan 	- read from digital channel\n");
				printf("[-o|--awrite] chan val - write to analog channel\n");
				printf("[-i|--aread] chan 	- read from analog channel\n");
				printf("[-s|--subdev] sub	- use subdev number sub. (Default: 0)\n");
				printf("[-d|--device] dev	- use device dev. (Default: /dev/comedi0)\n");
				printf("[--aref] val		- AREF (Default: %d)\n",aref);
				printf("[--range] val		- RANGE (Default: %d)\n",range);
				printf("[-p|--plus] chan	- Add channel for command.\n");
				printf("[-a|--autoconf] 	- autoconf channel type\n");
				printf("[-q|--subdev-config] 	- subdev type configure\n");
				printf("    1 	- TBI 24/0\n");
				printf("    2 	- TBI 0/24\n");
				printf("    3 	- TBI 16/8\n");
				printf("aref:\n");
				printf("	0	- analog ref = analog ground\n");
				printf("	1	- analog ref = analog common\n");
				printf("	2	- analog ref = differential\n");
				printf("	3	- analog ref = other (undefined)\n");
				printf("range:\n");
				printf("	0	-  -10В - 10В\n");
				printf("	1	-  -5В - 5В\n");
				printf("	2	-  -2.5В - 2.5В\n");
				printf("	3	-  -1.25В - 1.25В\n");
				printf("[--blink]			- (blink output): ONLY FOR 'write': 0-1-0-1-0-...\n");
				printf("[--blink-msec] val	- Blink pause [msec]. Default: 300 msec\n");
			return 0;

			case 'r':
				chan[0] = atoi(optarg);
				cmd = cmdDRead;
			break;

			case 'w':
				chan[0] = atoi(optarg);
				cmd = cmdDWrite;
				if( optind<argc && (argv[optind])[0]!='-' )
						val = atoi(argv[optind]);
			break;

			case 'i':
				chan[0] = atoi(optarg);
				cmd = cmdARead;
			break;

			case 'o':
				chan[0] = atoi(optarg);
				cmd = cmdAWrite;
				if( optind<argc && (argv[optind])[0]!='-' )
						val = atoi(argv[optind]);
			break;

			case 'd':
				dev = optarg;
			break;

			case 's':
				subdev = atoi(optarg);
			break;

			case 'v':
				verb = 1;
			break;

			case 'x':
				range = atoi(optarg);
			break;

			case 'z':
				aref = atoi(optarg);
			break;

			case 'c':
				cmd = cmdConfig;
				val = atoi(optarg);
			break;

			case 'q':
				cmd = cmdSubConfig;
				val = atoi(optarg);
			break;

			case 'a':
				autoconf = 1;
			break;

			case 'p':
				chan[++cnum] = atoi(optarg);
			break;

			case 'm':
				blink_msec = atoi(optarg);
			break;

			case 'b':
				blink = 1;
			break;

			case '?':
			default:
				printf("? argumnet\n");
				return 0;
		}
	}

	card = comedi_open(dev);
	if( card == NULL )
	{
		comedi_perror("comedi_open error");
		exit(EXIT_FAILURE);
	}

	if( verb )
	{
		fprintf(stdout, "init comedi:\n");
		fprintf(stdout, "dev: %s\n",dev);
		fprintf(stdout, "subdev: %d\n",subdev);
		fprintf(stdout, "channel: %d\n",chan[0]);
	}

	switch( cmd )
	{
		case cmdDRead:
		{
			for( int k=0; chan[k]!=-1; k++ )
			{
				if( comedi_dio_config(card, subdev, chan[k],INSN_CONFIG_DIO_INPUT) < 0)
				{
					comedi_perror("can't configure DI channels");
					exret = EXIT_FAILURE;
				}

				if( comedi_dio_read(card, subdev, chan[k],&data) < 0)
				{
					fprintf(stderr, "can't read from channel %d\n",chan[k]);
			  		exret = EXIT_FAILURE;
				}

				printf("Digital input %d: %d\n",chan[k], data);
			}
		}
		break;

		case cmdDWrite:
		{
			for( int k=0; chan[k]!=-1; k++ )
			{
				if( comedi_dio_config(card, subdev, chan[k],INSN_CONFIG_DIO_OUTPUT) < 0 )
				{
					comedi_perror("can't configure DO channels");
					exret = EXIT_FAILURE;
				}
			}
			// реализация мигания
			while(1)
			{
				for( int k=0; chan[k]!=-1; k++ )
				{
					if( verb )
						printf( "write: ch=%d val=%d\n",chan[k],val);

				  	if( comedi_dio_write(card, subdev, chan[k], val) < 0)
					{
						comedi_perror("can't write 1 to channel\n");
				  		exret = EXIT_FAILURE;
					}
				}

				if( !blink )
					break;

				val = val ? 0 : 1;
				usleep(1000*blink_msec);
			}
		}
		break;

		case cmdARead:
		{
			for( int k=0; chan[k]!=-1; k++ )
			{
				if( autoconf )
					insn_config(card, subdev,chan[k],100,range,aref);

				int ret = comedi_data_read(card, subdev, chan[k], range, aref, &data);
				if( ret < 0)
				{
					fprintf(stderr, "can't read from channel %d: (%d) %s\n",chan[k],ret,strerror(ret));
				  	exret = EXIT_FAILURE;
				}

  				printf("Readed from channel %d value is %d\n", chan[k], data);
			}
		}
		break;

		case cmdAWrite:
		{
			for( int k=0; chan[k]!=-1; k++ )
			{
				if( autoconf )
					insn_config(card,subdev,chan[k],101,range,aref);

				int ret = comedi_data_write(card, subdev, chan[k], range, aref, val);
				if( ret < 0)
				{
					fprintf(stderr, "can't write to channel %d: (%d) %s\n",chan[k],ret,strerror(ret));
					exret = EXIT_FAILURE;
				}
			}
		}
		break;

		case cmdConfig:
		{
			for( int k=0; chan[k]!=-1; k++ )
			{

				if( val != INSN_CONFIG_DIO_INPUT
					&& val != INSN_CONFIG_DIO_OUTPUT
					&& val != 100 /* INSN_CONFIG_AIO_INPUT */
					&& val != 101 /* INSN_CONFIG_AIO_OUTPUT */
				)
				{
	       		    fprintf(stderr, "can't config channel %d for type = %d (val=[%d,%d,%d,%d])\n"
							,chan[k],val,INSN_CONFIG_DIO_INPUT,INSN_CONFIG_DIO_OUTPUT,100,101);
					exret = EXIT_FAILURE;
				}
			}
		}

		case cmdSubConfig:
			insn_subdev_config(card, subdev, val);
		break;

		default:
			comedi_perror("\n...No command... Use -h for help\n");
			exit(EXIT_FAILURE);
		break;
	}

  exit(exret);
}

void insn_config( comedi_t* card, int subdev, int channel, lsampl_t iotype, int range, int aref )
{
	comedi_insn insn;
	memset(&insn,0,sizeof(insn));
	insn.insn = INSN_CONFIG;
	insn.n = 1;
	insn.data = &iotype;
	insn.subdev = subdev;
	insn.chanspec = CR_PACK(channel,range,aref);
	if( comedi_do_insn(card,&insn) < 0 )
	{
		fprintf(stderr, "can`t configure (AIO) subdev=%d channel=%d type=%d",subdev,channel,iotype);
	  	exit(EXIT_FAILURE);
	}
}

void insn_subdev_config( comedi_t* card, int subdev, lsampl_t type )
{
	lsampl_t cmd = 102;
	comedi_insn insn;
	memset(&insn,0,sizeof(insn));
	insn.insn = INSN_CONFIG;
	insn.n = 1;
	insn.data = &cmd;
	insn.unused[0] = type;
	insn.subdev = subdev;
	insn.chanspec = 0;

	switch(type)
	{
		case 1:
			printf("set subdev %d type: 'TBI 24/0'\n",subdev);
		break;

		case 2:
			printf("set subdev %d type: 'TBI 0/24'\n",subdev);
		break;

		case 3:
			printf("set subdev %d type: 'TBI 16/8'\n",subdev);
		break;

		default:
			printf("set subdev %d type: UNKNOWN\n",subdev);
		break;
	}

	if( comedi_do_insn(card,&insn) < 0 )
	{
		fprintf(stderr, "can`t configure subdev subdev=%d type=%d",subdev,type);
	  	exit(EXIT_FAILURE);
	}
}
<commit_msg>(iotest): 1. Убрал автоконфигурирование при чтении  DO,DI 2. Подправил вывод ошибок<commit_after>#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <error.h>
#include <errno.h>
#include <comedilib.h>
#include <getopt.h>

int subdev = 0;
int range = 0;
int aref = AREF_GROUND;
int chan[50];
int blink_msec = 300;

static struct option longopts[] = {
	{ "help", no_argument, 0, 'h' },
	{ "read", required_argument, 0, 'r' },
	{ "write", required_argument, 0, 'w' },
	{ "aread", required_argument, 0, 'i' },
	{ "awrite", required_argument, 0, 'o' },
	{ "subdev", required_argument, 0, 's' },
	{ "device", required_argument, 0, 'd' },
	{ "verbose", no_argument, 0, 'v' },
	{ "aref", required_argument, 0, 'z' },
	{ "range", required_argument, 0, 'x' },
	{ "config", required_argument, 0, 'c' },
	{ "autoconf", no_argument, 0, 'a' },
	{ "plus", required_argument, 0, 'p' },
	{ "blink", no_argument, 0, 'b' },
	{ "blink-msec", required_argument, 0, 'm' },
	{ "subdev-config", required_argument, 0, 'q' },
	{ NULL, 0, 0, 0 }
};
// --------------------------------------------------------------------------
enum Command
{
	cmdNOP,
	cmdDRead,
	cmdDWrite,
	cmdARead,
	cmdAWrite,
	cmdConfig,
	cmdSubConfig

} cmd;
// --------------------------------------------------------------------------
static void insn_config( comedi_t* card, int subdev, int channel, lsampl_t iotype, int range, int aref );
static void insn_subdev_config( comedi_t* card, int subdev, lsampl_t type );
// --------------------------------------------------------------------------

int main(int argc, char* argv[])
{
	comedi_t* card;
	lsampl_t data = 0;
	int optindex = 0;
	int opt = 0;
	const char* dev = "/dev/comedi0";
	int val = -1;
	cmd = cmdNOP;
	int verb = 0;
	int autoconf = 0;
	int cnum = 0;
	int blink = 0;
	int exret = EXIT_SUCCESS;

	memset(chan,-1,sizeof(chan));

	while( (opt = getopt_long(argc, argv, "habr:w:i:o:s:d:c:p:m:q:",longopts,&optindex)) != -1 )
	{
		switch (opt)
		{
			case 'h':
				printf("-h|--help 		- this message\n");
				printf("[-w|--write] chan val - write to digital channel\n");
				printf("[-r|--read] chan 	- read from digital channel\n");
				printf("[-o|--awrite] chan val - write to analog channel\n");
				printf("[-i|--aread] chan 	- read from analog channel\n");
				printf("[-s|--subdev] sub	- use subdev number sub. (Default: 0)\n");
				printf("[-d|--device] dev	- use device dev. (Default: /dev/comedi0)\n");
				printf("[--aref] val		- AREF (Default: %d)\n",aref);
				printf("[--range] val		- RANGE (Default: %d)\n",range);
				printf("[-p|--plus] chan	- Add channel for command.\n");
				printf("[-a|--autoconf] 	- autoconf channel type\n");
				printf("[-q|--subdev-config] 	- subdev type configure\n");
				printf("    1 	- TBI 24/0\n");
				printf("    2 	- TBI 0/24\n");
				printf("    3 	- TBI 16/8\n");
				printf("aref:\n");
				printf("	0	- analog ref = analog ground\n");
				printf("	1	- analog ref = analog common\n");
				printf("	2	- analog ref = differential\n");
				printf("	3	- analog ref = other (undefined)\n");
				printf("range:\n");
				printf("	0	-  -10В - 10В\n");
				printf("	1	-  -5В - 5В\n");
				printf("	2	-  -2.5В - 2.5В\n");
				printf("	3	-  -1.25В - 1.25В\n");
				printf("[--blink]			- (blink output): ONLY FOR 'write': 0-1-0-1-0-...\n");
				printf("[--blink-msec] val	- Blink pause [msec]. Default: 300 msec\n");
			return 0;

			case 'r':
				chan[0] = atoi(optarg);
				cmd = cmdDRead;
			break;

			case 'w':
				chan[0] = atoi(optarg);
				cmd = cmdDWrite;
				if( optind<argc && (argv[optind])[0]!='-' )
						val = atoi(argv[optind]);
			break;

			case 'i':
				chan[0] = atoi(optarg);
				cmd = cmdARead;
			break;

			case 'o':
				chan[0] = atoi(optarg);
				cmd = cmdAWrite;
				if( optind<argc && (argv[optind])[0]!='-' )
						val = atoi(argv[optind]);
			break;

			case 'd':
				dev = optarg;
			break;

			case 's':
				subdev = atoi(optarg);
			break;

			case 'v':
				verb = 1;
			break;

			case 'x':
				range = atoi(optarg);
			break;

			case 'z':
				aref = atoi(optarg);
			break;

			case 'c':
				cmd = cmdConfig;
				val = atoi(optarg);
			break;

			case 'q':
				cmd = cmdSubConfig;
				val = atoi(optarg);
			break;

			case 'a':
				autoconf = 1;
			break;

			case 'p':
				chan[++cnum] = atoi(optarg);
			break;

			case 'm':
				blink_msec = atoi(optarg);
			break;

			case 'b':
				blink = 1;
			break;

			case '?':
			default:
				printf("? argumnet\n");
				return 0;
		}
	}

	card = comedi_open(dev);
	if( card == NULL )
	{
		comedi_perror("comedi_open error");
		exit(EXIT_FAILURE);
	}

	if( verb )
	{
		fprintf(stdout, "init comedi:\n");
		fprintf(stdout, "dev: %s\n",dev);
		fprintf(stdout, "subdev: %d\n",subdev);
		fprintf(stdout, "channel: %d\n",chan[0]);
	}

	switch( cmd )
	{
		case cmdDRead:
		{
			if( autoconf )
			{
				for( int k=0; chan[k]!=-1; k++ )
				{
					if( comedi_dio_config(card, subdev, chan[k],INSN_CONFIG_DIO_INPUT) < 0)
					{
						comedi_perror("can't configure DI channels");
						exret = EXIT_FAILURE;
					}
				}
			}

			for( int k=0; chan[k]!=-1; k++ )
			{
				if( comedi_dio_read(card, subdev, chan[k],&data) < 0)
				{
					fprintf(stderr, "can't read from channel %d (err(%d): %s)\n",chan[k],errno,strerror(errno));
			  		exret = EXIT_FAILURE;
				}

				printf("Digital input %d: %d\n",chan[k], data);
			}
		}
		break;

		case cmdDWrite:
		{
			if( autoconf )
			{
				for( int k=0; chan[k]!=-1; k++ )
				{
					if( comedi_dio_config(card, subdev, chan[k],INSN_CONFIG_DIO_OUTPUT) < 0 )
					{
						fprintf(stderr,"can't configure DO channels. (%d) %s", errno,strerror(errno));
						exret = EXIT_FAILURE;
					}
				}
			}
			// реализация мигания
			while(1)
			{
				for( int k=0; chan[k]!=-1; k++ )
				{
					if( verb )
						printf( "write: ch=%d val=%d\n",chan[k],val);

				  	if( comedi_dio_write(card, subdev, chan[k], val) < 0)
					{
						fprintf(stderr,"can't write 1 to channel %d. (%d) %s\n",k,errno,strerror(errno));
				  		exret = EXIT_FAILURE;
					}
				}

				if( !blink )
					break;

				val = val ? 0 : 1;
				usleep(1000*blink_msec);
			}
		}
		break;

		case cmdARead:
		{
			for( int k=0; chan[k]!=-1; k++ )
			{
				if( autoconf )
					insn_config(card, subdev,chan[k],100,range,aref);

				int ret = comedi_data_read(card, subdev, chan[k], range, aref, &data);
				if( ret < 0)
				{
					fprintf(stderr, "can't read from channel %d: (%d) %s\n",chan[k],errno,strerror(errno));
				  	exret = EXIT_FAILURE;
				}

  				printf("Readed from channel %d value is %d\n", chan[k], data);
			}
		}
		break;

		case cmdAWrite:
		{
			for( int k=0; chan[k]!=-1; k++ )
			{
				if( autoconf )
					insn_config(card,subdev,chan[k],101,range,aref);

				int ret = comedi_data_write(card, subdev, chan[k], range, aref, val);
				if( ret < 0)
				{
					fprintf(stderr, "can't write to channel %d: (%d) %s\n",chan[k],errno,strerror(errno));
					exret = EXIT_FAILURE;
				}
			}
		}
		break;

		case cmdConfig:
		{
			for( int k=0; chan[k]!=-1; k++ )
			{

				if( val != INSN_CONFIG_DIO_INPUT
					&& val != INSN_CONFIG_DIO_OUTPUT
					&& val != 100 /* INSN_CONFIG_AIO_INPUT */
					&& val != 101 /* INSN_CONFIG_AIO_OUTPUT */
				)
				{
	       		    fprintf(stderr, "can't config channel %d for type = %d (val=[%d,%d,%d,%d])\n"
							,chan[k],val,INSN_CONFIG_DIO_INPUT,INSN_CONFIG_DIO_OUTPUT,100,101);
					exret = EXIT_FAILURE;
				}
			}
		}

		case cmdSubConfig:
			insn_subdev_config(card, subdev, val);
		break;

		default:
			comedi_perror("\n...No command... Use -h for help\n");
			exit(EXIT_FAILURE);
		break;
	}

  exit(exret);
}

void insn_config( comedi_t* card, int subdev, int channel, lsampl_t iotype, int range, int aref )
{
	comedi_insn insn;
	memset(&insn,0,sizeof(insn));
	insn.insn = INSN_CONFIG;
	insn.n = 1;
	insn.data = &iotype;
	insn.subdev = subdev;
	insn.chanspec = CR_PACK(channel,range,aref);
	if( comedi_do_insn(card,&insn) < 0 )
	{
		fprintf(stderr, "can`t configure (AIO) subdev=%d channel=%d type=%d",subdev,channel,iotype);
	  	exit(EXIT_FAILURE);
	}
}

void insn_subdev_config( comedi_t* card, int subdev, lsampl_t type )
{
	lsampl_t cmd = 102;
	comedi_insn insn;
	memset(&insn,0,sizeof(insn));
	insn.insn = INSN_CONFIG;
	insn.n = 1;
	insn.data = &cmd;
	insn.unused[0] = type;
	insn.subdev = subdev;
	insn.chanspec = 0;

	switch(type)
	{
		case 1:
			printf("set subdev %d type: 'TBI 24/0'\n",subdev);
		break;

		case 2:
			printf("set subdev %d type: 'TBI 0/24'\n",subdev);
		break;

		case 3:
			printf("set subdev %d type: 'TBI 16/8'\n",subdev);
		break;

		default:
			printf("set subdev %d type: UNKNOWN\n",subdev);
		break;
	}

	if( comedi_do_insn(card,&insn) < 0 )
	{
		fprintf(stderr, "can`t configure subdev subdev=%d type=%d. Err(%d): %s",subdev,type,errno,strerror(errno));
	  	exit(EXIT_FAILURE);
	}
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2014-2016, imec
 * All rights reserved.
 */

#include <random>
#include <memory>
#include <cstdio>
#include <iostream>
#include <climits>
#include <stdexcept>

#include "error.h"
#include "bpmf.h"
#include "io.h"

static const bool measure_perf = false;

std::ostream *Sys::os;
int Sys::procid = -1;
int Sys::nprocs = -1;

int Sys::nsims;
int Sys::burnin;
int Sys::update_freq;
double Sys::alpha = 2.0;

std::string Sys::odirname = "";

bool Sys::permute = true;
bool Sys::verbose = false;

// verifies that A has the same non-zero structure as B
void assert_same_struct(SparseMatrixD &A, SparseMatrixD &B)
{
    assert(A.cols() == B.cols());
    assert(A.rows() == B.rows());
    for(int i=0; i<A.cols(); ++i) assert(A.col(i).nonZeros() == B.col(i).nonZeros());
}

//
// Does predictions for prediction matrix T
// Computes RMSE (Root Means Square Error)
//
void Sys::predict(Sys& other, bool all)
{
    int n = (iter < burnin) ? 0 : (iter - burnin);

    double se(0.0); // squared err
    double se_avg(0.0); // squared avg err
    unsigned nump(0); // number of predictions

    int lo = all ? 0 : from();
    int hi = all ? num() : to();
    #pragma omp parallel for reduction(+:se,se_avg,nump)
    for(int k = lo; k<hi; k++) {
        for (Eigen::SparseMatrix<double>::InnerIterator it(T,k); it; ++it)
        {
#ifdef BPMF_REDUCE
            if (it.row() >= other.to() || it.row() < other.from())
                continue;
#endif
            auto m = items().col(it.col());
            auto u = other.items().col(it.row());

            assert(m.norm() > 0.0);
            assert(u.norm() > 0.0);

            const double pred = m.dot(u) + mean_rating;
            se += sqr(it.value() - pred);

            // update average prediction
            double &avg = Pavg.coeffRef(it.row(), it.col());
            double delta = pred - avg;
            avg = (n == 0) ? pred : (avg + delta/n);
            double &m2 = Pm2.coeffRef(it.row(), it.col());
            m2 = (n == 0) ? 0 : m2 + delta * (pred - avg);
            se_avg += sqr(it.value() - avg);

            nump++;
        }
    }

    rmse = sqrt( se / nump );
    rmse_avg = sqrt( se_avg / nump );
}

//
// Prints sampling progress
//
void Sys::print(double items_per_sec, double ratings_per_sec, double norm_u, double norm_m) {
  char buf[1024];
  std::string phase = (iter < Sys::burnin) ? "Burnin" : "Sampling";
  sprintf(buf, "%d: %s iteration %d:\t RMSE: %3.4f\tavg RMSE: %3.4f\tFU(%6.2f)\tFM(%6.2f)\titems/sec: %6.2f\tratings/sec: %6.2fM\n",
                    Sys::procid, phase.c_str(), iter, rmse, rmse_avg, norm_u, norm_m, items_per_sec, ratings_per_sec / 1e6);
  Sys::cout() << buf;
}

//
// Constructor with that reads MTX files
//
Sys::Sys(std::string name, std::string fname, std::string probename)
    : name(name), iter(-1), assigned(false), dom(nprocs+1)
{

    read_matrix(fname, M);
    read_matrix(probename, T);

    auto rows = std::max(M.rows(), T.rows());
    auto cols = std::max(M.cols(), T.cols());
    M.conservativeResize(rows,cols);
    T.conservativeResize(rows,cols);
    Pm2 = Pavg = Torig = T; // reference ratings and predicted ratings
    assert(M.rows() == Pavg.rows());
    assert(M.cols() == Pavg.cols());
    assert(Sys::nprocs <= (int)Sys::max_procs);
}

//
// Constructs Sys as transpose of existing Sys
//
Sys::Sys(std::string name, const SparseMatrixD &Mt, const SparseMatrixD &Pt) : name(name), iter(-1), assigned(false), dom(nprocs+1) {
    M = Mt.transpose();
    Pm2 = Pavg = T = Torig = Pt.transpose(); // reference ratings and predicted ratings
    assert(M.rows() == Pavg.rows());
    assert(M.cols() == Pavg.cols());
}

Sys::~Sys()
{
    if (measure_perf) {
        Sys::cout() << " --------------------\n";
        Sys::cout() << name << ": sampling times on " << procid << "\n";
        for(int i = from(); i<to(); ++i)
        {
            Sys::cout() << "\t" << nnz(i) << "\t" << sample_time.at(i) / nsims  << "\n";
        }
        Sys::cout() << " --------------------\n\n";
    }
}

bool Sys::has_prop_posterior() const
{
    return propMu.nonZeros() > 0;
}

void Sys::add_prop_posterior(std::string fnames)
{
    if (fnames.empty()) return;

    std::size_t pos = fnames.find_first_of(",");
    std::string mu_name = fnames.substr(0, pos);
    std::string lambda_name = fnames.substr(pos+1);

    read_matrix(mu_name, propMu);
    read_matrix(lambda_name, propLambda);

    assert(propMu.cols() == num());
    assert(propLambda.cols() == num());

    assert(propMu.rows() == num_latent);
    assert(propLambda.rows() == num_latent * num_latent);

}

//
// Intializes internal Matrices and Vectors
//
void Sys::init()
{
    //-- M
    assert(M.rows() > 0 && M.cols() > 0);
    mean_rating = M.sum() / M.nonZeros();
    items().setZero();
    sum_map().setZero();
    cov_map().setZero();
    norm_map().setZero();
    col_permutation.setIdentity(num());

#ifdef BPMF_REDUCE
    precMu = MatrixNXd::Zero(num_latent, num());
    precLambda = Eigen::MatrixXd::Zero(num_latent * num_latent, num());
#endif

    if (Sys::odirname.size())
    {
        aggrMu = Eigen::MatrixXd::Zero(num_latent, num());
        aggrLambda = Eigen::MatrixXd::Zero(num_latent * num_latent, num());
    }

    int count_larger_bp1 = 0;
    int count_larger_bp2 = 0;
    int count_sum = 0;
    for(int k = 0; k<M.cols(); k++) {
        int count = M.col(k).nonZeros();
        count_sum += count;
        if (count > breakpoint1) count_larger_bp1++;
        if (count > breakpoint2) count_larger_bp2++;
    }

    Sys::cout() << "mean rating: " << mean_rating << std::endl;
    Sys::cout() << "total number of ratings in train: " << M.nonZeros() << std::endl;
    Sys::cout() << "total number of ratings in test: " << T.nonZeros() << std::endl;
    Sys::cout() << "average ratings per row: " << (double)count_sum / (double)M.cols() << std::endl;
    Sys::cout() << "rows > break_point1: " << 100. * (double)count_larger_bp1 / (double)M.cols() << std::endl;
    Sys::cout() << "rows > break_point2: " << 100. * (double)count_larger_bp2 / (double)M.cols() << std::endl;
    Sys::cout() << "num " << name << ": " << num() << std::endl;
    if (has_prop_posterior())
    {
        Sys::cout() << "with propagated posterior" << std::endl;
    }

    if (measure_perf) sample_time.resize(num(), .0);
}

class PrecomputedLLT : public Eigen::LLT<MatrixNNd>
{
  public:
    void operator=(const MatrixNNd &m) { m_matrix = m; m_isInitialized = true; m_info = Eigen::Success; }
};

void Sys::computeMuLambda(long idx, const Sys &other, VectorNd &rr, MatrixNNd &MM) const
{
    const int count = M.innerVector(idx).nonZeros(); // count of nonzeros elements in idx-th row of M matrix
                                                     // (how many movies watched idx-th user?).
    if (count < breakpoint2)
    {
        for (SparseMatrixD::InnerIterator it(M, idx); it; ++it)
        {
            auto col = other.items().col(it.row());
            MM.triangularView<Eigen::Upper>() += col * col.transpose();
            rr.noalias() += col * ((it.value() - mean_rating) * alpha);
        }
    }
    else
    {
        const int task_size = int(count / 100) + 1;

        unsigned from = M.outerIndexPtr()[idx];   // "from" belongs to [1..m], m - number of movies in M matrix
        unsigned to = M.outerIndexPtr()[idx + 1]; // "to"   belongs to [1..m], m - number of movies in M matrix

        thread_vector<VectorNd> rrs(VectorNd::Zero());
        thread_vector<MatrixNNd> MMs(MatrixNNd::Zero());

#pragma omp taskloop shared(rrs, MMs, other) grainsize(task_size)
        for (unsigned j = from; j < to; j++)
        {
            // for each nonzeros elemen in the i-th row of M matrix
            auto val = M.valuePtr()[j];        // value of the j-th nonzeros element from idx-th row of M matrix
            auto idx = M.innerIndexPtr()[j];   // index "j" of the element [i,j] from M matrix in compressed M matrix
            auto col = other.items().col(idx); // vector num_latent x 1 from V matrix: M[i,j] = U[i,:] x V[idx,:]

            MMs.local().triangularView<Eigen::Upper>() += col * col.transpose(); // outer product
            rrs.local().noalias() += col * ((val - mean_rating) * alpha);        // vector num_latent x 1
        }

        // accumulate
        MM += MMs.combine();
        rr += rrs.combine();
    }
}

//
// Update ONE movie or one user
//
VectorNd Sys::sample(long idx, Sys &other)
{
    auto start = tick();

    VectorNd hp_mu;
    MatrixNNd hp_LambdaF;
    MatrixNNd hp_LambdaL;
    if (has_prop_posterior())
    {
        hp_mu = propMu.col(idx);
        hp_LambdaF = Eigen::Map<MatrixNNd>(propLambda.col(idx).data());
        hp_LambdaL =  hp_LambdaF.llt().matrixL();
    }
    else
    {
        hp_mu = hp.mu;
        hp_LambdaF = hp.LambdaF;
        hp_LambdaL = hp.LambdaL;
    }

    VectorNd rr = hp_LambdaF * hp.mu;                // vector num_latent x 1, we will use it in formula (14) from the paper
    MatrixNNd MM(MatrixNNd::Zero());
    PrecomputedLLT chol;                             // matrix num_latent x num_latent, chol="lambda_i with *" from formula (14)

#ifdef BPMF_REDUCE
    rr += precMu.col(idx);
    MM += precLambdaMatrix(idx);
#else
    computeMuLambda(idx, other, rr, MM);
#endif

    // copy upper -> lower part, matrix is symmetric.
    MM.triangularView<Eigen::Lower>() = MM.transpose();

    chol.compute(hp_LambdaF + alpha * MM);

    if(chol.info() != Eigen::Success) THROWERROR("Cholesky failed");

    // now we should calculate formula (14) from the paper
    // u_i for k-th iteration = Gaussian distribution N(u_i | mu_i with *, [lambda_i with *]^-1) =
    //                        = mu_i with * + s * [U]^-1,
    //                        where
    //                              s is a random vector with N(0, I),
    //                              mu_i with * is a vector num_latent x 1,
    //                              mu_i with * = [lambda_i with *]^-1 * rr,
    //                              lambda_i with * = L * U

    // Expression u_i = U \ (s + (L \ rr)) in Matlab looks for Eigen library like:

    chol.matrixL().solveInPlace(rr);                    // L*Y=rr => Y=L\rr, we store Y result again in rr vector
    rr += nrandn(num_latent);                           // rr=s+(L\rr), we store result again in rr vector
    chol.matrixU().solveInPlace(rr);                    // u_i=U\rr
    items().col(idx) = rr;                              // we save rr vector in items matrix (it is user features matrix)

#ifdef BPMF_REDUCE
    #pragma omp critical
    for (SparseMatrixD::InnerIterator it(M, idx); it; ++it)
    {
        other.precLambdaMatrix(it.row()).triangularView<Eigen::Upper>() += rr * rr.transpose();
        other.precMu.col(it.row()).noalias() += rr * (it.value() - mean_rating) * alpha;
    }
#endif

    auto stop = tick();
    register_time(idx, 1e6 * (stop - start));
    //Sys::cout() << "  " << count << ": " << 1e6*(stop - start) << std::endl;

    assert(rr.norm() > .0);

    return rr;
}

//
// update ALL movies / users in parallel
//
void Sys::sample(Sys &other)
{
    iter++;
    thread_vector<VectorNd>  sums(VectorNd::Zero()); // sum
    thread_vector<double>    norms(0.0); // squared norm
    thread_vector<MatrixNNd> prods(MatrixNNd::Zero()); // outer prod

#ifdef BPMF_REDUCE
    other.precMu.setZero();
    other.precLambda.setZero();
#endif

#pragma omp parallel for schedule(guided)
    for (int i = from(); i < to(); ++i)
    {
#pragma omp task
        {
            auto r = sample(i, other);

            MatrixNNd cov = (r * r.transpose());
            prods.local() += cov;
            sums.local() += r;
            norms.local() += r.squaredNorm();

            if (iter >= burnin && Sys::odirname.size())
            {
                aggrMu.col(i) += r;
                aggrLambda.col(i) += Eigen::Map<Eigen::VectorXd>(cov.data(), num_latent * num_latent);
            }

            send_item(i);
        }
    }
#pragma omp taskwait

    VectorNd sum = sums.combine();
    MatrixNNd prod = prods.combine();
    double norm = norms.combine();

    const int N = num();
    local_sum() = sum;
    local_cov() = (prod - (sum * sum.transpose() / N)) / (N-1);
    local_norm() = norm;
}

void Sys::register_time(int i, double t)
{
    if (measure_perf) sample_time.at(i) += t;
}<commit_msg>ENH: add perf counters<commit_after>/*
 * Copyright (c) 2014-2016, imec
 * All rights reserved.
 */

#include <random>
#include <memory>
#include <cstdio>
#include <iostream>
#include <climits>
#include <stdexcept>

#include "error.h"
#include "bpmf.h"
#include "io.h"

static const bool measure_perf = false;

std::ostream *Sys::os;
int Sys::procid = -1;
int Sys::nprocs = -1;

int Sys::nsims;
int Sys::burnin;
int Sys::update_freq;
double Sys::alpha = 2.0;

std::string Sys::odirname = "";

bool Sys::permute = true;
bool Sys::verbose = false;

// verifies that A has the same non-zero structure as B
void assert_same_struct(SparseMatrixD &A, SparseMatrixD &B)
{
    assert(A.cols() == B.cols());
    assert(A.rows() == B.rows());
    for(int i=0; i<A.cols(); ++i) assert(A.col(i).nonZeros() == B.col(i).nonZeros());
}

//
// Does predictions for prediction matrix T
// Computes RMSE (Root Means Square Error)
//
void Sys::predict(Sys& other, bool all)
{
    int n = (iter < burnin) ? 0 : (iter - burnin);

    double se(0.0); // squared err
    double se_avg(0.0); // squared avg err
    unsigned nump(0); // number of predictions

    int lo = all ? 0 : from();
    int hi = all ? num() : to();
    #pragma omp parallel for reduction(+:se,se_avg,nump)
    for(int k = lo; k<hi; k++) {
        for (Eigen::SparseMatrix<double>::InnerIterator it(T,k); it; ++it)
        {
#ifdef BPMF_REDUCE
            if (it.row() >= other.to() || it.row() < other.from())
                continue;
#endif
            auto m = items().col(it.col());
            auto u = other.items().col(it.row());

            assert(m.norm() > 0.0);
            assert(u.norm() > 0.0);

            const double pred = m.dot(u) + mean_rating;
            se += sqr(it.value() - pred);

            // update average prediction
            double &avg = Pavg.coeffRef(it.row(), it.col());
            double delta = pred - avg;
            avg = (n == 0) ? pred : (avg + delta/n);
            double &m2 = Pm2.coeffRef(it.row(), it.col());
            m2 = (n == 0) ? 0 : m2 + delta * (pred - avg);
            se_avg += sqr(it.value() - avg);

            nump++;
        }
    }

    rmse = sqrt( se / nump );
    rmse_avg = sqrt( se_avg / nump );
}

//
// Prints sampling progress
//
void Sys::print(double items_per_sec, double ratings_per_sec, double norm_u, double norm_m) {
  char buf[1024];
  std::string phase = (iter < Sys::burnin) ? "Burnin" : "Sampling";
  sprintf(buf, "%d: %s iteration %d:\t RMSE: %3.4f\tavg RMSE: %3.4f\tFU(%6.2f)\tFM(%6.2f)\titems/sec: %6.2f\tratings/sec: %6.2fM\n",
                    Sys::procid, phase.c_str(), iter, rmse, rmse_avg, norm_u, norm_m, items_per_sec, ratings_per_sec / 1e6);
  Sys::cout() << buf;
}

//
// Constructor with that reads MTX files
//
Sys::Sys(std::string name, std::string fname, std::string probename)
    : name(name), iter(-1), assigned(false), dom(nprocs+1)
{

    read_matrix(fname, M);
    read_matrix(probename, T);

    auto rows = std::max(M.rows(), T.rows());
    auto cols = std::max(M.cols(), T.cols());
    M.conservativeResize(rows,cols);
    T.conservativeResize(rows,cols);
    Pm2 = Pavg = Torig = T; // reference ratings and predicted ratings
    assert(M.rows() == Pavg.rows());
    assert(M.cols() == Pavg.cols());
    assert(Sys::nprocs <= (int)Sys::max_procs);
}

//
// Constructs Sys as transpose of existing Sys
//
Sys::Sys(std::string name, const SparseMatrixD &Mt, const SparseMatrixD &Pt) : name(name), iter(-1), assigned(false), dom(nprocs+1) {
    M = Mt.transpose();
    Pm2 = Pavg = T = Torig = Pt.transpose(); // reference ratings and predicted ratings
    assert(M.rows() == Pavg.rows());
    assert(M.cols() == Pavg.cols());
}

Sys::~Sys()
{
    if (measure_perf) {
        Sys::cout() << " --------------------\n";
        Sys::cout() << name << ": sampling times on " << procid << "\n";
        for(int i = from(); i<to(); ++i)
        {
            Sys::cout() << "\t" << nnz(i) << "\t" << sample_time.at(i) / nsims  << "\n";
        }
        Sys::cout() << " --------------------\n\n";
    }
}

bool Sys::has_prop_posterior() const
{
    return propMu.nonZeros() > 0;
}

void Sys::add_prop_posterior(std::string fnames)
{
    if (fnames.empty()) return;

    std::size_t pos = fnames.find_first_of(",");
    std::string mu_name = fnames.substr(0, pos);
    std::string lambda_name = fnames.substr(pos+1);

    read_matrix(mu_name, propMu);
    read_matrix(lambda_name, propLambda);

    assert(propMu.cols() == num());
    assert(propLambda.cols() == num());

    assert(propMu.rows() == num_latent);
    assert(propLambda.rows() == num_latent * num_latent);

}

//
// Intializes internal Matrices and Vectors
//
void Sys::init()
{
    //-- M
    assert(M.rows() > 0 && M.cols() > 0);
    mean_rating = M.sum() / M.nonZeros();
    items().setZero();
    sum_map().setZero();
    cov_map().setZero();
    norm_map().setZero();
    col_permutation.setIdentity(num());

#ifdef BPMF_REDUCE
    precMu = MatrixNXd::Zero(num_latent, num());
    precLambda = Eigen::MatrixXd::Zero(num_latent * num_latent, num());
#endif

    if (Sys::odirname.size())
    {
        aggrMu = Eigen::MatrixXd::Zero(num_latent, num());
        aggrLambda = Eigen::MatrixXd::Zero(num_latent * num_latent, num());
    }

    int count_larger_bp1 = 0;
    int count_larger_bp2 = 0;
    int count_sum = 0;
    for(int k = 0; k<M.cols(); k++) {
        int count = M.col(k).nonZeros();
        count_sum += count;
        if (count > breakpoint1) count_larger_bp1++;
        if (count > breakpoint2) count_larger_bp2++;
    }

    Sys::cout() << "mean rating: " << mean_rating << std::endl;
    Sys::cout() << "total number of ratings in train: " << M.nonZeros() << std::endl;
    Sys::cout() << "total number of ratings in test: " << T.nonZeros() << std::endl;
    Sys::cout() << "average ratings per row: " << (double)count_sum / (double)M.cols() << std::endl;
    Sys::cout() << "rows > break_point1: " << 100. * (double)count_larger_bp1 / (double)M.cols() << std::endl;
    Sys::cout() << "rows > break_point2: " << 100. * (double)count_larger_bp2 / (double)M.cols() << std::endl;
    Sys::cout() << "num " << name << ": " << num() << std::endl;
    if (has_prop_posterior())
    {
        Sys::cout() << "with propagated posterior" << std::endl;
    }

    if (measure_perf) sample_time.resize(num(), .0);
}

class PrecomputedLLT : public Eigen::LLT<MatrixNNd>
{
  public:
    void operator=(const MatrixNNd &m) { m_matrix = m; m_isInitialized = true; m_info = Eigen::Success; }
};

void Sys::computeMuLambda(long idx, const Sys &other, VectorNd &rr, MatrixNNd &MM) const
{
    BPMF_COUNTER("computeMuLambda");
    const int count = M.innerVector(idx).nonZeros(); // count of nonzeros elements in idx-th row of M matrix
                                                     // (how many movies watched idx-th user?).
    if (count < breakpoint2)
    {
        for (SparseMatrixD::InnerIterator it(M, idx); it; ++it)
        {
            auto col = other.items().col(it.row());
            MM.triangularView<Eigen::Upper>() += col * col.transpose();
            rr.noalias() += col * ((it.value() - mean_rating) * alpha);
        }
    }
    else
    {
        const int task_size = int(count / 100) + 1;

        unsigned from = M.outerIndexPtr()[idx];   // "from" belongs to [1..m], m - number of movies in M matrix
        unsigned to = M.outerIndexPtr()[idx + 1]; // "to"   belongs to [1..m], m - number of movies in M matrix

        thread_vector<VectorNd> rrs(VectorNd::Zero());
        thread_vector<MatrixNNd> MMs(MatrixNNd::Zero());

#pragma omp taskloop shared(rrs, MMs, other) grainsize(task_size)
        for (unsigned j = from; j < to; j++)
        {
            // for each nonzeros elemen in the i-th row of M matrix
            auto val = M.valuePtr()[j];        // value of the j-th nonzeros element from idx-th row of M matrix
            auto idx = M.innerIndexPtr()[j];   // index "j" of the element [i,j] from M matrix in compressed M matrix
            auto col = other.items().col(idx); // vector num_latent x 1 from V matrix: M[i,j] = U[i,:] x V[idx,:]

            MMs.local().triangularView<Eigen::Upper>() += col * col.transpose(); // outer product
            rrs.local().noalias() += col * ((val - mean_rating) * alpha);        // vector num_latent x 1
        }

        // accumulate
        MM += MMs.combine();
        rr += rrs.combine();
    }
}

//
// Update ONE movie or one user
//
VectorNd Sys::sample(long idx, Sys &other)
{
    auto start = tick();

    VectorNd hp_mu;
    MatrixNNd hp_LambdaF;
    MatrixNNd hp_LambdaL;
    if (has_prop_posterior())
    {
        hp_mu = propMu.col(idx);
        hp_LambdaF = Eigen::Map<MatrixNNd>(propLambda.col(idx).data());
        hp_LambdaL =  hp_LambdaF.llt().matrixL();
    }
    else
    {
        hp_mu = hp.mu;
        hp_LambdaF = hp.LambdaF;
        hp_LambdaL = hp.LambdaL;
    }

    VectorNd rr = hp_LambdaF * hp.mu;                // vector num_latent x 1, we will use it in formula (14) from the paper
    MatrixNNd MM(MatrixNNd::Zero());
    PrecomputedLLT chol;                             // matrix num_latent x num_latent, chol="lambda_i with *" from formula (14)

#ifdef BPMF_REDUCE
    rr += precMu.col(idx);
    MM += precLambdaMatrix(idx);
#else
    computeMuLambda(idx, other, rr, MM);
#endif

    // copy upper -> lower part, matrix is symmetric.
    MM.triangularView<Eigen::Lower>() = MM.transpose();

    chol.compute(hp_LambdaF + alpha * MM);

    if(chol.info() != Eigen::Success) THROWERROR("Cholesky failed");

    // now we should calculate formula (14) from the paper
    // u_i for k-th iteration = Gaussian distribution N(u_i | mu_i with *, [lambda_i with *]^-1) =
    //                        = mu_i with * + s * [U]^-1,
    //                        where
    //                              s is a random vector with N(0, I),
    //                              mu_i with * is a vector num_latent x 1,
    //                              mu_i with * = [lambda_i with *]^-1 * rr,
    //                              lambda_i with * = L * U

    // Expression u_i = U \ (s + (L \ rr)) in Matlab looks for Eigen library like:

    chol.matrixL().solveInPlace(rr);                    // L*Y=rr => Y=L\rr, we store Y result again in rr vector
    rr += nrandn(num_latent);                           // rr=s+(L\rr), we store result again in rr vector
    chol.matrixU().solveInPlace(rr);                    // u_i=U\rr
    items().col(idx) = rr;                              // we save rr vector in items matrix (it is user features matrix)

#ifdef BPMF_REDUCE
    {
        BPMF_COUNTER("update_Lambda");

        MatrixNNd Lambda_update;
        Lambda_update.triangularView<Eigen::Upper>() = rr * rr.transpose();

    for (SparseMatrixD::InnerIterator it(M, idx); it; ++it)
    {
            other.precLambdaMatrix(it.row()).triangularView<Eigen::Upper>() += Lambda_update;
            VectorNd mu_update = rr * (it.value() - mean_rating) * alpha;
            other.precMu.col(it.row()).noalias() += mu_update;
        }
    }
#endif

    auto stop = tick();
    register_time(idx, 1e6 * (stop - start));
    //Sys::cout() << "  " << count << ": " << 1e6*(stop - start) << std::endl;

    assert(rr.norm() > .0);

    return rr;
}

//
// update ALL movies / users in parallel
//
void Sys::sample(Sys &other)
{
    iter++;
    thread_vector<VectorNd>  sums(VectorNd::Zero()); // sum
    thread_vector<double>    norms(0.0); // squared norm
    thread_vector<MatrixNNd> prods(MatrixNNd::Zero()); // outer prod

#ifdef BPMF_REDUCE
    other.precMu.setZero();
    other.precLambda.setZero();
#endif

#pragma omp parallel for schedule(guided)
    for (int i = from(); i < to(); ++i)
    {
#pragma omp task
        {
            auto r = sample(i, other);

            MatrixNNd cov = (r * r.transpose());
            prods.local() += cov;
            sums.local() += r;
            norms.local() += r.squaredNorm();

            if (iter >= burnin && Sys::odirname.size())
            {
                aggrMu.col(i) += r;
                aggrLambda.col(i) += Eigen::Map<Eigen::VectorXd>(cov.data(), num_latent * num_latent);
            }

            send_item(i);
        }
    }
#pragma omp taskwait

    VectorNd sum = sums.combine();
    MatrixNNd prod = prods.combine();
    double norm = norms.combine();

    const int N = num();
    local_sum() = sum;
    local_cov() = (prod - (sum * sum.transpose() / N)) / (N-1);
    local_norm() = norm;
}

void Sys::register_time(int i, double t)
{
    if (measure_perf) sample_time.at(i) += t;
}<|endoftext|>


<commit_before>/*
 * Copyright (c) 2014-2016, imec
 * All rights reserved.
 */

#include "error.h"
#include "bpmf.h"

#include <random>
#include <memory>
#include <cstdio>
#include <iostream>
#include <climits>
#include <stdexcept>

#include "io.h"

#if defined(_OPENMP)
#include "omp.h"

#pragma omp declare reduction (VectorPlus : VectorNd : omp_out += omp_in) initializer(omp_priv = VectorNd::Zero())
#pragma omp declare reduction (MatrixPlus : MatrixNNd : omp_out += omp_in) initializer(omp_priv = MatrixNNd::Zero())
#endif

static const bool measure_perf = false;

std::ostream *Sys::os;
int Sys::procid = -1;
int Sys::nprocs = -1;

int Sys::nsims;
int Sys::burnin;

bool Sys::permute = true;

bool Sys::verbose = false;

unsigned Sys::grain_size;

void calc_upper_part(MatrixNNd &m, VectorNd v);         // function for calcutation of an upper part of a symmetric matrix: m = v * v.transpose();
void copy_lower_part(MatrixNNd &m);                     // function to copy an upper part of a symmetric matrix to a lower part

// verifies that A is transpose of B
void assert_transpose(SparseMatrixD &A, SparseMatrixD &B)
{
    SparseMatrixD At = A.transpose();
    SparseMatrixD Bt = B.transpose();
    assert(At.cols() == B.cols());
    assert(A.cols() == Bt.cols());

    for(int i=0; i<B.cols(); ++i) assert(At.col(i).nonZeros() == B.col(i).nonZeros());
    for(int i=0; i<A.cols(); ++i) assert(Bt.col(i).nonZeros() == A.col(i).nonZeros());
}

//
// Does predictions for prediction matrix T
// Computes RMSE (Root Means Square Error)
//
void Sys::predict(Sys& other, bool all)
{
    int n = (iter < burnin) ? 0 : (iter - burnin);

    double se(0.0); // squared err
    double se_avg(0.0); // squared avg err
    unsigned nump(0); // number of predictions

    int lo = all ? 0 : from();
    int hi = all ? num() : to();
    #pragma omp parallel for reduction(+:se,se_avg,nump)
    for(int k = lo; k<hi; k++) {
        for (Eigen::SparseMatrix<double>::InnerIterator it(T,k); it; ++it)
        {
            auto m = items().col(it.col());
            auto u = other.items().col(it.row());

            assert(m.norm() > 0.0);
            assert(u.norm() > 0.0);

            const double pred = m.dot(u) + mean_rating;
            se += sqr(it.value() - pred);

            // update average prediction
            double &avg = Pavg.coeffRef(it.row(), it.col());
            double delta = pred - avg;
            avg = (n == 0) ? pred : (avg + delta/n);
            double &m2 = Pm2.coeffRef(it.row(), it.col());
            m2 = (n == 0) ? 0 : m2 + delta * (pred - avg);
            se_avg += sqr(it.value() - avg);

            nump++;
        }
    }

    rmse = sqrt( se / nump );
    rmse_avg = sqrt( se_avg / nump );
}

//
// Prints sampling progress
//
void Sys::print(double items_per_sec, double ratings_per_sec, double norm_u, double norm_m) {
  char buf[1024];
  std::string phase = (iter < Sys::burnin) ? "Burnin" : "Sampling";
  sprintf(buf, "%d: %s iteration %d:\t RMSE: %3.2f\tavg RMSE: %3.2f\tFU(%6.2f)\tFM(%6.2f)\titems/sec: %6.2f\tratings/sec: %6.2fM\n",
                    Sys::procid, phase.c_str(), iter, rmse, rmse_avg, norm_u, norm_m, items_per_sec, ratings_per_sec / 1e6);
  Sys::cout() << buf;
}

//
// Constructor with that reads MTX files
//
Sys::Sys(std::string name, std::string fname, std::string probename)
    : name(name), iter(-1), assigned(false), dom(nprocs+1)
{

    read_matrix(fname, M);
    read_matrix(probename, T);

    auto rows = std::max(M.rows(), T.rows());
    auto cols = std::max(M.cols(), T.cols());
    M.conservativeResize(rows,cols);
    T.conservativeResize(rows,cols);
    Pm2 = Pavg = T; // reference ratings and predicted ratings
    assert(M.rows() == Pavg.rows());
    assert(M.cols() == Pavg.cols());
    assert(Sys::nprocs <= (int)Sys::max_procs);
}

//
// Constructs Sys as transpose of existing Sys
//
Sys::Sys(std::string name, const SparseMatrixD &Mt, const SparseMatrixD &Pt) : name(name), iter(-1), assigned(false), dom(nprocs+1) {
    M = Mt.transpose();
    Pm2 = Pavg = T = Pt.transpose(); // reference ratings and predicted ratings
    assert(M.rows() == Pavg.rows());
    assert(M.cols() == Pavg.cols());
}

Sys::~Sys()
{
    if (measure_perf) {
        Sys::cout() << " --------------------\n";
        Sys::cout() << name << ": sampling times on " << procid << "\n";
        for(int i = from(); i<to(); ++i)
        {
            Sys::cout() << "\t" << nnz(i) << "\t" << sample_time.at(i) / nsims  << "\n";
        }
        Sys::cout() << " --------------------\n\n";
    }
}

bool Sys::has_prop_posterior() const
{
    return propMu.nonZeros() > 0;
}

void Sys::add_prop_posterior(std::string fnames)
{
    if (fnames.empty()) return;

    std::size_t pos = fnames.find_first_of(",");
    std::string mu_name = fnames.substr(0, pos);
    std::string lambda_name = fnames.substr(pos+1);

    read_matrix(mu_name, propMu);
    read_matrix(lambda_name, propLambda);

    assert(propMu.cols() == num());
    assert(propLambda.cols() == num());

    assert(propMu.rows() == num_latent);
    assert(propLambda.rows() == num_latent * num_latent);

}

//
// Intializes internal Matrices and Vectors
//
void Sys::init()
{
    //-- M
    assert(M.rows() > 0 && M.cols() > 0);
    mean_rating = M.sum() / M.nonZeros();
    items().setZero();
    sum_map().setZero();
    cov_map().setZero();
    norm_map().setZero();

    aggrMu = Eigen::MatrixXd::Zero(num_latent, num());
    aggrLambda = Eigen::MatrixXd::Zero(num_latent * num_latent, num());

    Sys::cout() << "mean rating = " << mean_rating << std::endl;
    Sys::cout() << "total number of ratings in train = " << M.nonZeros() << std::endl;
    Sys::cout() << "total number of ratings in test = " << T.nonZeros() << std::endl;
    Sys::cout() << "num " << name << ": " << num() << std::endl;
    if (has_prop_posterior())
    {
        Sys::cout() << "with propagated posterior" << std::endl;
    }

    if (measure_perf) sample_time.resize(num(), .0);
}

class PrecomputedLLT : public Eigen::LLT<MatrixNNd>
{
  public:
    void operator=(const MatrixNNd &m) { m_matrix = m; m_isInitialized = true; m_info = Eigen::Success; }
};

//
// Update ONE movie or one user
//
VectorNd Sys::sample(long idx, const MapNXd in)
{
    auto start = tick();
    const double alpha = 2;       // Gaussian noice

    VectorNd hp_mu;
    MatrixNNd hp_LambdaF;
    MatrixNNd hp_LambdaL;
    if (has_prop_posterior())
    {
        hp_mu = propMu.col(idx);
        hp_LambdaF = Eigen::Map<MatrixNNd>(propLambda.col(idx).data());
        hp_LambdaL =  hp_LambdaF.llt().matrixL();
    }
    else
    {
        hp_mu = hp.mu;
        hp_LambdaF = hp.LambdaF;
        hp_LambdaL = hp.LambdaL;
    }

    int breakpoint1 = 24;
    int breakpoint2 = 10500;

    const int count = M.innerVector(idx).nonZeros(); // count of nonzeros elements in idx-th row of M matrix
                                                     // (how many movies watched idx-th user?).

    VectorNd rr = hp_LambdaF * hp.mu;                 // vector num_latent x 1, we will use it in formula (14) from the paper
    PrecomputedLLT chol;                             // matrix num_latent x num_latent, chol="lambda_i with *" from formula (14)

    // if this user movie has less than 1K ratings,
    // we do a serial rank update
    if( count < breakpoint1 ) {

        chol = hp_LambdaL;
        for (SparseMatrixD::InnerIterator it(M,idx); it; ++it) {
            auto col = in.col(it.row());
            chol.rankUpdate(col, alpha);
            rr.noalias() += col * ((it.value() - mean_rating) * alpha);
        }

    // else we do a serial full cholesky decomposition
    // (not used if breakpoint1 == breakpoint2)
    } else if (count < breakpoint2) {

        MatrixNNd MM(MatrixNNd::Zero());
        for (SparseMatrixD::InnerIterator it(M,idx); it; ++it) {
            auto col = in.col(it.row());

            //MM.noalias() += col * col.transpose();
            calc_upper_part(MM, col);

            rr.noalias() += col * ((it.value() - mean_rating) * alpha);
        }

        // Here, we copy a triangular upper part to a triangular lower part, because the matrix is symmetric.
        copy_lower_part(MM);

        chol.compute(hp_LambdaF + alpha * MM);
    // for > 1K ratings, we have additional thread-level parallellism
    } else {
        auto from = M.outerIndexPtr()[idx];   // "from" belongs to [1..m], m - number of movies in M matrix
        auto to = M.outerIndexPtr()[idx+1];   // "to"   belongs to [1..m], m - number of movies in M matrix
        MatrixNNd MM(MatrixNNd::Zero());               // matrix num_latent x num_latent

        // #pragma omp parallel for reduction(VectorPlus:rr) reduction(MatrixPlus:MM)
        #pragma omp parallel for reduction(VectorPlus:rr) reduction(MatrixPlus:MM) schedule(dynamic,200)
        for(int j = from; j<to; ++j) {                 // for each nonzeros elemen in the i-th row of M matrix
            auto val = M.valuePtr()[j];                // value of the j-th nonzeros element from idx-th row of M matrix
            auto idx = M.innerIndexPtr()[j];           // index "j" of the element [i,j] from M matrix in compressed M matrix
            auto col = in.col(idx);                    // vector num_latent x 1 from V matrix: M[i,j] = U[i,:] x V[idx,:]

            //MM.noalias() += col * col.transpose();     // outer product
            calc_upper_part(MM, col);
            rr.noalias() += col * ((val - mean_rating) * alpha); // vector num_latent x 1
        }

        copy_lower_part(MM);

        chol.compute(hp_LambdaF + alpha * MM);         // matrix num_latent x num_latent
                                                       // chol="lambda_i with *" from formula (14)
                                                       // lambda_i with * = LambdaU + alpha * MM
    }

    if(chol.info() != Eigen::Success) THROWERROR("Cholesky failed");

    // now we should calculate formula (14) from the paper
    // u_i for k-th iteration = Gaussian distribution N(u_i | mu_i with *, [lambda_i with *]^-1) =
    //                        = mu_i with * + s * [U]^-1,
    //                        where
    //                              s is a random vector with N(0, I),
    //                              mu_i with * is a vector num_latent x 1,
    //                              mu_i with * = [lambda_i with *]^-1 * rr,
    //                              lambda_i with * = L * U

    // Expression u_i = U \ (s + (L \ rr)) in Matlab looks for Eigen library like:

    chol.matrixL().solveInPlace(rr);                    // L*Y=rr => Y=L\rr, we store Y result again in rr vector
    rr += nrandn();                                     // rr=s+(L\rr), we store result again in rr vector
    chol.matrixU().solveInPlace(rr);                    // u_i=U\rr
    items().col(idx) = rr;                              // we save rr vector in items matrix (it is user features matrix)

    auto stop = tick();
    register_time(idx, 1e6 * (stop - start));
    //Sys::cout() << "  " << count << ": " << 1e6*(stop - start) << std::endl;

    assert(rr.norm() > .0);

    return rr;
}

//
// update ALL movies / users in parallel
//
void Sys::sample(Sys &in)
{
    iter++;
    VectorNd  sum(VectorNd::Zero()); // sum
    double    norm(0.0); // squared norm
    MatrixNNd prod(MatrixNNd::Zero()); // outer prod

//#pragma omp parallel for reduction(VectorPlus:sum) reduction(MatrixPlus:prod) reduction(+:norm) schedule(dynamic, 1)
#pragma omp parallel for reduction(VectorPlus:sum) reduction(MatrixPlus:prod) reduction(+:norm) schedule(dynamic,1)
    for(int i = from(); i<to(); ++i) {
        auto r = sample(i,in.items());

        MatrixNNd cov = (r * r.transpose());
        prod += cov;
        sum += r;
        norm += r.squaredNorm();

        if (iter >= burnin)
        {
            aggrMu.col(i) += r;
            aggrLambda.col(i) += Eigen::Map<Eigen::VectorXd>(cov.data(), num_latent * num_latent);
        }

        send_items(i, i + 1);
    }

    const int N = num();
    local_sum() = sum;
    local_cov() = (prod - (sum * sum.transpose() / N)) / (N-1);
    local_norm() = norm;

}

void Sys::register_time(int i, double t)
{
    if (measure_perf) sample_time.at(i) += t;
}

void calc_upper_part(MatrixNNd &m, VectorNd v)
{
  // we use the formula: m = m + v * v.transpose(), but we calculate only an upper part of m matrix
  for (int j=0; j<num_latent; j++)          // columns
  {
    for(int i=0; i<=j; i++)              // rows
    {
      m(i,j) = m(i,j) + v[j] * v[i];
    }
  }
}

void copy_lower_part(MatrixNNd &m)
{
  // Here, we copy a triangular upper part to a triangular lower part, because the matrix is symmetric.
  for (int j=1; j<num_latent; j++)          // columns
  {
    for(int i=0; i<=j-1; i++)            // rows
    {
      m(j,i) = m(i,j);
    }
  }
}
<commit_msg>ENH: print 4 digits after decimal point<commit_after>/*
 * Copyright (c) 2014-2016, imec
 * All rights reserved.
 */

#include "error.h"
#include "bpmf.h"

#include <random>
#include <memory>
#include <cstdio>
#include <iostream>
#include <climits>
#include <stdexcept>

#include "io.h"

#if defined(_OPENMP)
#include "omp.h"

#pragma omp declare reduction (VectorPlus : VectorNd : omp_out += omp_in) initializer(omp_priv = VectorNd::Zero())
#pragma omp declare reduction (MatrixPlus : MatrixNNd : omp_out += omp_in) initializer(omp_priv = MatrixNNd::Zero())
#endif

static const bool measure_perf = false;

std::ostream *Sys::os;
int Sys::procid = -1;
int Sys::nprocs = -1;

int Sys::nsims;
int Sys::burnin;

bool Sys::permute = true;

bool Sys::verbose = false;

unsigned Sys::grain_size;

void calc_upper_part(MatrixNNd &m, VectorNd v);         // function for calcutation of an upper part of a symmetric matrix: m = v * v.transpose();
void copy_lower_part(MatrixNNd &m);                     // function to copy an upper part of a symmetric matrix to a lower part

// verifies that A is transpose of B
void assert_transpose(SparseMatrixD &A, SparseMatrixD &B)
{
    SparseMatrixD At = A.transpose();
    SparseMatrixD Bt = B.transpose();
    assert(At.cols() == B.cols());
    assert(A.cols() == Bt.cols());

    for(int i=0; i<B.cols(); ++i) assert(At.col(i).nonZeros() == B.col(i).nonZeros());
    for(int i=0; i<A.cols(); ++i) assert(Bt.col(i).nonZeros() == A.col(i).nonZeros());
}

//
// Does predictions for prediction matrix T
// Computes RMSE (Root Means Square Error)
//
void Sys::predict(Sys& other, bool all)
{
    int n = (iter < burnin) ? 0 : (iter - burnin);

    double se(0.0); // squared err
    double se_avg(0.0); // squared avg err
    unsigned nump(0); // number of predictions

    int lo = all ? 0 : from();
    int hi = all ? num() : to();
    #pragma omp parallel for reduction(+:se,se_avg,nump)
    for(int k = lo; k<hi; k++) {
        for (Eigen::SparseMatrix<double>::InnerIterator it(T,k); it; ++it)
        {
            auto m = items().col(it.col());
            auto u = other.items().col(it.row());

            assert(m.norm() > 0.0);
            assert(u.norm() > 0.0);

            const double pred = m.dot(u) + mean_rating;
            se += sqr(it.value() - pred);

            // update average prediction
            double &avg = Pavg.coeffRef(it.row(), it.col());
            double delta = pred - avg;
            avg = (n == 0) ? pred : (avg + delta/n);
            double &m2 = Pm2.coeffRef(it.row(), it.col());
            m2 = (n == 0) ? 0 : m2 + delta * (pred - avg);
            se_avg += sqr(it.value() - avg);

            nump++;
        }
    }

    rmse = sqrt( se / nump );
    rmse_avg = sqrt( se_avg / nump );
}

//
// Prints sampling progress
//
void Sys::print(double items_per_sec, double ratings_per_sec, double norm_u, double norm_m) {
  char buf[1024];
  std::string phase = (iter < Sys::burnin) ? "Burnin" : "Sampling";
  sprintf(buf, "%d: %s iteration %d:\t RMSE: %3.4f\tavg RMSE: %3.4f\tFU(%6.2f)\tFM(%6.2f)\titems/sec: %6.2f\tratings/sec: %6.2fM\n",
                    Sys::procid, phase.c_str(), iter, rmse, rmse_avg, norm_u, norm_m, items_per_sec, ratings_per_sec / 1e6);
  Sys::cout() << buf;
}

//
// Constructor with that reads MTX files
//
Sys::Sys(std::string name, std::string fname, std::string probename)
    : name(name), iter(-1), assigned(false), dom(nprocs+1)
{

    read_matrix(fname, M);
    read_matrix(probename, T);

    auto rows = std::max(M.rows(), T.rows());
    auto cols = std::max(M.cols(), T.cols());
    M.conservativeResize(rows,cols);
    T.conservativeResize(rows,cols);
    Pm2 = Pavg = T; // reference ratings and predicted ratings
    assert(M.rows() == Pavg.rows());
    assert(M.cols() == Pavg.cols());
    assert(Sys::nprocs <= (int)Sys::max_procs);
}

//
// Constructs Sys as transpose of existing Sys
//
Sys::Sys(std::string name, const SparseMatrixD &Mt, const SparseMatrixD &Pt) : name(name), iter(-1), assigned(false), dom(nprocs+1) {
    M = Mt.transpose();
    Pm2 = Pavg = T = Pt.transpose(); // reference ratings and predicted ratings
    assert(M.rows() == Pavg.rows());
    assert(M.cols() == Pavg.cols());
}

Sys::~Sys()
{
    if (measure_perf) {
        Sys::cout() << " --------------------\n";
        Sys::cout() << name << ": sampling times on " << procid << "\n";
        for(int i = from(); i<to(); ++i)
        {
            Sys::cout() << "\t" << nnz(i) << "\t" << sample_time.at(i) / nsims  << "\n";
        }
        Sys::cout() << " --------------------\n\n";
    }
}

bool Sys::has_prop_posterior() const
{
    return propMu.nonZeros() > 0;
}

void Sys::add_prop_posterior(std::string fnames)
{
    if (fnames.empty()) return;

    std::size_t pos = fnames.find_first_of(",");
    std::string mu_name = fnames.substr(0, pos);
    std::string lambda_name = fnames.substr(pos+1);

    read_matrix(mu_name, propMu);
    read_matrix(lambda_name, propLambda);

    assert(propMu.cols() == num());
    assert(propLambda.cols() == num());

    assert(propMu.rows() == num_latent);
    assert(propLambda.rows() == num_latent * num_latent);

}

//
// Intializes internal Matrices and Vectors
//
void Sys::init()
{
    //-- M
    assert(M.rows() > 0 && M.cols() > 0);
    mean_rating = M.sum() / M.nonZeros();
    items().setZero();
    sum_map().setZero();
    cov_map().setZero();
    norm_map().setZero();

    aggrMu = Eigen::MatrixXd::Zero(num_latent, num());
    aggrLambda = Eigen::MatrixXd::Zero(num_latent * num_latent, num());

    Sys::cout() << "mean rating = " << mean_rating << std::endl;
    Sys::cout() << "total number of ratings in train = " << M.nonZeros() << std::endl;
    Sys::cout() << "total number of ratings in test = " << T.nonZeros() << std::endl;
    Sys::cout() << "num " << name << ": " << num() << std::endl;
    if (has_prop_posterior())
    {
        Sys::cout() << "with propagated posterior" << std::endl;
    }

    if (measure_perf) sample_time.resize(num(), .0);
}

class PrecomputedLLT : public Eigen::LLT<MatrixNNd>
{
  public:
    void operator=(const MatrixNNd &m) { m_matrix = m; m_isInitialized = true; m_info = Eigen::Success; }
};

//
// Update ONE movie or one user
//
VectorNd Sys::sample(long idx, const MapNXd in)
{
    auto start = tick();
    const double alpha = 2;       // Gaussian noice

    VectorNd hp_mu;
    MatrixNNd hp_LambdaF;
    MatrixNNd hp_LambdaL;
    if (has_prop_posterior())
    {
        hp_mu = propMu.col(idx);
        hp_LambdaF = Eigen::Map<MatrixNNd>(propLambda.col(idx).data());
        hp_LambdaL =  hp_LambdaF.llt().matrixL();
    }
    else
    {
        hp_mu = hp.mu;
        hp_LambdaF = hp.LambdaF;
        hp_LambdaL = hp.LambdaL;
    }

    int breakpoint1 = 24;
    int breakpoint2 = 10500;

    const int count = M.innerVector(idx).nonZeros(); // count of nonzeros elements in idx-th row of M matrix
                                                     // (how many movies watched idx-th user?).

    VectorNd rr = hp_LambdaF * hp.mu;                 // vector num_latent x 1, we will use it in formula (14) from the paper
    PrecomputedLLT chol;                             // matrix num_latent x num_latent, chol="lambda_i with *" from formula (14)

    // if this user movie has less than 1K ratings,
    // we do a serial rank update
    if( count < breakpoint1 ) {

        chol = hp_LambdaL;
        for (SparseMatrixD::InnerIterator it(M,idx); it; ++it) {
            auto col = in.col(it.row());
            chol.rankUpdate(col, alpha);
            rr.noalias() += col * ((it.value() - mean_rating) * alpha);
        }

    // else we do a serial full cholesky decomposition
    // (not used if breakpoint1 == breakpoint2)
    } else if (count < breakpoint2) {

        MatrixNNd MM(MatrixNNd::Zero());
        for (SparseMatrixD::InnerIterator it(M,idx); it; ++it) {
            auto col = in.col(it.row());

            //MM.noalias() += col * col.transpose();
            calc_upper_part(MM, col);

            rr.noalias() += col * ((it.value() - mean_rating) * alpha);
        }

        // Here, we copy a triangular upper part to a triangular lower part, because the matrix is symmetric.
        copy_lower_part(MM);

        chol.compute(hp_LambdaF + alpha * MM);
    // for > 1K ratings, we have additional thread-level parallellism
    } else {
        auto from = M.outerIndexPtr()[idx];   // "from" belongs to [1..m], m - number of movies in M matrix
        auto to = M.outerIndexPtr()[idx+1];   // "to"   belongs to [1..m], m - number of movies in M matrix
        MatrixNNd MM(MatrixNNd::Zero());               // matrix num_latent x num_latent

        // #pragma omp parallel for reduction(VectorPlus:rr) reduction(MatrixPlus:MM)
        #pragma omp parallel for reduction(VectorPlus:rr) reduction(MatrixPlus:MM) schedule(dynamic,200)
        for(int j = from; j<to; ++j) {                 // for each nonzeros elemen in the i-th row of M matrix
            auto val = M.valuePtr()[j];                // value of the j-th nonzeros element from idx-th row of M matrix
            auto idx = M.innerIndexPtr()[j];           // index "j" of the element [i,j] from M matrix in compressed M matrix
            auto col = in.col(idx);                    // vector num_latent x 1 from V matrix: M[i,j] = U[i,:] x V[idx,:]

            //MM.noalias() += col * col.transpose();     // outer product
            calc_upper_part(MM, col);
            rr.noalias() += col * ((val - mean_rating) * alpha); // vector num_latent x 1
        }

        copy_lower_part(MM);

        chol.compute(hp_LambdaF + alpha * MM);         // matrix num_latent x num_latent
                                                       // chol="lambda_i with *" from formula (14)
                                                       // lambda_i with * = LambdaU + alpha * MM
    }

    if(chol.info() != Eigen::Success) THROWERROR("Cholesky failed");

    // now we should calculate formula (14) from the paper
    // u_i for k-th iteration = Gaussian distribution N(u_i | mu_i with *, [lambda_i with *]^-1) =
    //                        = mu_i with * + s * [U]^-1,
    //                        where
    //                              s is a random vector with N(0, I),
    //                              mu_i with * is a vector num_latent x 1,
    //                              mu_i with * = [lambda_i with *]^-1 * rr,
    //                              lambda_i with * = L * U

    // Expression u_i = U \ (s + (L \ rr)) in Matlab looks for Eigen library like:

    chol.matrixL().solveInPlace(rr);                    // L*Y=rr => Y=L\rr, we store Y result again in rr vector
    rr += nrandn();                                     // rr=s+(L\rr), we store result again in rr vector
    chol.matrixU().solveInPlace(rr);                    // u_i=U\rr
    items().col(idx) = rr;                              // we save rr vector in items matrix (it is user features matrix)

    auto stop = tick();
    register_time(idx, 1e6 * (stop - start));
    //Sys::cout() << "  " << count << ": " << 1e6*(stop - start) << std::endl;

    assert(rr.norm() > .0);

    return rr;
}

//
// update ALL movies / users in parallel
//
void Sys::sample(Sys &in)
{
    iter++;
    VectorNd  sum(VectorNd::Zero()); // sum
    double    norm(0.0); // squared norm
    MatrixNNd prod(MatrixNNd::Zero()); // outer prod

//#pragma omp parallel for reduction(VectorPlus:sum) reduction(MatrixPlus:prod) reduction(+:norm) schedule(dynamic, 1)
#pragma omp parallel for reduction(VectorPlus:sum) reduction(MatrixPlus:prod) reduction(+:norm) schedule(dynamic,1)
    for(int i = from(); i<to(); ++i) {
        auto r = sample(i,in.items());

        MatrixNNd cov = (r * r.transpose());
        prod += cov;
        sum += r;
        norm += r.squaredNorm();

        if (iter >= burnin)
        {
            aggrMu.col(i) += r;
            aggrLambda.col(i) += Eigen::Map<Eigen::VectorXd>(cov.data(), num_latent * num_latent);
        }

        send_items(i, i + 1);
    }

    const int N = num();
    local_sum() = sum;
    local_cov() = (prod - (sum * sum.transpose() / N)) / (N-1);
    local_norm() = norm;

}

void Sys::register_time(int i, double t)
{
    if (measure_perf) sample_time.at(i) += t;
}

void calc_upper_part(MatrixNNd &m, VectorNd v)
{
  // we use the formula: m = m + v * v.transpose(), but we calculate only an upper part of m matrix
  for (int j=0; j<num_latent; j++)          // columns
  {
    for(int i=0; i<=j; i++)              // rows
    {
      m(i,j) = m(i,j) + v[j] * v[i];
    }
  }
}

void copy_lower_part(MatrixNNd &m)
{
  // Here, we copy a triangular upper part to a triangular lower part, because the matrix is symmetric.
  for (int j=1; j<num_latent; j++)          // columns
  {
    for(int i=0; i<=j-1; i++)            // rows
    {
      m(j,i) = m(i,j);
    }
  }
}
<|endoftext|>


<commit_before>#include <iostream>
#include <string>
#include <algorithm>
#include <numeric>
#include <cstdlib>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/strong_components.hpp>
#include <boost/graph/graphviz.hpp>
#include <boost/program_options.hpp>
#include "float-semiring.h"
#include "semilinSetExp.h"
#include "matrix.h"
#include "polynomial.h"
#include "newton.h"
#include "commutativeRExp.h"
#include "parser.h"

template <typename SR>
struct VertexProp {
	std::string name;   // used for graphviz output
	VarPtr var;         // var and rex combines the equations in the vertex
	Polynomial<SR> rex;
};

// group the equations to SCCs
template <typename SR>
std::vector<std::vector<std::pair<VarPtr, Polynomial<SR>>>> group_by_scc(std::vector<std::pair<VarPtr, Polynomial<SR>>> equations, bool graphviz_output)
{
	// create map of variables to [0..n]. this is used to enumerate important variables in a clean way from 0 to n during graph construction
	std::map<VarPtr, int> var_key;

	// build the graph
	boost::adjacency_list<boost::vecS, boost::vecS, boost::bidirectionalS, VertexProp<SR>> graph(equations.size());
	for(auto e_it = equations.begin(); e_it != equations.end(); ++e_it)
	{
		// if the variable is not yet in the map insert it together with the size of
		// the map. this way we get a unique identifier for the variable counting from 0 to n
		if(var_key.find(e_it->first) == var_key.end())
			var_key.insert(var_key.begin(), std::pair<VarPtr,int>(e_it->first,var_key.size()));
		int a = var_key.find(e_it->first)->second; // variable key
		graph[a].var = e_it->first; // store VarPtr to the vertex
		graph[a].name = e_it->first->string(); // store the name to the vertex
		graph[a].rex = e_it->second; // store the regular expression to the vertex

		auto v = e_it->second.get_variables(); // all variables of this rule;
		for(auto v_it = v.begin(); v_it != v.end(); ++v_it)
		{
			if(var_key.find(*v_it) == var_key.end()) // variable is not yet in the map
				var_key.insert(var_key.begin(), std::pair<VarPtr,int>(*v_it,var_key.size()));
			int b = var_key.find(*v_it)->second; // variable key
			boost::add_edge(a, b, graph);
		}
	} // graph is complete

	if(graphviz_output)
	{
		// output the created graph to graph.dot
		boost::dynamic_properties dp;
		dp.property("label", boost::get(&VertexProp<SR>::name, graph)); // vertex name is the name of the equation
		dp.property("node_id", get(boost::vertex_index, graph)); // this is needed
		std::ofstream outf("graph.dot");
		boost::write_graphviz_dp(outf, graph, dp);
	}

	// calculate strong connected components and store them in 'component'
	std::vector<int> component(boost::num_vertices(graph));
	boost::strong_components(graph,&component[0]);

	// group neccessary equations together
	int num_comp = *std::max_element(component.begin(), component.end()) + 1; // find the number of components
	std::vector<std::vector<std::pair<VarPtr,Polynomial<SR>>>> grouped_equations;
	grouped_equations.resize(num_comp);

	// iterate over all vertices (0 to n)
	// collect the necessary variables + equations for every component
	for (unsigned int j = 0; j != component.size(); ++j)
	{
		//std::cout << j << ", " << graph[j].var << " is in component " << component[j] << std::endl;
		grouped_equations[component[j]].push_back(std::pair<VarPtr,Polynomial<SR>>(graph[j].var, graph[j].rex));
	}

	return grouped_equations;
}

// apply the newton method to the given input
template <typename SR>
std::map<VarPtr, SR> apply_newton(std::vector<std::pair<VarPtr, Polynomial<SR>>> equations, bool scc, bool iteration_flag, int iterations, bool graphviz_output)
{
	// TODO: sanity checks on the input!

	// generate an instance of the newton solver
	Newton<SR> newton;

	// if we use the scc method, group the equations
	// the outer vector contains SCCs starting with a bottom SCC at 0
	std::vector<std::vector<std::pair<VarPtr,Polynomial<SR>>>> equations2;
	if(scc)
	{
		auto tmp = group_by_scc(equations, graphviz_output);
		equations2.insert(equations2.begin(), tmp.begin(), tmp.end());
	}
	else if (!scc)
	{
		equations2.push_back(equations);
	}

	// this holds the solution
	std::map<VarPtr, SR> solution;

	// the same loop is used for both the scc and the non-scc variant
	// in the non-scc variant, we just run once through the loop
	//for(auto it1 = equations2.begin(); it != equations2.end(); ++it)
	for(unsigned int j = 0; j != equations2.size(); ++j)
	{
		// use the solutions to get rid of variables in the remaining equations
		// does nothing in the first round
		std::vector<std::pair<VarPtr, Polynomial<SR>>> tmp1;
		for(auto it = equations2[j].begin(); it != equations2[j].end(); ++it)
		{ // it = (VarPtr, Polynomial[SR])
			auto tmp2 = it->second.partial_eval(solution);
			tmp1.push_back(std::pair<VarPtr, Polynomial<SR>>(it->first, tmp2));
		}
		// replace old equations with simplified ones
		equations2[j] = tmp1;

		// dynamic iterations
		if(!iteration_flag)
			// for commutative SRs newton has converged after n+1 iterations, so use this number as default
			iterations = equations2[j].size() + 1;

		// do some real work here
		std::map<VarPtr, SR> result = newton.solve_fixpoint(equations2[j], iterations);

		// copy the results into the solution map
		solution.insert(result.begin(), result.end());
	}

	return solution;
}

template <typename SR>
std::string result_string(std::map<VarPtr,SR> result)
{
	std::stringstream ss;
	for(auto r_it = result.begin(); r_it != result.end(); ++r_it)
		ss << r_it->first << " == " << r_it->second << std::endl;
	return ss.str();
}

int main(int argc, char* argv[])
{
	namespace po = boost::program_options;

	po::options_description desc("Allowed options");
	desc.add_options()
		( "scc", "apply newton method iteratively to strongly connected components of the equation graph" )
		( "help,h", "print this help message" )
		( "iterations,i", po::value<int>(), "specify the number of newton iterations. default is optimal number" )
		//( "verbose", "enable verbose output" )
		//( "debug", "enable debug output" )
		( "test", "just for testing purposes ... explicit test defined in main()" )
		( "file,f", po::value<std::string>(), "input file" )
		( "float", "float semiring" )
		( "rexp", "commutative regular expression semiring" )
		( "slset", "explicit semilinear sets semiring (as vectors)" )
		( "graphviz", "create the file graph.dot with the equation graph" )
		;

	po::variables_map vm;
	po::store(po::parse_command_line(argc, argv, desc), vm);
	po::notify(vm);

	if(vm.count("test")) {
		Newton<SemilinSetExp> newton;
		std::vector<VarPtr> variables;
		variables.push_back(Var::getVar("x"));
		std::cout << "- newton (cnt-SR):" << std::endl;

		std::vector<Polynomial<SemilinSetExp> > polynomials;
		Polynomial<SemilinSetExp> f1 = Polynomial<SemilinSetExp>({
		Monomial<SemilinSetExp>(SemilinSetExp(Var::getVar("a")), {Var::getVar("x"),Var::getVar("x")}),
		Monomial<SemilinSetExp>(SemilinSetExp(Var::getVar("c")), {}) });

		polynomials.push_back(f1);

		Matrix<SemilinSetExp> result = newton.solve_fixpoint(polynomials, variables, 2);
		std::cout << result << std::endl;

/*		auto s1 = CommutativeRExp(Var::getVar("a"));
		auto s2 = CommutativeRExp(Var::getVar("b"));
		auto m1 = Matrix<CommutativeRExp>(1,1,{s1});
		auto m2 = Matrix<CommutativeRExp>(1,1,{s2});
*/

		// this actually led to a strange bug with the ublas-matrix implementation!!
/*		auto s1 = SemilinSetExp(Var::getVar("a"));
		auto s2 = SemilinSetExp(Var::getVar("b"));
		auto m1 = Matrix<SemilinSetExp>(1,1,{s1});
		auto m2 = Matrix<SemilinSetExp>(1,1,{s2});

		auto m3 = m1*m2;
		std::cout << m3;
*/
		return 0;
	}

	if(vm.count("help"))
	{
		std::cout << desc << std::endl;
		return 0;
	}

	int iterations=0;
	if(vm.count("iterations"))
		iterations = vm["iterations"].as<int>();

	// check if we can do something useful
	if(!vm.count("float") && !vm.count("rexp") && !vm.count("slset")) // check for all compatible parameters
	{
		std::cout << "Please supply a supported semiring :)" << std::endl;
		return 0;
	}

	std::vector<std::string> input;
	std::string line;
	if(vm.count("file"))
	{
		// we are reading the input from the given file
		std::ifstream file;
		file.open(vm["file"].as<std::string>(), std::ifstream::in);
		if(file.fail())
		  std::cerr << "Could not open input file: " << vm["file"].as<std::string>() << std::endl;
		while(std::getline(file, line))
			input.push_back(line);
	}
	else
	{
		// we are reading from stdin
		while(std::getline(std::cin, line))
			input.push_back(line);
	}

	// join the input into one string
	std::string input_all = std::accumulate(input.begin(), input.end(), std::string(""));

	Parser p;

	if(vm.count("slset")) {
		std::vector<std::pair<VarPtr, Polynomial<SemilinSetExp>>> equations(p.slset_parser(input_all));
		if(equations.empty()) return -1;

		for(auto eq_it = equations.begin(); eq_it != equations.end(); ++eq_it)
		{
			std::cout << "* " << eq_it->first << " → " << eq_it->second << std::endl;
		}

		auto result = apply_newton<SemilinSetExp>(equations, vm.count("scc"), vm.count("iterations"), iterations, vm.count("graphviz"));
		std::cout << result_string(result) << std::endl;
	}
	else if(vm.count("rexp")) {
		// parse the input into a list of (Var → Polynomial[SR])
		std::vector<std::pair<VarPtr, Polynomial<CommutativeRExp>>> equations(p.rexp_parser(input_all));
		if(equations.empty()) return -1;

		for(auto eq_it = equations.begin(); eq_it != equations.end(); ++eq_it)
		{
			std::cout << "* " << eq_it->first << " → " << eq_it->second << std::endl;
		}

		// apply the newton method to the equations
		auto result = apply_newton<CommutativeRExp>(equations, vm.count("scc"), vm.count("iterations"), iterations, vm.count("graphviz"));
		std::cout << result_string(result) << std::endl;
	}
	else if(vm.count("float")) {
		std::vector<std::pair<VarPtr, Polynomial<FloatSemiring>>> equations(p.float_parser(input_all));
		if(equations.empty()) return -1;

		for(auto eq_it = equations.begin(); eq_it != equations.end(); ++eq_it)
		{
			std::cout << "* " << eq_it->first << " → " << eq_it->second << std::endl;
		}

		auto result = apply_newton<FloatSemiring>(equations, vm.count("scc"), vm.count("iterations"), iterations, vm.count("graphviz"));
		std::cout << result_string(result) << std::endl;
	}

	return 0;
}
<commit_msg>main: update for new {Mo,Poly}nomial implementation<commit_after>#include <iostream>
#include <string>
#include <algorithm>
#include <numeric>
#include <cstdlib>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/strong_components.hpp>
#include <boost/graph/graphviz.hpp>
#include <boost/program_options.hpp>
#include "float-semiring.h"
#include "semilinSetExp.h"
#include "matrix.h"
#include "polynomial.h"
#include "newton.h"
#include "commutativeRExp.h"
#include "parser.h"

template <typename SR>
struct VertexProp {
	std::string name;   // used for graphviz output
	VarPtr var;         // var and rex combines the equations in the vertex
	Polynomial<SR> rex;
};

// group the equations to SCCs
template <typename SR>
std::vector<std::vector<std::pair<VarPtr, Polynomial<SR>>>> group_by_scc(std::vector<std::pair<VarPtr, Polynomial<SR>>> equations, bool graphviz_output)
{
	// create map of variables to [0..n]. this is used to enumerate important variables in a clean way from 0 to n during graph construction
	std::map<VarPtr, int> var_key;

	// build the graph
	boost::adjacency_list<boost::vecS, boost::vecS, boost::bidirectionalS, VertexProp<SR>> graph(equations.size());
	for(auto e_it = equations.begin(); e_it != equations.end(); ++e_it)
	{
		// if the variable is not yet in the map insert it together with the size of
		// the map. this way we get a unique identifier for the variable counting from 0 to n
		if(var_key.find(e_it->first) == var_key.end())
			var_key.insert(var_key.begin(), std::pair<VarPtr,int>(e_it->first,var_key.size()));
		int a = var_key.find(e_it->first)->second; // variable key
		graph[a].var = e_it->first; // store VarPtr to the vertex
		graph[a].name = e_it->first->string(); // store the name to the vertex
		graph[a].rex = e_it->second; // store the regular expression to the vertex

		auto v = e_it->second.get_variables(); // all variables of this rule;
		for(auto v_it = v.begin(); v_it != v.end(); ++v_it)
		{
			if(var_key.find(*v_it) == var_key.end()) // variable is not yet in the map
				var_key.insert(var_key.begin(), std::pair<VarPtr,int>(*v_it,var_key.size()));
			int b = var_key.find(*v_it)->second; // variable key
			boost::add_edge(a, b, graph);
		}
	} // graph is complete

	if(graphviz_output)
	{
		// output the created graph to graph.dot
		boost::dynamic_properties dp;
		dp.property("label", boost::get(&VertexProp<SR>::name, graph)); // vertex name is the name of the equation
		dp.property("node_id", get(boost::vertex_index, graph)); // this is needed
		std::ofstream outf("graph.dot");
		boost::write_graphviz_dp(outf, graph, dp);
	}

	// calculate strong connected components and store them in 'component'
	std::vector<int> component(boost::num_vertices(graph));
	boost::strong_components(graph,&component[0]);

	// group neccessary equations together
	int num_comp = *std::max_element(component.begin(), component.end()) + 1; // find the number of components
	std::vector<std::vector<std::pair<VarPtr,Polynomial<SR>>>> grouped_equations;
	grouped_equations.resize(num_comp);

	// iterate over all vertices (0 to n)
	// collect the necessary variables + equations for every component
	for (unsigned int j = 0; j != component.size(); ++j)
	{
		//std::cout << j << ", " << graph[j].var << " is in component " << component[j] << std::endl;
		grouped_equations[component[j]].push_back(std::pair<VarPtr,Polynomial<SR>>(graph[j].var, graph[j].rex));
	}

	return grouped_equations;
}

// apply the newton method to the given input
template <typename SR>
std::map<VarPtr, SR> apply_newton(std::vector<std::pair<VarPtr, Polynomial<SR>>> equations, bool scc, bool iteration_flag, int iterations, bool graphviz_output)
{
	// TODO: sanity checks on the input!

	// generate an instance of the newton solver
	Newton<SR> newton;

	// if we use the scc method, group the equations
	// the outer vector contains SCCs starting with a bottom SCC at 0
	std::vector<std::vector<std::pair<VarPtr,Polynomial<SR>>>> equations2;
	if(scc)
	{
		auto tmp = group_by_scc(equations, graphviz_output);
		equations2.insert(equations2.begin(), tmp.begin(), tmp.end());
	}
	else if (!scc)
	{
		equations2.push_back(equations);
	}

	// this holds the solution
	std::map<VarPtr, SR> solution;

	// the same loop is used for both the scc and the non-scc variant
	// in the non-scc variant, we just run once through the loop
	//for(auto it1 = equations2.begin(); it != equations2.end(); ++it)
	for(unsigned int j = 0; j != equations2.size(); ++j)
	{
		// use the solutions to get rid of variables in the remaining equations
		// does nothing in the first round
		std::vector<std::pair<VarPtr, Polynomial<SR>>> tmp1;
		for(auto it = equations2[j].begin(); it != equations2[j].end(); ++it)
		{ // it = (VarPtr, Polynomial[SR])
			auto tmp2 = it->second.partial_eval(solution);
			tmp1.push_back(std::pair<VarPtr, Polynomial<SR>>(it->first, tmp2));
		}
		// replace old equations with simplified ones
		equations2[j] = tmp1;

		// dynamic iterations
		if(!iteration_flag)
			// for commutative SRs newton has converged after n+1 iterations, so use this number as default
			iterations = equations2[j].size() + 1;

		// do some real work here
		std::map<VarPtr, SR> result = newton.solve_fixpoint(equations2[j], iterations);

		// copy the results into the solution map
		solution.insert(result.begin(), result.end());
	}

	return solution;
}

template <typename SR>
std::string result_string(std::map<VarPtr,SR> result)
{
	std::stringstream ss;
	for(auto r_it = result.begin(); r_it != result.end(); ++r_it)
		ss << r_it->first << " == " << r_it->second << std::endl;
	return ss.str();
}

int main(int argc, char* argv[])
{
	namespace po = boost::program_options;

	po::options_description desc("Allowed options");
	desc.add_options()
		( "scc", "apply newton method iteratively to strongly connected components of the equation graph" )
		( "help,h", "print this help message" )
		( "iterations,i", po::value<int>(), "specify the number of newton iterations. default is optimal number" )
		//( "verbose", "enable verbose output" )
		//( "debug", "enable debug output" )
		( "test", "just for testing purposes ... explicit test defined in main()" )
		( "file,f", po::value<std::string>(), "input file" )
		( "float", "float semiring" )
		( "rexp", "commutative regular expression semiring" )
		( "slset", "explicit semilinear sets semiring (as vectors)" )
		( "graphviz", "create the file graph.dot with the equation graph" )
		;

	po::variables_map vm;
	po::store(po::parse_command_line(argc, argv, desc), vm);
	po::notify(vm);

	if(vm.count("test")) {
		Newton<SemilinSetExp> newton;
		std::vector<VarPtr> variables;
		variables.push_back(Var::getVar("x"));
		std::cout << "- newton (cnt-SR):" << std::endl;

		std::vector<Polynomial<SemilinSetExp> > polynomials;
		Polynomial<SemilinSetExp> f1 = Polynomial<SemilinSetExp>({
		  { SemilinSetExp(Var::getVar("a")), Monomial{ {Var::getVar("x"),Var::getVar("x")} } },
		  { SemilinSetExp(Var::getVar("c")), Monomial{} } });

		polynomials.push_back(f1);

		Matrix<SemilinSetExp> result = newton.solve_fixpoint(polynomials, variables, 2);
		std::cout << result << std::endl;

/*		auto s1 = CommutativeRExp(Var::getVar("a"));
		auto s2 = CommutativeRExp(Var::getVar("b"));
		auto m1 = Matrix<CommutativeRExp>(1,1,{s1});
		auto m2 = Matrix<CommutativeRExp>(1,1,{s2});
*/

		// this actually led to a strange bug with the ublas-matrix implementation!!
/*		auto s1 = SemilinSetExp(Var::getVar("a"));
		auto s2 = SemilinSetExp(Var::getVar("b"));
		auto m1 = Matrix<SemilinSetExp>(1,1,{s1});
		auto m2 = Matrix<SemilinSetExp>(1,1,{s2});

		auto m3 = m1*m2;
		std::cout << m3;
*/
		return 0;
	}

	if(vm.count("help"))
	{
		std::cout << desc << std::endl;
		return 0;
	}

	int iterations=0;
	if(vm.count("iterations"))
		iterations = vm["iterations"].as<int>();

	// check if we can do something useful
	if(!vm.count("float") && !vm.count("rexp") && !vm.count("slset")) // check for all compatible parameters
	{
		std::cout << "Please supply a supported semiring :)" << std::endl;
		return 0;
	}

	std::vector<std::string> input;
	std::string line;
	if(vm.count("file"))
	{
		// we are reading the input from the given file
		std::ifstream file;
		file.open(vm["file"].as<std::string>(), std::ifstream::in);
		if(file.fail())
		  std::cerr << "Could not open input file: " << vm["file"].as<std::string>() << std::endl;
		while(std::getline(file, line))
			input.push_back(line);
	}
	else
	{
		// we are reading from stdin
		while(std::getline(std::cin, line))
			input.push_back(line);
	}

	// join the input into one string
	std::string input_all = std::accumulate(input.begin(), input.end(), std::string(""));

	Parser p;

	if(vm.count("slset")) {
		std::vector<std::pair<VarPtr, Polynomial<SemilinSetExp>>> equations(p.slset_parser(input_all));
		if(equations.empty()) return -1;

		for(auto eq_it = equations.begin(); eq_it != equations.end(); ++eq_it)
		{
			std::cout << "* " << eq_it->first << " → " << eq_it->second << std::endl;
		}

		auto result = apply_newton<SemilinSetExp>(equations, vm.count("scc"), vm.count("iterations"), iterations, vm.count("graphviz"));
		std::cout << result_string(result) << std::endl;
	}
	else if(vm.count("rexp")) {
		// parse the input into a list of (Var → Polynomial[SR])
		std::vector<std::pair<VarPtr, Polynomial<CommutativeRExp>>> equations(p.rexp_parser(input_all));
		if(equations.empty()) return -1;

		for(auto eq_it = equations.begin(); eq_it != equations.end(); ++eq_it)
		{
			std::cout << "* " << eq_it->first << " → " << eq_it->second << std::endl;
		}

		// apply the newton method to the equations
		auto result = apply_newton<CommutativeRExp>(equations, vm.count("scc"), vm.count("iterations"), iterations, vm.count("graphviz"));
		std::cout << result_string(result) << std::endl;
	}
	else if(vm.count("float")) {
		std::vector<std::pair<VarPtr, Polynomial<FloatSemiring>>> equations(p.float_parser(input_all));
		if(equations.empty()) return -1;

		for(auto eq_it = equations.begin(); eq_it != equations.end(); ++eq_it)
		{
			std::cout << "* " << eq_it->first << " → " << eq_it->second << std::endl;
		}

		auto result = apply_newton<FloatSemiring>(equations, vm.count("scc"), vm.count("iterations"), iterations, vm.count("graphviz"));
		std::cout << result_string(result) << std::endl;
	}

	return 0;
}
<|endoftext|>