commit 2398be8e50a063d95ee9e7489e25beb8776d2fa3 Author: Christos Choutouridis Date: Fri Nov 27 15:29:38 2020 +0200 Init commit: a bare implementation before parallelism diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7ef5152 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +# project +resources/ +bin/ + +# eclipse +.project +.cproject +.settings/ + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..634ceb1 --- /dev/null +++ b/Makefile @@ -0,0 +1,167 @@ +# +# PDS excercise 1 Makefile +# +# Copyright (C) 2019-2020 Christos Choutouridis +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +# + +# ============== Project settings ============== +# Project's name +PROJECT := exercise_1 +# Excecutable's name +TARGET := triangCounting +# Source directories list(space seperated). Makefile-relative path, UNDER current directory. +SRC_DIR_LIST := src +# Include directories list(space seperated). Makefile-relative path. +INC_DIR_LIST := src inc +# Exclude files list(space seperated). Filenames only. +# EXC_FILE_LIST := bad.cpp old.cpp + +# Build directories +BUILD_DIR := bin +OBJ_DIR := $(BUILD_DIR)/obj +DEP_DIR := $(BUILD_DIR)/.dep + + +# ========== Compiler settings ========== +# Compiler flags for debug and release +DEB_CFLAGS := -DDEBUG -g3 -Wall -Wextra -std=c++14 +REL_CFLAGS := -Wall -Wextra -O2 -std=c++14 +# Pre-defines +# PRE_DEFS := MYCAB=1729 SUPER_MODE + +# ============== Linker settings ============== +# Linker flags (example: -pthread -lm) +LDFLAGS := -pthread +# Map output file +MAP_FILE := output.map +MAP_FLAG := -Xlinker -Map=$(BUILD_DIR)/$(MAP_FILE) + +# ============== Docker settings ============== +# We need: +# - Bind the entire project directory(the dir that icludes all the code) as volume. +# - In docker instance, change to working directory(where the makefile is). +DOCKER_VOL_DIR := $${PWD} +DOCKER_WRK_DIR := +DOCKER_RUN := docker run --rm +DOCKER_FLAGS := -v $(DOCKER_VOL_DIR):/usr/src/$(PROJECT) -w /usr/src/$(PROJECT)/$(DOCKER_WRK_DIR) + +# docker invoke mechanism (edit with care) +# note: +# Here, `DOCKER` variable is empty. Rules can assign `DOCKER := DOCKER_CMD` when docker +# functionality is needed. +DOCKER_CMD = $(DOCKER_RUN) $(DOCKER_FLAGS) $(IMAGE) +DOCKER := + +# ============== Tool selection ============== +# compiler and compiler flags. +CSIZE := size +CFLAGS := $(DEB_CFLAGS) +CXX := g++ + + +# +# =========== Main body and Patterns =========== +# + +#ifeq ($(OS), Windows_NT) +# TARGET := $(TARGET).exe +#endif +INC := $(foreach dir,$(INC_DIR_LIST),-I$(dir)) +DEF := $(foreach def,$(PRE_DEFS),-D$(def)) +EXC := $(foreach fil,$(EXC_FILE_LIST), \ + $(foreach dir,$(SRC_DIR_LIST),$(wildcard $(dir)/$(fil))) \ + ) +# source files. object and dependencies list +# recursive search into current and source directories +SRC := $(wildcard *.cpp) +SRC += $(foreach dir,$(SRC_DIR_LIST),$(wildcard $(dir)/*.cpp)) +SRC += $(foreach dir,$(SRC_DIR_LIST),$(wildcard $(dir)/**/*.cpp)) +SRC := $(filter-out $(EXC),$(SRC)) +#SRC := $(abspath $(SRC)) + +OBJ := $(foreach file,$(SRC:%.cpp=%.o),$(OBJ_DIR)/$(file)) +DEP := $(foreach file,$(SRC:%.cpp=%.d),$(DEP_DIR)/$(file)) + + +# Make Dependencies pattern. +# This little trick enables recompilation only when dependencies change +# and it does so for changes both in source AND header files ;) +# +# It is based on Tom Tromey's method. +# +# Invoke cpp to create makefile rules with dependencies for each source file +$(DEP_DIR)/%.d: %.cpp + @mkdir -p $(@D) + @$(DOCKER) $(CXX) -E $(CFLAGS) $(INC) $(DEF) -MM -MT $(OBJ_DIR)/$(<:.cpp=.o) -MF $@ $< + +# objects depent on .cpp AND dependency files, which have an empty recipe +$(OBJ_DIR)/%.o: %.cpp $(DEP_DIR)/%.d + @mkdir -p $(@D) + $(DOCKER) $(CXX) -c $(CFLAGS) $(INC) $(DEF) -o $@ $< + +# empty recipe for dependency files. This prevents make errors +$(DEP): + +# now include all dependencies +# After all they are makefile dependency rules ;) +include $(wildcard $(DEP)) + +# main target rule +$(BUILD_DIR)/$(TARGET): $(OBJ) + @mkdir -p $(@D) + @echo Linking to target: $(TARGET) + @echo $(DOCKER) $(CXX) $(LDFLAGS) $(MAP_FLAG) -o $(@D)/$(TARGET) '$$(OBJ)' + @$(DOCKER) $(CXX) $(LDFLAGS) $(MAP_FLAG) -o $(@D)/$(TARGET) $(OBJ) + @echo + @echo Print size information + @$(CSIZE) $(@D)/$(TARGET) + @echo Done + +.PHONY: clean +clean: + @echo Cleaning build directories + @rm -rf $(OBJ_DIR) + @rm -rf $(DEP_DIR) + @rm -rf $(BUILD_DIR) + + +# +# ================ Local build rules ================= +# examples: +# make debug + +.PHONY: debug +debug: CFLAGS := $(DEB_CFLAGS) +debug: $(BUILD_DIR)/$(TARGET) + +.PHONY: release +release: CFLAGS := $(REL_CFLAGS) +release: $(BUILD_DIR)/$(TARGET) + +.PHONY: all +all: release + +# +# ================ Docker based rules ================ +# examples: +# make IMAGE="gcc:8.3" dock +# +.PHONY: dock +dock: DOCKER := $(DOCKER_CMD) +dock: CFLAGS := $(REL_CFLAGS) +dock: $(BUILD_DIR)/$(TARGET) + + diff --git a/inc/config.h b/inc/config.h new file mode 100644 index 0000000..bbca09d --- /dev/null +++ b/inc/config.h @@ -0,0 +1,36 @@ +/*! + * \file config,h + * \brief Build configuration file. + * + * \author + * Christos Choutouridis AEM:8997 + * + */ + +#ifndef CONFIG_H_ +#define CONFIG_H_ + +#include +#include +#include + +#define V12 2 +#define V3 3 +#define V4 4 + +#if !defined CODE_VERSION + #define CODE_VERSION V4 +#endif + +#if CODE_VERSION == V12 + using namespace v12; + using matrix = v12::matrix; +#elif CODE_VERSION == V3 + using namespace v3; + using matrix = v3::matrix; +#elif CODE_VERSION == V4 + using namespace v4; + using matrix = v4::matrix; +#endif + +#endif /* CONFIG_H_ */ diff --git a/inc/impl.hpp b/inc/impl.hpp new file mode 100644 index 0000000..a1aaebf --- /dev/null +++ b/inc/impl.hpp @@ -0,0 +1,519 @@ +/** + * \file impl.hpp + * \brief Implementation common header file + * + * \author + * Christos Choutouridis AEM:8997 + * + */ +#ifndef IMPL_HPP_ +#define IMPL_HPP_ + +#include +#include +#include +#include +#include +#include +#include +#include + +using std::size_t; + +/* + * Small helper to strip types + */ +template +struct remove_cvref { + typedef std::remove_cv_t> type; +}; +template +using remove_cvref_t = typename remove_cvref::type; + +/*! + * Enumerator to denote the storage type of the array to use. + */ +enum class MatrixType { + FULL, /*!< Matrix is asymmetric */ + SYMMETRIC, /*!< Matrix is symmetric */ +}; + +/* + * Forward type declerations + */ +template struct Matrix; +template struct SpMat; +template struct SpMatCol; +template struct SpMatRow; +template struct SpMatVal; + + /*! + * 2D-array wrapper for v1 and v2 part of the exercise t use as RAII + * and copy-prevention. + * + * This is a very thin abstraction layer over a native array. + * This is tested using compiler explorer and our template produce + * almost identical assembly. + * + * The penalty hit we have is due to the fact that we use a one dimension array + * and we have to calculate the actual position from an (i,j) pair. + * The use of 1D array was our intention from the beginning, so the penalty + * was pretty much unavoidable. + * + * \tparam DataType The underling data type of the array + * \tparam Type The storage type of the array + * \arg FULL For full matrix + * \arg SYMMETRIC For symmetric matrix (we use only the lower part) + */ +template +struct Matrix { + + using dataType = DataType; //!< meta:export of underling data type + using indexType = IndexType; //!< meta:export of underling index type + static constexpr MatrixType matrixType = Type; //!< export of array type + + /*! + * \name Obj lifetime + */ + //! @{ + + //! Constructor using data type and size + Matrix(DataType* t, IndexType s) noexcept : m_(t), size_(s) { } + + //! RAII deleter + ~Matrix() { delete m_; } + //! move ctor + Matrix(Matrix&& a) noexcept { a.swap(*this); } + //! move + Matrix& operator=(Matrix&& a) noexcept { a.swap(*this); return *this; } + Matrix(const Matrix& a) = delete; //!< No copy ctor + Matrix& operator=(const Matrix& a) = delete; //!< No copy + //! @} + + //! \name Data exposure + //! @{ + + //! memory capacity of the matrix with diagonal data + template std::enable_if_t + static constexpr capacity(IndexType N) noexcept { return (N+1)*N/2; } + //! memory capacity for full matrix + template std::enable_if_t + static constexpr capacity(IndexType N) noexcept { return N*N; } + + //! Get the size of each dimension + IndexType size() noexcept { return size_; } + + /* + * virtual 2D accessors + */ + template + std::enable_if_t + get (IndexType i, IndexType j) { + if (i + std::enable_if_t + get(IndexType i, IndexType j) { + return m_[i*size_ + j]; + } + template + std::enable_if_t + set(DataType v, IndexType i, IndexType j) { + if (i + std::enable_if_t + set(DataType v, IndexType i, IndexType j) { + return m_[i*size_ + j] =v; + } + DataType operator()(IndexType i, IndexType j) { return get(i, j); } + + // a basic serial iterator support + DataType* begin() noexcept { return m_; } + DataType* end() noexcept { return m_ + Matrix::capacity(size_); } + //! @} + + /*! + * \name Safe iteration API + * + * This api automates the iteration over the array based on + * MatrixType + */ + //! @{ + template + void for_each_in (IndexType begin, IndexType end, F&& lambda, Args&&... args) { + for (IndexType it=begin ; it(lambda)(std::forward(args)..., it); + } + } + //! @} + + // move helper + void swap(Matrix& src) noexcept { + std::swap(m_, src.m_); + std::swap(size_, src.size_); + } +private: + DataType* m_; //!< Pointer to actual data. + IndexType size_; //!< the virtual size of each dimension. +}; + + /*! + * RAII allocation helper, the smart_ptr way. + */ +template +Matrix make_Matrix(IndexType s) { + return Matrix(new DataType[Matrix::capacity(s)], s); +} + +template +using CooVal = std::tuple; + +template +struct SpMat { + using dataType = DataType; //!< meta:export of underling data type + using indexType = IndexType; //!< meta:export of underling index type + static constexpr MatrixType matrixType = Type; //!< export of array type + + friend class SpMatCol; + friend class SpMatRow; + friend class SpMatVal; + + /*! + * \name Obj lifetime + */ + //! @{ + + //! allocation with init value ctor + SpMat(IndexType n=IndexType{}, IndexType nnz=IndexType{}) : + values(nnz, DataType{}), + rows(nnz, IndexType{}), + col_ptr((n)? n+1:2, IndexType{}), + N(n), + NNZ(nnz) { } + + SpMat(IndexType n, IndexType nnz, const IndexType* row, const IndexType* col) : + values(nnz, 1), + rows(row, row+nnz), + col_ptr(col, col+n+1), + N(n), + NNZ(nnz) { } + + SpMat(IndexType n, IndexType nnz, const DataType* v, const IndexType* row, const IndexType* col) : + values(v, v+nnz), + rows(row, row+nnz), + col_ptr(col, col+n+1), + N(n), + NNZ(nnz) { } + + SpMat(IndexType n, IndexType nnz, const DataType v, const std::vector& row, const std::vector& col) : + values(nnz, v), + rows (row), + col_ptr(col), + N(n), + NNZ(nnz) { } + + //! move ctor + SpMat(SpMat&& a) noexcept { moves(std::move(a)); } + //! move + SpMat& operator=(SpMat&& a) noexcept { moves(std::move(a)); return *this; } + SpMat(const SpMat& a) = delete; //!< No copy ctor + SpMat& operator=(const SpMat& a) = delete; //!< No copy assignment + //! @} + + //! \name Data exposure + //! @{ + IndexType size() noexcept { return N; } + IndexType size(IndexType n) { + col_ptr.resize(n+1); + return N = n; + } + IndexType capacity() noexcept { return NNZ; } + IndexType capacity(IndexType nnz) { + values.reserve(nnz); + rows.reserve(nnz); + return NNZ; + } + std::vector& getRows() noexcept { return rows; } + std::vector& getCols() noexcept { return col_ptr; } + + SpMatVal operator()(IndexType i, IndexType j) { + return SpMatVal(this, get(i, j), i, j); + } + DataType get(IndexType i, IndexType j) { + IndexType idx; + bool found; + std::tie(idx, found) =find_idx(rows, col_ptr[j], col_ptr[j+1], i); + return (found) ? values[idx] : 0; + } + DataType set(DataType v, IndexType i, IndexType j) { + IndexType idx; bool found; + std::tie(idx, found) = find_idx(rows, col_ptr[j], col_ptr[j+1], i); + if (found) + return values[idx] = v; // we don't change NNZ even if we write "0" + else { + values.insert(values.begin()+idx, v); + rows.insert(rows.begin()+idx, i); + std::transform(col_ptr.begin()+j+1, col_ptr.end(), col_ptr.begin()+j+1, [](IndexType it) { + return ++it; + }); + ++NNZ; // we increase the NNZ even if we write "0" + return v; + } + } + + + SpMatCol getCol(IndexType j) { + return SpMatCol(this, col_ptr[j], col_ptr[j+1]); + } + template + std::enable_if_t> + getRow(IndexType i) { + return getCol(i); + } + template + std::enable_if_t> + getRow(IndexType i) { + return SpMatRow(this, i); + } + + // iterator support + DataType* begin() noexcept { return values.begin(); } + DataType* end() noexcept { return values.end(); } + //! @} + + /*! + * \name Safe iteration API + * + * This api automates the iteration over the array based on + * MatrixType + */ + //! @{ + + template + void for_each_in (IndexType begin, IndexType end, F&& lambda, Args&&... args) { + for (IndexType it=begin ; it(lambda)(std::forward(args)..., it); + } + } + + //! @} + + // operations + template friend void print(SpMat& mat); + template friend void print_dense(SpMat& mat); + +private: + // index-find helper + std::pair find_idx(const std::vector& v, IndexType begin, IndexType end, IndexType match) { + for ( ; begin < end ; ++begin) { + if (match == v[begin]) return std::make_pair(begin, true); + else if (match < v[begin]) return std::make_pair(begin, false); + } + return std::make_pair(end, false); + } + // move helper + void moves(SpMat&& src) noexcept { + values = std::move(src.values); + rows = std::move(src.rows); + col_ptr = std::move(src.col_ptr); + N = std::move(src.N); // redundant for primitives + NNZ = std::move(src.NNZ); // + } + //! \name Data + //! @{ + std::vector values {}; + std::vector rows{}; + std::vector col_ptr{1,0}; + IndexType N{0}; + IndexType NNZ{0}; + //! @} +}; + +template +struct SpMatCol { + using owner_t = SpMat; + + SpMatCol(owner_t* own, const IndexType begin, const IndexType end) noexcept : + owner_(own), index_(begin), begin_(begin), end_(end) { + vindex_ = vIndexCalc(index_); + } + SpMatCol() = default; + SpMatCol(const SpMatCol&) = delete; + SpMatCol& operator=(const SpMatCol&)= delete; + SpMatCol(SpMatCol&&) = default; + SpMatCol& operator=(SpMatCol&&) = default; + + DataType operator* () { + return get(); + } + SpMatCol& operator++ () { advance(); return *this; } + SpMatCol& operator++ (int) { SpMatCol& p = *this; advance(); return p; } + + DataType operator()(IndexType x) { + return (x == index())? get() : DataType{}; + } + DataType operator= (DataType v) { return owner_->values[index_] = v; } + IndexType index() noexcept { return vindex_; } + const IndexType index() const noexcept { return vindex_; } + IndexType begin() noexcept { return vIndexCalc(begin_); } + const IndexType begin() const noexcept { return vIndexCalc(begin_); } + IndexType end() noexcept { return owner_->N; } + const IndexType end() const noexcept { return owner_->N; } + + template + DataType operator* (C&& c) { + static_assert(std::is_same, SpMatCol>(), ""); + DataType v{}; + while (index() != end() && c.index() != c.end()) { + if (index() < c.index()) advance(); + else if (index() > c.index()) ++c; + else { //index() == c.index() + v += get() * *c; + ++c; + advance(); + } + } + return v; + } + +private: + void advance() noexcept { + ++index_; + vindex_ = vIndexCalc(index_); + } + IndexType vIndexCalc(IndexType idx) { + return (idx < end_) ? owner_->rows[idx] : end(); + } + DataType get() { return owner_->values[index_]; } + owner_t* owner_{nullptr}; + IndexType vindex_ {IndexType{}}; + IndexType index_{IndexType{}}; + IndexType begin_{IndexType{}}; + IndexType end_{IndexType{}}; +}; + +template +struct SpMatRow { + using owner_t = SpMat; + + SpMatRow(owner_t* own, const IndexType row) noexcept : + owner_(own), vindex_(IndexType{}), row_(row), index_(IndexType{}), + begin_(IndexType{}), end_(owner_->NNZ) { + // place begin + while(begin_ != end_ && owner_->rows[begin_] != row_) + ++begin_; + // place index_ and vindex_ + if (owner_->rows[index_] != row_) + advance(); + } + SpMatRow() = default; + SpMatRow(const SpMatRow&) = delete; + SpMatRow& operator=(const SpMatRow&)= delete; + SpMatRow(SpMatRow&&) = default; + SpMatRow& operator=(SpMatRow&&) = default; + + DataType operator* () { + return get(); + } + SpMatRow& operator++ () { advance(); return *this; } + SpMatRow& operator++ (int) { SpMatRow& p = *this; advance(); return p; } + DataType operator()(IndexType x) { + return (x == index())? get() : DataType{}; + } + DataType operator= (DataType v) { return owner_->values[index_] = v; } + IndexType index() noexcept { return vindex_; } + const IndexType index() const noexcept { return vindex_; } + IndexType begin() noexcept { return vIndexCalc(begin_); } + const IndexType begin() const noexcept { return vIndexCalc(begin_); } + IndexType end() noexcept { return owner_->N; } + const IndexType end() const noexcept { return owner_->N; } + + template + DataType operator* (C&& c) { + static_assert(std::is_same, SpMatCol>(), ""); + DataType v{}; + while (index() != end() && c.index() != c.end()) { + if (index() < c.index()) advance(); + else if (index() > c.index()) ++c; + else { //index() == c.index() + v += get()* *c; + ++c; + advance(); + } + } + return v; + } +private: + void advance() noexcept { + do + ++index_; + while(index_ != end_ && owner_->rows[index_] != row_); + vindex_ = vIndexCalc(index_); + } + IndexType vIndexCalc(IndexType idx) { + for(IndexType i =0 ; i<(owner_->N+1) ; ++i) + if (idx < owner_->col_ptr[i]) + return i-1; + return end(); + } + DataType get() { return owner_->values[index_]; } + owner_t* owner_ {nullptr}; + IndexType vindex_ {IndexType{}}; + IndexType row_ {IndexType{}}; + IndexType index_ {IndexType{}}; + IndexType begin_ {IndexType{}}; + IndexType end_ {IndexType{}}; +}; + +template +struct SpMatVal { + using owner_t = SpMat; + + SpMatVal(owner_t* own, DataType v, IndexType i, IndexType j) : + owner_(own), v_(v), i_(i), j_(j) { } + SpMatVal() = default; + SpMatVal(const SpMatVal&) = delete; + SpMatVal& operator=(const SpMatVal&) = delete; + SpMatVal(SpMatVal&&) = default; + SpMatVal& operator=(SpMatVal&&) = default; + + operator DataType() { return v_; } + SpMatVal& operator=(DataType v) { + v_ = v; + owner_->set(v_, i_, j_); + return *this; + } +private: + owner_t* owner_{nullptr};; + DataType v_{DataType{}}; + IndexType i_{IndexType{}}; + IndexType j_{IndexType{}}; +}; + + +enum class InputMatrix{ + GENERATE, + MTX +}; + +/*! + * Session option for each invocation of the executable + */ +struct session_t { + std::size_t size {0}; + double probability {0}; + InputMatrix inputMatrix {InputMatrix::GENERATE}; + std::ifstream mtxFile {}; + std::size_t print_size {80}; + bool timing {false}; + bool print {false}; + bool makeSymmetric {false}; +}; + +extern session_t session; + + +#endif /* IMPL_HPP_ */ diff --git a/inc/utils.h b/inc/utils.h new file mode 100644 index 0000000..77d3d66 --- /dev/null +++ b/inc/utils.h @@ -0,0 +1,198 @@ +/*! + * \file utils.h + * \brief Utilities to handle matrix files, chrono, etc... + * + * \author + * Christos Choutouridis AEM:8997 + * + */ +#ifndef UTILS_H_ +#define UTILS_H_ + +#include +#include +#include +#include +#include +#include +#include + +template +struct buffer_t { + buffer_t(size_t s) { p = new T[s]; } + ~buffer_t() { delete[] p; } + buffer_t() = default; + buffer_t(buffer_t&&) = default; + buffer_t& operator=(buffer_t&&) = default; + buffer_t(const buffer_t&) = delete; + buffer_t& operator=(const buffer_t&) = delete; + + T* allocate(size_t s) { return p = new T[s]; } + T* operator() () { return p; } + T& operator[] (size_t i){ return p[i]; } +private: + T* p{nullptr}; +}; + +struct Mtx { + + template + static void coo2csc(I *row, I *col, I const* row_coo, I const* col_coo, I nnz, I n, I isOneBased) { + // ----- cannot assume that input is already 0! + for (I l = 0; l < n+1; l++) col[l] = 0; + + // ----- find the correct column sizes + for (I l = 0; l < nnz; l++) + col[col_coo[l] - isOneBased]++; + + // ----- cumulative sum + for (I i = 0, cumsum = 0; i < n; i++) { + I temp = col[i]; + col[i] = cumsum; + cumsum += temp; + } + col[n] = nnz; + // ----- copy the row indices to the correct place + for (I l = 0; l < nnz; l++) { + I col_l; + col_l = col_coo[l] - isOneBased; + + I dst = col[col_l]; + row[dst] = row_coo[l] - isOneBased; + + col[col_l]++; + } + // ----- revert the column pointers + for (I i = 0, last = 0; i < n; i++) { + I temp = col[i]; + col[i] = last; + last = temp; + } + } + + template + static bool is_triangular (std::ifstream& file) { + std::string line, token; + enum state_en {HEADER, SIZE, DATA} state = HEADER; + enum LU_t {Z, LOWER, UPPER} LU = Z; + + while (std::getline (file, line, '\n')) { + std::stringstream ss(line); + switch (state) { + case HEADER: + ss >> token; + if (token != "%%MatrixMarket") return false; + else state = SIZE; + break; + case SIZE: + if (line[0] == '%') continue; + else state = DATA; + break; + case DATA: + if (line[0] == '%') continue; + I i, j; + ss >> i >> j; + switch (LU) { + case Z: LU = (i + static bool load (SpMat& M, std::ifstream& file) { + std::string line, token; + enum state_en {HEADER, SIZE, DATA} state = HEADER; + enum LU_t {Z, LOWER, UPPER} LU = Z; + IndexT n1, n2, nnz; + buffer_t col{}, row{}, coo_col{}, coo_row{}; + + IndexT cnt{}; + while (std::getline (file, line, '\n')) { + std::stringstream ss(line); + switch (state) { + case HEADER: + ss >> token; + if (token != "%%MatrixMarket") return false; + else state = SIZE; + break; + case SIZE: + if (line[0] == '%') continue; + else { + ss >> n1 >> n2 >> nnz; + if (session.makeSymmetric) + nnz *= 2; + col.allocate(nnz); + row.allocate(nnz); + coo_col.allocate(nnz); + coo_row.allocate(nnz); + state = DATA; + } + break; + case DATA: + if (line[0] == '%') continue; + IndexT i, j; + ss >> i >> j; + if (session.makeSymmetric) { + if (LU == Z) { + LU = (i(n1, cnt, &row[0], &col[0]); + return true; + } +}; + +struct Timing{ + using Tpoint = std::chrono::steady_clock::time_point; + using microseconds = std::chrono::microseconds; + using milliseconds = std::chrono::milliseconds; + using seconds = std::chrono::seconds; + + + Tpoint start () noexcept { return start_ = std::chrono::steady_clock::now(); } + Tpoint stop () noexcept { return stop_ = std::chrono::steady_clock::now(); } + + auto dt () noexcept { + return std::chrono::duration_cast(stop_ - start_).count(); + } + void print_dt () noexcept { + auto t = stop_ - start_; + if (std::chrono::duration_cast(t).count() < 10000) + std::cout << "time: " << std::to_string(std::chrono::duration_cast(t).count()) << " [usec]\n"; + else if (std::chrono::duration_cast(t).count() < 10000) + std::cout << "time: " << std::to_string(std::chrono::duration_cast(t).count()) << " [msec]\n"; + else + std::cout << "time: " << std::to_string(std::chrono::duration_cast(t).count()) << " [sec]\n"; + + } +private: + Tpoint start_; + Tpoint stop_; +}; + + +void init_ER_graph (matrix& A, double p); +void print_ER_graph (matrix& A); + +#endif /* UTILS_H_ */ diff --git a/inc/v12.h b/inc/v12.h new file mode 100644 index 0000000..3a30bd7 --- /dev/null +++ b/inc/v12.h @@ -0,0 +1,21 @@ +/*! + * \file v12.h + * \brief v1 and v2 part of the exercise header file. + * + * \author + * Christos Choutouridis AEM:8997 + * + */ +#ifndef V12_H_ +#define V12_H_ + +#include + +namespace v12 { + +using matrix = Matrix; + +int triang_count (matrix& A) ; + +}; +#endif /* V12_H_ */ diff --git a/inc/v3.h b/inc/v3.h new file mode 100644 index 0000000..f3b3f1a --- /dev/null +++ b/inc/v3.h @@ -0,0 +1,21 @@ +/*! + * \file v3.h + * \brief v3 part of the exercise header file. + * + * \author + * Christos Choutouridis AEM:8997 + * + */ +#ifndef V3_H_ +#define V3_H_ + +#include + +namespace v3 { + +using matrix = SpMat; + +int triang_count (matrix& A); + +}; +#endif /* V3_H_ */ diff --git a/inc/v4.h b/inc/v4.h new file mode 100644 index 0000000..2de0ffd --- /dev/null +++ b/inc/v4.h @@ -0,0 +1,21 @@ +/*! + * \file v4.h + * \brief v4 part of the exercise header file. + * + * \author + * Christos Choutouridis AEM:8997 + * + */ +#ifndef V4_H_ +#define V4_H_ + +#include + +namespace v4 { + +using matrix = SpMat; + +int triang_count (matrix& A); + +}; +#endif /* V4_H_ */ diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..ef80fc4 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,112 @@ +/*! + * \file main.cpp + * \brief Main application file + * + * \author + * Christos Choutouridis AEM:8997 + * + */ + +#include +#include +#include + +#include +#include + +// Global session data +session_t session; + +/*! + * A small command line argument parser + * \return The status of the operation + */ +bool get_options(int argc, char* argv[]){ + bool status =true; + + // iterate over the passed arguments + for (int i=1 ; i (session.mtxFile)) + throw std::runtime_error("Error: Matrix is not strictly upper or lower"); + if (!Mtx::load (A, session.mtxFile)) { + throw std::runtime_error("Error: fail to load matrix"); + } + timer.stop(); + std::cout << "Matrix size: " << A.size() << " and capacity: " << A.capacity() <<'\n'; + if (session.timing) timer.print_dt(); + } + + if (session.print) { + std::cout << "Array A:\n"; + print_ER_graph (A); + } + + std::cout << "count triangles\n"; + timer.start(); + std::cout << "There are " << triang_count(A) << " triangles\n"; + timer.stop(); + if (session.timing) timer.print_dt(); + + return 0; +} +catch (std::exception& e) { + //we probably pollute the user's screen. Comment `cerr << ...` if you don't like it. + std::cerr << e.what() << '\n'; + exit(1); +} diff --git a/src/utils.cpp b/src/utils.cpp new file mode 100644 index 0000000..bd57552 --- /dev/null +++ b/src/utils.cpp @@ -0,0 +1,53 @@ +/*! + * \file utils.cpp + * \brief Utilities to handle matrix files, chrono, etc... + * + * \author + * Christos Choutouridis AEM:8997 + * + */ + +#include +#include + +/*! + * Initialize the matrix as Erdős-Rényi graph + * \param A The matrix to initialize + * \param p The probability of each edge + */ +void init_ER_graph (matrix& A, double p) { + std::random_device rd; + std::mt19937 gen(rd()); + std::binomial_distribution<> d(1, p); + +#if CODE_VERSION == V12 + std::transform (A.begin(), A.end(), A.begin(), + [&] (int x) { std::ignore =x; return d(gen); } +#else + A.for_each_in(0, A.size(), [&](auto i) { + A.for_each_in(i+1, A.size(), [&](auto j){ + matrix::dataType edge = d(gen); + if (edge) { + A.set(edge, i, j); + A.set(edge, j, i); + } + }); + }); +#endif + +} + +/*! + * Utility to print the graph to sdtout + */ +void print_ER_graph (matrix& A) { + matrix::indexType N = (A.size() < (matrix::indexType)session.print_size) ? A.size() : session.print_size; + + A.for_each_in(0, N, [&](auto i){ + A.for_each_in(0, N, [&](auto j) { + std::cout << A(i, j) << ' '; + }); + std::cout << '\n'; + }); +} + diff --git a/src/v12.cpp b/src/v12.cpp new file mode 100644 index 0000000..4ee17ae --- /dev/null +++ b/src/v12.cpp @@ -0,0 +1,35 @@ +/*! + * \file v12.cpp + * \brief v1 and v2 part of the exercise. + * + * \author + * Christos Choutouridis AEM:8997 + * + */ + +#include +#include +#include + +namespace v12 { + +/*! + * A naive triangle counting algorithm + * \param A The adjacency matrix + * \return The number of triangles + */ +int triang_count (matrix& A) { + int count =0; + + // We use a symmetric matrix so we iterate using the constrain i + */ + +#include +#include +#include + +namespace v3 { + +using index_t = typename matrix::indexType; +using value_t = typename matrix::dataType; + +/*! + * A naive triangle counting algorithm + * \param A The adjacency matrix + * \return The number of triangles + */ +std::vector triang_v(matrix& A) { + std::vector c(A.size()); + + for (int i=0 ; i& v) { + value_t s =0; + for (auto& it : v) + s += it; + return s; +} + +value_t triang_count (matrix& A) { + auto v = triang_v(A); + return sum(v); +} + +} diff --git a/src/v4.cpp b/src/v4.cpp new file mode 100644 index 0000000..abba21c --- /dev/null +++ b/src/v4.cpp @@ -0,0 +1,43 @@ +/*! + * \file v4.cpp + * \brief vv3 part of the exercise. + * + * \author + * Christos Choutouridis AEM:8997 + * + */ + +#include +#include +#include + +namespace v4 { + +using index_t = typename matrix::indexType; +using value_t = typename matrix::dataType; + + +std::vector mmacc_v(matrix& A, matrix& B) { + std::vector c(A.size()); + for (int i=0 ; i& v) { + value_t s =0; + for (auto& it : v) + s += it; + return s; +} + +value_t triang_count (matrix& A) { + auto v = mmacc_v(A, A); + return (session.makeSymmetric) ? sum(v)/6 : sum(v); +} + +}