From 47aa0e77fbb9692c585d889cbf844a0a2d2e7446 Mon Sep 17 00:00:00 2001 From: mtzikara Date: Sun, 30 Sep 2018 23:57:12 +0200 Subject: [PATCH] Add files via upload --- openmp/Makefile | 37 ++ openmp/coo_sparse_matrix.c | 125 +++++++ openmp/coo_sparse_matrix.h | 33 ++ openmp/coo_sparse_matrix.o | Bin 0 -> 4096 bytes openmp/csr_sparse_matrix.c | 93 +++++ openmp/csr_sparse_matrix.h | 24 ++ openmp/csr_sparse_matrix.o | Bin 0 -> 3192 bytes openmp/pagerank.out | Bin 0 -> 28459 bytes openmp/serial_gs_pagerank.c | 44 +++ openmp/serial_gs_pagerank.o | Bin 0 -> 3456 bytes openmp/serial_gs_pagerank_functions.c | 512 ++++++++++++++++++++++++++ openmp/serial_gs_pagerank_functions.h | 98 +++++ openmp/serial_gs_pagerank_functions.o | Bin 0 -> 18752 bytes 13 files changed, 966 insertions(+) create mode 100644 openmp/Makefile create mode 100644 openmp/coo_sparse_matrix.c create mode 100644 openmp/coo_sparse_matrix.h create mode 100644 openmp/coo_sparse_matrix.o create mode 100644 openmp/csr_sparse_matrix.c create mode 100644 openmp/csr_sparse_matrix.h create mode 100644 openmp/csr_sparse_matrix.o create mode 100644 openmp/pagerank.out create mode 100644 openmp/serial_gs_pagerank.c create mode 100644 openmp/serial_gs_pagerank.o create mode 100644 openmp/serial_gs_pagerank_functions.c create mode 100644 openmp/serial_gs_pagerank_functions.h create mode 100644 openmp/serial_gs_pagerank_functions.o diff --git a/openmp/Makefile b/openmp/Makefile new file mode 100644 index 0000000..2e297ea --- /dev/null +++ b/openmp/Makefile @@ -0,0 +1,37 @@ +SHELL := /bin/bash + +# ============================================ +# COMMANDS + +CC = gcc -std=gnu99 -fopenmp +RM = rm -f +CFLAGS_DEBUG=-O0 -ggdb3 -Wall -I. +CFLAGS=-O3 -Wall -I. +OBJ=serial_gs_pagerank.o serial_gs_pagerank_functions.o coo_sparse_matrix.o csr_sparse_matrix.o +DEPS=serial_gs_pagerank_functions.h coo_sparse_matrix.h csr_sparse_matrix.h + +# ========================================== +# TARGETS + +EXECUTABLES = pagerank.out + +.PHONY: all clean + +all: $(EXECUTABLES) + +# ========================================== +# DEPENDENCIES (HEADERS) + +%.o: %.c $(DEPS) + $(CC) -c -o $@ $< $(CFLAGS) + +.PRECIOUS: $(EXECUTABLES) $(OBJ) + +# ========================================== +# EXECUTABLE (MAIN) + +$(EXECUTABLES): $(OBJ) + $(CC) -o $@ $^ $(CFLAGS) + +clean: + $(RM) *.o *~ $(EXECUTABLES) diff --git a/openmp/coo_sparse_matrix.c b/openmp/coo_sparse_matrix.c new file mode 100644 index 0000000..93d0ac9 --- /dev/null +++ b/openmp/coo_sparse_matrix.c @@ -0,0 +1,125 @@ +#include "coo_sparse_matrix.h" + +CooSparseMatrix initCooSparseMatrix() { + CooSparseMatrix sparseMatrix; + sparseMatrix.size = 0; + sparseMatrix.numberOfNonZeroElements = 0; + sparseMatrix.elements = NULL; + return sparseMatrix; +} + +void allocMemoryForCoo(CooSparseMatrix *sparseMatrix, int numberOfElements) { + sparseMatrix->elements = (CooSparseMatrixElement **) malloc( + numberOfElements * sizeof(CooSparseMatrixElement *)); + sparseMatrix->size = numberOfElements; +} + +void addElement(CooSparseMatrix *sparseMatrix, double value, int row, int column) { + if (sparseMatrix->numberOfNonZeroElements == sparseMatrix->size) { + printf("%d == %d |||| %d, %d\n", sparseMatrix->numberOfNonZeroElements, + sparseMatrix->size, row, column); + printf("Number of non zero elements exceeded size of matrix!\n"); + exit(EXIT_FAILURE); + } + + // Creates the new element + CooSparseMatrixElement *newElement = (CooSparseMatrixElement *) malloc( + sizeof(CooSparseMatrixElement)); + newElement->value = value; + newElement->rowIndex = row; + newElement->columnIndex = column; + + sparseMatrix->elements[sparseMatrix->numberOfNonZeroElements] = newElement; + sparseMatrix->numberOfNonZeroElements = sparseMatrix->numberOfNonZeroElements + 1; +} + +void transposeSparseMatrix(CooSparseMatrix *sparseMatrix) { + for (int i=0; inumberOfNonZeroElements; ++i) { + CooSparseMatrixElement *element = sparseMatrix->elements[i]; + int tempRow = element->rowIndex; + element->rowIndex = element->columnIndex; + element->columnIndex = tempRow; + } +} + +void transformToCSR(CooSparseMatrix initialSparseMatrix, + CsrSparseMatrix *transformedSparseMatrix) { + // Taken from here: https://github.com/scipy/scipy/blob/3b36a57/scipy/sparse/sparsetools/coo.h#L34 + if (initialSparseMatrix.numberOfNonZeroElements > transformedSparseMatrix->size) { + printf("Transformed CSR matrix does not have enough space!\n"); + exit(EXIT_FAILURE); + } + + for (int i=0; irowIndex; + transformedSparseMatrix->rowCumulativeIndexes[rowIndex] = + transformedSparseMatrix->rowCumulativeIndexes[rowIndex] + 1; + } + + // Cumulative sums the non zero elements per row + for (int i=0, sum=0; isize+1; ++i){ + int temp = transformedSparseMatrix->rowCumulativeIndexes[i]; + transformedSparseMatrix->rowCumulativeIndexes[i] = sum; + sum += temp; + } + + for (int i=0; irowIndex; + int destinationIndex = transformedSparseMatrix->rowCumulativeIndexes[row]; + + transformedSparseMatrix->columnIndexes[destinationIndex] = initialSparseMatrix.elements[i]->columnIndex; + transformedSparseMatrix->values[destinationIndex] = initialSparseMatrix.elements[i]->value; + + transformedSparseMatrix->rowCumulativeIndexes[row]++; + } + + for (int i=0, last=0; i<=transformedSparseMatrix->size; i++){ + int temp = transformedSparseMatrix->rowCumulativeIndexes[i]; + transformedSparseMatrix->rowCumulativeIndexes[i] = last; + last = temp; + } + + transformedSparseMatrix->numberOfNonZeroElements = initialSparseMatrix.numberOfNonZeroElements; +} + +void cooSparseMatrixVectorMultiplication(CooSparseMatrix sparseMatrix, + double *vector, double **product, int vectorSize) { + // Initializes the elements of the product vector to zero + for (int i=0; irowIndex, column = element->columnIndex; + + if (row >= vectorSize) { + printf("Error at sparseMatrixVectorMultiplication. Matrix has more rows than vector!\n"); + printf("row = %d\n", row); + exit(EXIT_FAILURE); + } + + (*product)[row] = (*product)[row] + element->value * vector[column]; + } +} + +void destroyCooSparseMatrix(CooSparseMatrix *sparseMatrix) { + for (int i=0; inumberOfNonZeroElements; ++i) { + free(sparseMatrix->elements[i]); + } + free(sparseMatrix->elements); +} + +void printCooSparseMatrix(CooSparseMatrix sparseMatrix) { + if (sparseMatrix.numberOfNonZeroElements == 0) { + return; + } + + CooSparseMatrixElement *element; + for (int i=0; irowIndex, element->columnIndex, + element->value); + } +} \ No newline at end of file diff --git a/openmp/coo_sparse_matrix.h b/openmp/coo_sparse_matrix.h new file mode 100644 index 0000000..dd4c31d --- /dev/null +++ b/openmp/coo_sparse_matrix.h @@ -0,0 +1,33 @@ +#ifndef COO_SPARSE_MATRIX_H /* Include guard */ +#define COO_SPARSE_MATRIX_H + +#include +#include +#include +#include + +#include "csr_sparse_matrix.h" + +typedef struct cooSparseMatrixElement { + double value; + int rowIndex, columnIndex; +} CooSparseMatrixElement; + +typedef struct cooSparseMatrix { + int size, numberOfNonZeroElements; + CooSparseMatrixElement **elements; +} CooSparseMatrix; + +CooSparseMatrix initCooSparseMatrix(); +void allocMemoryForCoo(CooSparseMatrix *sparseMatrix, int numberOfElements); +void addElement(CooSparseMatrix *sparseMatrix, double value, int row, + int column); +void transposeSparseMatrix(CooSparseMatrix *sparseMatrix); +void transformToCSR(CooSparseMatrix initialSparseMatrix, + CsrSparseMatrix *transformedSparseMatrix); +void cooSparseMatrixVectorMultiplication(CooSparseMatrix sparseMatrix, + double *vector, double **product, int vectorSize); +void destroyCooSparseMatrix(CooSparseMatrix *sparseMatrix); +void printCooSparseMatrix(CooSparseMatrix sparseMatrix); + +#endif // COO_SPARSE_MATRIX_H \ No newline at end of file diff --git a/openmp/coo_sparse_matrix.o b/openmp/coo_sparse_matrix.o new file mode 100644 index 0000000000000000000000000000000000000000..8b80d4e77349e7a99abd331292352e4fbfe21943 GIT binary patch literal 4096 zcmbtWUu+yl8K3iQlI``_v#SCp6)8h;E0?xgU+V`SCDpBc7tcv{TE)JSCN}PQZ?8V$ zyES|3)UjF{>2P4RHsk>$o_Og4Jb?OwPbyht#0rXwIQ8K*W8e@j{w-UE& zeUSET<!_o_1gyg#HAme&?3^Ow}jrPIt;e#crb z$BgWz&s2Xl>9M3Yt1i9s;YMnIo)BX!?Ga-%D2Jc%S-QpZDIMs{M`(w1=z|9r}I86inP{aKvoK|SAbz; zFFgP5^7i7vVSh?x{?|G)Ys^2IhLj=xu&;2xMU$xo|44GnNOh9pYI0@RyzQVjzDl+R zQeDcctr!n@Q3UM9fSK!=3-2dHetGYF-p9CJFGkKLp18NuN1vEj3u_Ut9M?0|Ffz6IoP+otaTm^euwu(IDTB=6_&lcHJExzd2K6o1Ztuy zHg_o~Ff0J$tHxwBbMN=O7GxRh9)7qUA^DF>nQNm^2bn)W?so_KT=b#jS{pIuf4PUX z&IKiDWXHRerMHhw6#eJARb%`ro-|bVP5!VricsF(jZtv8z4#D6x1z7Sxm%eGJo_x^ zKcV8{UfRhl`_|HBeg^kc=z#+ip3i}RegIm6IQFv?(F8dL&^?t2990vT#i6UJ&5}~VSB&O z4}ku$=>G#X&{sN`mOh34T1v;o1dlCT5C)Vhf`3-$Fp5c*)VAX^m_X)ur+Trp% zIroIj66f^h$+}yf0_{41o|Zkwnknn9HSZF=Qg+KkpK3OV?$|g$dPdLOJnSdBY1gqx zi4MhP=|hJ~4>sMhc^r7?%$lC8I_0{Bf&V|DV2(=+{nog{9Dfq>Ok8w?!a54eT`*9> zfapK{si5;mv42SzVi)%nB^@YoFKEOgVaSi7fZz*vVnhhm68enN|5y7A*rLyep@l`- z>tPW3GQQL~fko}Fh=hI_v6t(Y_KypD`5sF8_r<2-`obdZq{jDB_QSmOc8Ai2TNhYI z*cke+2>bbnbD6)SPeZ3&s9Y*wtO+8X|83Buy*&Rez@le|COJ`%&*C5?k#zXlqq!r! zpMU+qkC?=AL?{{`b7~iV9uo%nbD(yf(fB(Pkq+_{^4IMM%!l!|Cx&^n%LMUuJB+ye zlHVL9e#_e&%*S_DALZjN+$wRh?-uaWfZP$vUuJNhz~%R$TUb5{T4Dm?Y5<<-CnrGu zUxEGzw08WXpMXz(0$u?e{cnOsk^i0r==1!=LGu3(V4lQ9y_|(H%1@!S%jai+bN_;m z>p^)HT01`GfqW{CnEy}Ez_jhj;I1$kTt9S^)EYH+(6&cIR|ry`nX%0g3vLQ0584jQ zk~)V-xl$%*(lYa0#Q1BfO|I2>C(ZD}`k|cj4gyVTh{B2Pe(oetOT@^US4g2{pgoph+f;)nNPErf^j`6z^=51G#aQID7p`lrxP zjtU&j!v3`o9`-}g2n6{e6d(8T=Pr&pp^OJ=ho2MUA4Kp?G5$#ehgXTqn^9Z@?+Kj? NLYo2~i{SEo{157s#?$}+ literal 0 HcmV?d00001 diff --git a/openmp/csr_sparse_matrix.c b/openmp/csr_sparse_matrix.c new file mode 100644 index 0000000..bdf8413 --- /dev/null +++ b/openmp/csr_sparse_matrix.c @@ -0,0 +1,93 @@ +#include "csr_sparse_matrix.h" + +CsrSparseMatrix initCsrSparseMatrix() { + CsrSparseMatrix sparseMatrix; + sparseMatrix.size = 0; + sparseMatrix.numberOfNonZeroElements = 0; + + sparseMatrix.values = NULL; + sparseMatrix.columnIndexes = NULL; + sparseMatrix.rowCumulativeIndexes = NULL; + return sparseMatrix; +} + +void allocMemoryForCsr(CsrSparseMatrix *sparseMatrix, int numberOfElements) { + sparseMatrix->values = (double *) malloc(numberOfElements * sizeof(double)); + sparseMatrix->columnIndexes = (int *) malloc( + numberOfElements * sizeof(int)); + sparseMatrix->rowCumulativeIndexes = (int *) malloc( + (numberOfElements + 1) * sizeof(int)); + + for (int i=0; irowCumulativeIndexes[i] = 0; + } + sparseMatrix->size = numberOfElements; +} + +// Row indexes start from 0! +void zeroOutRow(CsrSparseMatrix *sparseMatrix, int row) { + int startIndex = sparseMatrix->rowCumulativeIndexes[row], + endIndex = sparseMatrix->rowCumulativeIndexes[row+1]; + for (int i=startIndex; ivalues[i] = 0; + } +} + +void zeroOutColumn(CsrSparseMatrix *sparseMatrix, int column) { + for (int i=0; inumberOfNonZeroElements; ++i){ + if(sparseMatrix->columnIndexes[i] == column){ + // Zeros out this element + sparseMatrix->values[i] = 0; + } + } +} + +void csrSparseMatrixVectorMultiplication(CsrSparseMatrix sparseMatrix, + double *vector, double **product, int vectorSize) { + // Initializes the elements of the product vector to zero + for (int i=0; ivalues); + free(sparseMatrix->rowCumulativeIndexes); + free(sparseMatrix->columnIndexes); +} + +void printCsrSparseMatrix(CsrSparseMatrix sparseMatrix) { + if (sparseMatrix.size == 0) { + return; + } + + for (int i=0; i +#include +#include +#include + +typedef struct csrSparseMatrix { + int size, numberOfNonZeroElements; + int *rowCumulativeIndexes, *columnIndexes; + double *values; +} CsrSparseMatrix; + +CsrSparseMatrix initCsrSparseMatrix(); +void allocMemoryForCsr(CsrSparseMatrix *sparseMatrix, int numberOfElements); +void zeroOutRow(CsrSparseMatrix *sparseMatrix, int row); +void zeroOutColumn(CsrSparseMatrix *sparseMatrix, int column); +void csrSparseMatrixVectorMultiplication(CsrSparseMatrix sparseMatrix, + double *vector, double **product, int vectorSize); +void destroyCsrSparseMatrix(CsrSparseMatrix *sparseMatrix); +void printCsrSparseMatrix(CsrSparseMatrix sparseMatrix); + +#endif // CSR_SPARSE_MATRIX_H \ No newline at end of file diff --git a/openmp/csr_sparse_matrix.o b/openmp/csr_sparse_matrix.o new file mode 100644 index 0000000000000000000000000000000000000000..c5872b439d8077c33563caffdd2443694f24642e GIT binary patch literal 3192 zcmbtWUu;ul6#v>@SQ+!SQ%q{ma388}DmN?aVFSe7tz);O69Ow^G8o<6ZPw5~OM9yu z!Y0;Dn@eFYzUhPd!W#)Okr#A-kS>~#IehV7f&>HwjE+D^B*AmO`+cL_Z7+I~@1Aph z=kNEo9cc}3b2=P^lY?v}k2H=Fa#?sJH&L;PJV7dnTDV`TK)uvPJ=9X08&%+5#(l`A zJ?q{b!wMxNu!9wD_nTDjx9*pn0h_dK*OdV0AI# z2;5T(C3W(O6;gP4-+rZgI_6pT9%dk_+38lbd#Wa!F+0u(jk8CeQ_UT|+9?Q|yy8-e zTPA-;Kkk2LI))^3-Ru-b=Z0T`7;j+W=BjZuKjCPc8h)m+{#fo#*lhEKO2PytunZ&D ztpp44N+eXct8_$gero=fdvq>n_WA2nGvRL-u2#*hsy1h}Lg&>wdUbdx=IQ(?Y7wa#Ei(5{rj_=fB z;-cHLj30Y$?;yApEI0n6r zwN(}FacCMZ#k0&-%Xkg&Iwp4_Zx+4Bok7tz=4uh8yx`m}`V`UIEV`}uZsxmzam+cu z z@x1d5EKlHwk)II*I*T8OI_Yc{eJ5PaqEr-G#hT+Q6tQk>r6M-uD}v%q=SN}#08kJd zz|ks0$65wI|P0ht@roDGSQ@t9sVzpAR5owN?VpDrk`>?Zos`Lr?sHq{j}2zoGBzX zCI?y#14Ue8B>HFqe*p&KE*Rx>!&aBuSURqfS3ts?|1~}Uv6z#eH4rKB`mb?-ao#`g zqX}@7`T1sUH=Y@{hP%N z13G0fx_=nU<&p0{(@%z&OGUBroC4|4%y$tuV4WMr} zU}M~$;S%UbAH!IxpHBhD{C_cj`bGox#WMax%Vv7A1JO)Y?@30DOngw*NIVrcTC$nl z6mF*|i6#jqPp3$q4$pde$X+v+(RDJAiKiBI)6+xYSdZ3!2xbQV1?ZO$yeoJ$A3U@u zvJ9>*gC8lw@lNqPo7s9|{&M_>G92$Z_rJ0Xp27q`c>T&x_c4YedkO~6^Et>!_PV#0 u$uCiF%b%sBNWBfOWBNfG&d;l1!})prz=rel%HI*JiDQ=aciHC8&*i@xdkho+ literal 0 HcmV?d00001 diff --git a/openmp/pagerank.out b/openmp/pagerank.out new file mode 100644 index 0000000000000000000000000000000000000000..37ff79d49f942d0c283a294b8594d6e676621673 GIT binary patch literal 28459 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 literal 0 HcmV?d00001 diff --git a/openmp/serial_gs_pagerank.c b/openmp/serial_gs_pagerank.c new file mode 100644 index 0000000..3836cf8 --- /dev/null +++ b/openmp/serial_gs_pagerank.c @@ -0,0 +1,44 @@ +#include +#include +#include "serial_gs_pagerank_functions.h" +//#include "coo_sparse_matrix.h" + +struct timeval startwtime, endwtime; +double seq_time; + +int main(int argc, char **argv) { + CsrSparseMatrix transitionMatrix = initCsrSparseMatrix(); + double *pagerankVector; + bool convergenceStatus; + Parameters parameters; + omp_set_dynamic(0); + parseArguments(argc, argv, ¶meters); + + initialize(&transitionMatrix, &pagerankVector, ¶meters); + + // Starts wall-clock timer + gettimeofday (&startwtime, NULL); + + int iterations = pagerank(&transitionMatrix, &pagerankVector, + &convergenceStatus, parameters); + if (parameters.verbose) { + printf(ANSI_COLOR_YELLOW "\n----- RESULTS -----\n" ANSI_COLOR_RESET); + if (convergenceStatus) { + printf(ANSI_COLOR_GREEN "Pagerank converged after %d iterations!\n" \ + ANSI_COLOR_RESET, iterations); + } else { + printf(ANSI_COLOR_RED "Pagerank did not converge after max number of" \ + " iterations (%d) was reached!\n" ANSI_COLOR_RESET, iterations); + } + } + + // Stops wall-clock timer + gettimeofday (&endwtime, NULL); + double seq_time = (double)((endwtime.tv_usec - startwtime.tv_usec)/1.0e6 + + endwtime.tv_sec - startwtime.tv_sec); + printf("%s wall clock time = %f\n","Pagerank (Gauss-Seidel method), serial implementation", + seq_time); + + free(pagerankVector); + destroyCsrSparseMatrix(&transitionMatrix); +} diff --git a/openmp/serial_gs_pagerank.o b/openmp/serial_gs_pagerank.o new file mode 100644 index 0000000000000000000000000000000000000000..366656bad738e1327246669f577a5f563a5691df GIT binary patch literal 3456 zcmbtWQEMDk6uz5{Hb%R&r9rZUKoJD-$)BL$LlLnlRS=&FzV*fWB8nmy&=(8woICgK-kr_7=z*Pc&Ue0Z z?zwmGy)#$N&YbH?B#4j%IYU~WKnb~*Y}q+x=17JdCH;%z<96=|igNyJVSZ+=pfInu ze{s|%7$37|jb-W?&WciUolDePrlw+)1L`RQrec9M0?T#$lRSl0hdiy7XIiG>xIr_Y zr`yJ=;?(UWsN|NVO3L7XIiy@Re8r1Hc~d86+8BL!-iw5r%NsJQ0Pu;NqC zGOCJY*Q(T}PQY3t13px%R+UoKEv+blWmDxPWuV+k$h@|vH~uO#{?V`gegit!cQ>-L zzu*+K1E%!dxok~u{L%PPa(9EQE;0YUi3wqRXBy~`lh>Ohz0sZ30bkV{)7c(g$>``o5li?kMnxzViT6&olKU+F#@2 zChiSn?cHEj`XVFjrx$KDbNAD4-{N!G$m*fhTowK5H@HmN-r|MZjoYlIw#TZ5ZEa-p zq;a=?qBWYf$0EPZ9UL5VqGtfb#!xo5)Jc+`ntDMQoL{OtL0y?pPpcCnV|C_f?`sq4 z=!7~x6yhxBG4>=Rv3{N;Rx^qIM~?Jdhp7z%{uGaG;Z!G6nGd?orTVTVUrF6bBu^!J zQ+-pZ%w(!(@?qqu{+b;aOP~WU)D>X`bTuHVX#@sQmwcb7o{J7LHT876tAR_ahKe~r zFub5%YcfiH0Q8b;+1TIq17w#%#FcDF;!%dkyH$U;{bOo75NME`vuOfG=Np^kH3sPA~5 z;0s*%Lcaq6%pVm5uJ7=CTwhtlyoL55C~TQ`pXbXM+EMN{$2#bIL+FtLqHh6T1LLsu ztFUX15V20Uez>F}U*N|8i|##uh|a+e(UBzDKl&ZL19tC7m{jAzG`QGObo_?^J8b+} zc=w>Nt2s2;GdjK$YBQ17P5x}&opLbQ@h^nYZuM)nWH z7(!by{7W(Tm6$r)G5n8W@K0j!&v|{ZqC%7K1FE>}7i;{hPc0F3W@?<+hUE~;v4Sbz zE7T0nr>`4aj;}eNrNB3twN43pAzsGsFS1Mn)Na`{ z)*vJNz$DZ$!*H=!^DHMQ7fY2DQuZh%CWZH{yA~bXr|%Y7L6{IoxKF|}i+93(_`X1S znngn#&#J)1{fEaG|0{v#z(zqF?+}6K5d^`VE8od83=R2@f-U%8NH~702>e@)W1Yvj zjp}=D7%E5{`WeT)Z!FTs*~}ao%rSBQC$|y7+~J{ott-kGV3!tuWl_%jmzgoF=EINsTU|1!r#pAcg5n#7muzZKyxa6XOUyAi&^`R~Q>MI6_G IU;QlOUp7|u&;S4c literal 0 HcmV?d00001 diff --git a/openmp/serial_gs_pagerank_functions.c b/openmp/serial_gs_pagerank_functions.c new file mode 100644 index 0000000..cb406c1 --- /dev/null +++ b/openmp/serial_gs_pagerank_functions.c @@ -0,0 +1,512 @@ +/* ===== INCLUDES ===== */ + +#include "serial_gs_pagerank_functions.h" +#include +/* ===== CONSTANTS ===== */ + +const char *ARGUMENT_CONVERGENCE_TOLERANCE = "-c"; +const char *ARGUMENT_MAX_ITERATIONS = "-m"; +const char *ARGUMENT_DAMPING_FACTOR = "-a"; +const char *ARGUMENT_VERBAL_OUTPUT = "-v"; +const char *ARGUMENT_OUTPUT_HISTORY = "-h"; +const char *ARGUMENT_OUTPUT_FILENAME = "-o"; + +const int NUMERICAL_BASE = 10; +char *DEFAULT_OUTPUT_FILENAME = "pagerank_output"; +const int FILE_READ_BUFFER_SIZE = 4096; + +const int CONVERGENCE_CHECK_ITERATION_PERIOD = 3; +const int SPARSITY_INCREASE_ITERATION_PERIOD = 3; + +/* ===== FUNCTIONS ===== */ + +int pagerank(CsrSparseMatrix *transitionMatrix, double **pagerankVector, + bool *convergenceStatus, Parameters parameters) { + // Variables declaration + int iterations = 0, numberOfPages = parameters.numberOfPages; + double delta, *pagerankDifference, *previousPagerankVector, + *convergedPagerankVector, *linksFromConvergedPagesPagerankVector; + CooSparseMatrix linksFromConvergedPages = initCooSparseMatrix(); + bool *convergenceMatrix; + + int P = omp_get_max_threads(); + omp_set_num_threads(P); + + // Space allocation + { + size_t sizeofDouble = sizeof(double); + // pagerankDifference used to calculate delta + pagerankDifference = (double *) malloc(numberOfPages * sizeofDouble); + // previousPagerankVector holds last iteration's pagerank vector + previousPagerankVector = (double *) malloc(numberOfPages * sizeofDouble); + // convergedPagerankVector is the pagerank vector of converged pages only + convergedPagerankVector = (double *) malloc(numberOfPages * sizeofDouble); + // linksFromConvergedPagesPagerankVector holds the partial sum of the + // pagerank vector, that describes effect of the links from converged + // pages to non converged pages + linksFromConvergedPagesPagerankVector = (double *) malloc(numberOfPages * sizeofDouble); + // convergenceMatrix indicates which pages have converged + convergenceMatrix = (bool *) malloc(numberOfPages * sizeof(bool)); + *convergenceStatus = false; + + // Initialization + allocMemoryForCoo(&linksFromConvergedPages, transitionMatrix->numberOfNonZeroElements); + #pragma omp parallel for num_threads(P) + for (int i=0; irowCumulativeIndexes[i], + rowEndIndex = transitionMatrix->rowCumulativeIndexes[i+1]; + if (rowEndIndex > rowStartIndex) { + // This row (page) has non zero elements (out-links) + for (int j=rowStartIndex; jcolumnIndexes[j]; + if (convergenceMatrix[pageLinksTo] == false){ + // Link exists, adds element to the vector + addElement(&linksFromConvergedPages, + transitionMatrix->values[j], i, pageLinksTo); + } + } + } + + // Increases sparsity of the transition matrix by + // deleting elements that correspond to converged pages + zeroOutRow(transitionMatrix, i); + zeroOutColumn(transitionMatrix, i); + + // Builds the new linksFromConvergedPagesPagerankVector + cooSparseMatrixVectorMultiplication(linksFromConvergedPages, + *pagerankVector, &linksFromConvergedPagesPagerankVector, + numberOfPages); + } + } + free(newlyConvergedPages); + } + + ++iterations; + // Outputs information about this iteration + if (iterations%2) { + printf(ANSI_COLOR_BLUE "Iteration %d: delta = %f\n" ANSI_COLOR_RESET, iterations, delta); + } else { + printf(ANSI_COLOR_CYAN "Iteration %d: delta = %f\n" ANSI_COLOR_RESET, iterations, delta); + } + } while (!*convergenceStatus && (parameters.maxIterations == 0 || + iterations < parameters.maxIterations)); + parameters.realIterations = iterations; + if (!parameters.history) { + // Outputs last pagerank vector to file + savePagerankToFile(parameters.outputFilename, false, *pagerankVector, + numberOfPages, parameters.realIterations); + } + + // Frees memory + free(pagerankDifference); + free(previousPagerankVector); + free(convergedPagerankVector); + free(linksFromConvergedPagesPagerankVector); + free(convergenceMatrix); + destroyCooSparseMatrix(&linksFromConvergedPages); + + return iterations; +} + +/* + * initialize allocates required memory for arrays, reads the web graph from the + * from the file and creates the initial transition probability distribution + * matrix. +*/ +void initialize(CsrSparseMatrix *transitionMatrix, + double **pagerankVector, Parameters *parameters) { + + // Reads web graph from file + if ((*parameters).verbose) { + printf(ANSI_COLOR_YELLOW "----- Reading graph from file -----\n" ANSI_COLOR_RESET); + } + generateNormalizedTransitionMatrixFromFile(transitionMatrix, parameters); + + // Outputs the algorithm parameters to the console + if ((*parameters).verbose) { + printf(ANSI_COLOR_YELLOW "\n----- Running with parameters -----\n" ANSI_COLOR_RESET\ + "Number of pages: %d", (*parameters).numberOfPages); + if (!(*parameters).maxIterations) { + printf("\nMaximum number of iterations: inf"); + } else { + printf("\nMaximum number of iterations: %d", (*parameters).maxIterations); + } + printf("\nConvergence criterion: %f" \ + "\nDamping factor: %f" \ + "\nGraph filename: %s\n", (*parameters).convergenceCriterion, + (*parameters).dampingFactor, (*parameters).graphFilename); + } + (*parameters).realIterations = 0; + // Allocates memory for the pagerank vector + (*pagerankVector) = (double *) malloc((*parameters).numberOfPages * sizeof(double)); + double webUniformProbability = 1. / (*parameters).numberOfPages; + for (int i=0; i<(*parameters).numberOfPages; ++i) { + (*pagerankVector)[i] = webUniformProbability; + } +} + +// ==================== MATH UTILS ==================== + +/* + * calculateNextPagerank calculates the product of the multiplication + * between a matrix and the a vector in a cheap way. +*/ +void calculateNextPagerank(CsrSparseMatrix *transitionMatrix, + double *previousPagerankVector, double **pagerankVector, + double *linksFromConvergedPagesPagerankVector, + double *convergedPagerankVector, int vectorSize, double dampingFactor) { + // Calculates the web uniform probability once. + + + double webUniformProbability = 1. / vectorSize; + + csrSparseMatrixVectorMultiplication(*transitionMatrix, previousPagerankVector, + pagerankVector, vectorSize); + #pragma omp parallel for + for (int i=0; i 10) { + validUsage(argumentVector[0]); + } + + (*parameters).numberOfPages = 0; + (*parameters).maxIterations = 0; + (*parameters).convergenceCriterion = 1; + (*parameters).dampingFactor = 0.85; + (*parameters).verbose = false; + (*parameters).history = false; + (*parameters).outputFilename = DEFAULT_OUTPUT_FILENAME; + + char *endPointer; + int argumentIndex = 1; + + while (argumentIndex < argumentCount) { + if (!strcmp(argumentVector[argumentIndex], ARGUMENT_CONVERGENCE_TOLERANCE)) { + argumentIndex = checkIncrement(argumentIndex, argumentCount, argumentVector[0]); + + double convergenceInput = strtod(argumentVector[argumentIndex], &endPointer); + if (convergenceInput == 0) { + printf("Invalid convergence argument\n"); + exit(EXIT_FAILURE); + } + (*parameters).convergenceCriterion = convergenceInput; + } else if (!strcmp(argumentVector[argumentIndex], ARGUMENT_MAX_ITERATIONS)) { + argumentIndex = checkIncrement(argumentIndex, argumentCount, argumentVector[0]); + + size_t iterationsInput = strtol(argumentVector[argumentIndex], &endPointer, NUMERICAL_BASE); + if (iterationsInput == 0 && endPointer) { + printf("Invalid iterations argument\n"); + exit(EXIT_FAILURE); + } + (*parameters).maxIterations = iterationsInput; + } else if (!strcmp(argumentVector[argumentIndex], ARGUMENT_DAMPING_FACTOR)) { + argumentIndex = checkIncrement(argumentIndex, argumentCount, argumentVector[0]); + + double alphaInput = strtod(argumentVector[argumentIndex], &endPointer); + if ((alphaInput == 0 || alphaInput > 1) && endPointer) { + printf("Invalid alpha argument\n"); + exit(EXIT_FAILURE); + } + (*parameters).dampingFactor = alphaInput; + } else if (!strcmp(argumentVector[argumentIndex], ARGUMENT_VERBAL_OUTPUT)) { + (*parameters).verbose = true; + } else if (!strcmp(argumentVector[argumentIndex], ARGUMENT_OUTPUT_HISTORY)) { + (*parameters).history = true; + } else if (!strcmp(argumentVector[argumentIndex], ARGUMENT_OUTPUT_FILENAME)) { + argumentIndex = checkIncrement(argumentIndex, argumentCount, argumentVector[0]); + + if (fopen(argumentVector[argumentIndex], "w") == NULL) { + printf("Invalid output filename. Reverting to default.\n"); + continue; + } + (*parameters).outputFilename = argumentVector[argumentIndex]; + } else if (argumentIndex == argumentCount - 1) { + (*parameters).graphFilename = argumentVector[argumentIndex]; + } else { + validUsage(argumentVector[0]); + exit(EXIT_FAILURE); + } + ++argumentIndex; + } +} + +/* + * readGraphFromFile loads the file supplied in the command line arguments to an + * array (directedWebGraph) that represents the graph. +*/ +void generateNormalizedTransitionMatrixFromFile(CsrSparseMatrix *transitionMatrix, + Parameters *parameters){ + FILE *graphFile; + + // Opens the file for reading + graphFile = fopen((*parameters).graphFilename, "r+"); + if (!graphFile) { + printf("Error opening file \n"); + exit(EXIT_FAILURE); + } + + char buffer[FILE_READ_BUFFER_SIZE]; + char *readResult; + // Skips the first two lines + readResult = fgets(buffer, FILE_READ_BUFFER_SIZE, graphFile); + readResult = fgets(buffer, FILE_READ_BUFFER_SIZE, graphFile); + if (readResult == NULL) { + printf("Error while reading from the file. Does the file have the correct format?\n"); + exit(EXIT_FAILURE); + } + + // Third line contains the numbers of nodes and edges + int numberOfNodes = 0, numberOfEdges = 0; + + readResult = fgets(buffer, FILE_READ_BUFFER_SIZE, graphFile); + if (readResult == NULL) { + printf("Error while reading from the file. Does the file have the correct format?\n"); + exit(EXIT_FAILURE); + } + + // Parses the number of nodes and number of edges + { + // Splits string to whitespace + char *token = strtok(buffer, " "); + bool nextIsNodes = false, nextIsEdges = false; + + while (token != NULL) { + if (strcmp(token, "Nodes:") == 0) { + nextIsNodes = true; + } else if (nextIsNodes) { + numberOfNodes = atoi(token); + nextIsNodes = false; + } else if (strcmp(token, "Edges:") == 0) { + nextIsEdges = true; + } else if (nextIsEdges) { + numberOfEdges = atoi(token); + break; + } + + // Gets next string token + token = strtok (NULL, " ,.-"); + } + } + + if ((*parameters).verbose) { + printf("File claims number of pages is: %d\nThe number of edges is: %d\n", + numberOfNodes, numberOfEdges); + } + + // Skips the fourth line + readResult = fgets(buffer, 512, graphFile); + if (readResult == NULL) { + printf("Error while reading from the file. Does the file have the correct format?\n"); + exit(EXIT_FAILURE); + } + + + int maxPageIndex = 0; + CooSparseMatrix tempMatrix = initCooSparseMatrix(); + allocMemoryForCoo(&tempMatrix, numberOfEdges); + + for (int i=0; i maxPageIndex) { + maxPageIndex = fileFrom; + } + if (fileTo > maxPageIndex) { + maxPageIndex = fileTo; + } + addElement(&tempMatrix, 1, fileFrom, fileTo); + } + + if ((*parameters).verbose) { + printf("Max page index found is: %d\n", maxPageIndex); + } + (*parameters).numberOfPages = maxPageIndex + 1; + + // Calculates the outdegree of each page and assigns the uniform probability + // of transition to the elements of the corresponding row + + int* pageOutdegree = malloc((*parameters).numberOfPages*sizeof(int)); + for (int i=0; i<(*parameters).numberOfPages; ++i){ + pageOutdegree[i] = 0; + } + + + for (int i=0; irowIndex; + + if (currentRow == tempMatrix.elements[i]->rowIndex) { + ++pageOutdegree[currentRow]; + } + + + } + + for (int i=0; ivalue = 1./pageOutdegree[tempMatrix.elements[i]->rowIndex]; + } + + // Transposes the temporary transition matrix (P^T). + transposeSparseMatrix(&tempMatrix); + allocMemoryForCsr(transitionMatrix, numberOfEdges); + // Transforms the temporary COO matrix to the desired CSR format + transformToCSR(tempMatrix, transitionMatrix); + //printCsrSparseMatrix(*transitionMatrix); + destroyCooSparseMatrix(&tempMatrix); + + fclose(graphFile); +} + +/* + * validUsage outputs a message to the console that informs the user of the + * correct (valid) way to use the program. +*/ +void validUsage(char *programName) { + printf("%s [-c convergence_criterion] [-m max_iterations] [-a alpha] [-v] [-h] [-o output_filename] " \ + "\n-c convergence_criterion" \ + "\n\tthe convergence tolerance criterion" \ + "\n-m max_iterations" \ + "\n\tmaximum number of iterations to perform" \ + "\n-a alpha" \ + "\n\tthe damping factor" \ + "\n-v enable verbal output" \ + "\n-h enable history output to file" \ + "\n-o output_filename" \ + "\n\tfilename and path for the output" \ + "\n", programName); + exit(EXIT_FAILURE); +} + +/* + * checkIncrement is a helper function for parseArguments function. +*/ +int checkIncrement(int previousIndex, int maxIndex, char *programName) { + if (previousIndex == maxIndex) { + validUsage(programName); + exit(EXIT_FAILURE); + } + return ++previousIndex; +} + +void savePagerankToFile(char *filename, bool append, double *pagerankVector, + int vectorSize, int realIterations) { + FILE *outputFile; + + if (append) { + outputFile = fopen(filename, "a"); + } else { + outputFile = fopen(filename, "w"); + } + + if (outputFile == NULL) { + printf("Error while opening the output file.\n"); + return; + } + //Save numberofPages and convergence time + + for (int i=0; i +#include +#include +#include +#include + +#include "coo_sparse_matrix.h" + +/* ===== DEFINITIONS ===== */ + +//Colors used for better console output formating. +#define ANSI_COLOR_RED "\x1B[31m" +#define ANSI_COLOR_GREEN "\x1B[32m" +#define ANSI_COLOR_YELLOW "\x1B[33m" +#define ANSI_COLOR_BLUE "\x1B[34m" +#define ANSI_COLOR_CYAN "\x1B[36m" +#define ANSI_COLOR_RESET "\x1B[0m" + +/* ===== CONSTANTS DEFINITION ===== */ + +// Constant strings that store the command line options available. +extern const char *ARGUMENT_CONVERGENCE_TOLERANCE; +extern const char *ARGUMENT_MAX_ITERATIONS; +extern const char *ARGUMENT_DAMPING_FACTOR; +extern const char *ARGUMENT_VERBAL_OUTPUT; +extern const char *ARGUMENT_OUTPUT_HISTORY; +extern const char *ARGUMENT_OUTPUT_FILENAME; +// The numerical base used when parsing numerical command line arguments. +extern const int NUMERICAL_BASE; +// Default filename used for the output. +extern char *DEFAULT_OUTPUT_FILENAME; +// The size of the buffer used for reading the graph input file. +extern const int FILE_READ_BUFFER_SIZE; + +/* ===== STRUCTURES ===== */ + +// A data structure to conveniently hold the algorithm's parameters. +typedef struct parameters { + int numberOfPages, maxIterations, realIterations; + double convergenceCriterion, dampingFactor; + bool verbose, history; + char *outputFilename, *graphFilename; +} Parameters; + +/* ===== FUNCTION DEFINITIONS ===== */ + +// Function validUsage outputs the correct way to use the program with command +// line arguments. +void validUsage(char *programName); + +// Function checkIncrement is a helper function used in parseArguments (see +// bellow). +int checkIncrement(int previousIndex, int maxIndex, char *programName); + +// Function parseArguments parses command line arguments. +void parseArguments(int argumentCount, char **argumentVector, + Parameters *parameters); + +// Function generateNormalizedTransitionMatrixFromFile reads through the entries +// of the file specified in the arguments (parameters->graphFilename), using +// them to populate the sparse array (transitionMatrix). The entries of the file +// represent the edges of the web transition graph. The entries are then +// modified to become the rows of the transition matrix. +void generateNormalizedTransitionMatrixFromFile(CsrSparseMatrix *transitionMatrix, + Parameters *parameters); + +// Function savePagerankToFile appends or overwrites the pagerank vector +// "pagerankVector" to the file with the filename supplied in the arguments. +void savePagerankToFile(char *filename, bool append, double *pagerankVector, + int vectorSize, int realIterations); + +// Function initialize allocates memory for the pagerank vector, reads the +// dataset from the file and creates the transition probability distribution +// matrix. +void initialize(CsrSparseMatrix *transitionMatrix, double **pagerankVector, + Parameters *parameters); + +// Function vectorNorm calculates the first norm of a vector. +double vectorNorm(double *vector, int vectorSize); + +// Function calculateNextPagerank calculates the next pagerank vector. +void calculateNextPagerank(CsrSparseMatrix *transitionMatrix, + double *previousPagerankVector, double **pagerankVector, + double *linksFromConvergedPagesPagerankVector, + double *convergedPagerankVector, int vectorSize, double dampingFactor); + +// Function pagerank iteratively calculates the pagerank of each page until +// either the convergence criterion is met or the maximum number of iterations +// is reached. +int pagerank(CsrSparseMatrix *transitionMatrix, double **pagerankVector, + bool *convergenceStatus, Parameters parameters); + +#endif // SERIAL_GS_PAGERANK_FUNCTIONS_H \ No newline at end of file diff --git a/openmp/serial_gs_pagerank_functions.o b/openmp/serial_gs_pagerank_functions.o new file mode 100644 index 0000000000000000000000000000000000000000..6e91ac740e531c407f846b1524581cbd678197cf GIT binary patch literal 18752 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 literal 0 HcmV?d00001